import { gzip , ungzip } from "pako" ; import { createClient } from "webdav/web" ; import * as base64 from "base64-js" ; import { getSharedAppEnv } from "buttercup" ; import { HMAC_KEY_SIZE , PASSWORD_KEY_SIZE , DerivedKeyInfo , EncryptedComponents , EncryptedBinaryComponents , EncryptionAlgorithm , IocaneAdapter , createAdapter } from "iocane/web" ; import uuid from "react-native-uuid" ; import hexToArrayBuffer from "hex-to-array-buffer" ; import arrayBufferToHex from "array-buffer-to-hex" ; import { CryptoBridge } from "./cryptoBridge" ; import { CRYPTO_DERIVATION_ROUNDS } from "../symbols" ; interface TextDecoderClass { new ( ) ; decode : ( data : Uint8Array ) => string ; } interface TextEncoderClass { new ( ) ; encode : ( text : string ) => string ; } declare var TextDecoder ; declare var TextEncoder ; const NOOP = ( ) => { } ; let __hasInitialised = false , __derivationRoundsOverride = CRYPTO_DERIVATION_ROUNDS ; function compressV1 ( text ) { return gzip ( text , { level : 9 , to : "string" } ) ; } async function compressV2 ( text ) { const output = gzip ( text , { level : 9 } ) ; return encodeBytesToBase64 ( output ) ; } function decodeBase64 ( b64 , raw = false ) { return raw ? base64 . toByteArray ( b64 ) : new TextDecoder ( ) . decode ( base64 . toByteArray ( b64 ) ) ; } function decompressV1 ( text ) { return ungzip ( text , { to : "string" } ) ; } async function decompressV2 ( text ) { const bytes = decodeBase64 ( text , true ) ; return ungzip ( bytes , { to : "string" } ) ; } export async function decryptCBC ( encryptedComponents , keyDerivationInfo ) { if ( typeof encryptedComponents . content === "string" ) { const result = await CryptoBridge . decryptText ( encryptedComponents . content , arrayBufferToHex ( keyDerivationInfo . key ) , encryptedComponents . iv , encryptedComponents . salt , arrayBufferToHex ( keyDerivationInfo . hmac ) , encryptedComponents . auth ) ; return decodeBase64 ( result ) ; } throw new Error ( "Binary mode not supported yet" ) ; } function decryptData ( data , password ) { const adapter = getAdapter ( ) ; return adapter . decrypt ( data , password ) ; } export async function deriveKey ( password , salt ) { const bits = ( PASSWORD_KEY_SIZE + HMAC_KEY_SIZE ) * 8 ; const derivedKeyHex = await CryptoBridge . deriveKeyFromPassword ( password , salt , this . derivationRounds , bits ) ; const dkhLength = derivedKeyHex . length ; const keyBuffer = hexToArrayBuffer ( derivedKeyHex . substr ( 0 , dkhLength / 2 ) ) ; const hmacBuffer = hexToArrayBuffer ( derivedKeyHex . substr ( dkhLength / 2 , dkhLength / 2 ) ) ; return { salt , key : keyBuffer , rounds : this . derivationRounds , hmac : hmacBuffer } ; } function encodeBytesToBase64 ( bytes ) { return base64 . fromByteArray ( bytes ) ; } function encodeTextToBase64 ( text ) { return encodeBytesToBase64 ( new TextEncoder ( ) . encode ( text ) ) ; } async function encryptCBC ( content , keyDerivationInfo , iv ) { if ( typeof content === "string" ) { const ivHex = arrayBufferToHex ( iv ) ; const preparedContent = encodeTextToBase64 ( content ) ; const result = await CryptoBridge . encryptText ( preparedContent , arrayBufferToHex ( keyDerivationInfo . key ) , keyDerivationInfo . salt , ivHex , arrayBufferToHex ( keyDerivationInfo . hmac ) ) ; const [ encryptedContent , hmacHex , , saltHex ] = result . split ( "|" ) ; const output = { content : encryptedContent , auth : hmacHex , iv : ivHex , salt : saltHex , rounds : keyDerivationInfo . rounds , method : EncryptionAlgorithm . CBC } ; return output ; } throw new Error ( "Binary mode not supported yet" ) ; } function encryptData ( data , password ) { const adapter = getAdapter ( ) ; return adapter . encrypt ( data , password ) ; } async function generateIV ( ) { const ivStr = await CryptoBridge . generateIV ( ) ; return hexToArrayBuffer ( ivStr ) ; } async function generateSalt ( length ) { return CryptoBridge . generateSaltWithLength ( length ) ; } function generateUUID ( ) { return uuid . v4 ( ) as string ; } export function getAdapter ( ) { const adapter = createAdapter ( ) ; patchCrypto ( adapter ) ; if ( __derivationRoundsOverride !== null ) { adapter . setDerivationRounds ( __derivationRoundsOverride ) ; } return adapter ; } export function initAppEnv ( ) { if ( __hasInitialised ) return ; __hasInitialised = true ; const appEnv = getSharedAppEnv ( ) ; appEnv . setProperties ( { "compression/v1/compressText" : compressV1 , "compression/v1/decompressText" : decompressV1 , "compression/v2/compressText" : compressV2 , "compression/v2/decompressText" : decompressV2 , "crypto/v2/decryptBuffer" : decryptData , "crypto/v2/encryptBuffer" : encryptData , "crypto/v1/decryptText" : decryptData , "crypto/v1/encryptText" : encryptData , "crypto/v1/setDerivationRounds" : NOOP , "encoding/v1/base64ToBytes" : ( input ) => decodeBase64 ( input , true ) , "encoding/v1/base64ToText" : decodeBase64 , "encoding/v1/bytesToBase64" : encodeBytesToBase64 , "encoding/v1/textToBase64" : encodeTextToBase64 , "env/v1/isClosedEnv" : ( ) => true , "net/webdav/v1/newClient" : createClient , "rng/v1/uuid" : generateUUID } ) ; } function patchCrypto ( adapter ) { Object . assign ( adapter , { decryptCBC , deriveKey : deriveKey . bind ( adapter ) , encryptCBC , generateIV , generateSalt } ) ; }