deprecated import { encodeText , encodeArrayBuffer , decodeArrayBuffer , decodeText , concatArrayBuffer } from '@dimensiondev/kit' ; import { memoizePromise } from '../../utils-pure' ; import { i18n } from '../../shared-ui/locales_legacy' ; import { CryptoWorker } from '../modules/workers' ; import { EC_Private_JsonWebKey , EC_Public_JsonWebKey , AESJsonWebKey , makeTypedMessageText } from '@masknet/shared-base' ; import { derive_AES_GCM_256_Key_From_PBKDF2 , derive_AES_GCM_256_Key_From_ECDH_256k1_Keys , } from '../modules/CryptoAlgorithm/helper' ; export type PublishedAESKey = { encryptedKey : string ; salt : string ; } ; export type PublishedAESKeyRecordV40 = { key : PublishedAESKey ; name : string ; } ; param privateKey param othersPublicKey param salt async function deriveAESKey ( privateKey , othersPublicKey , /** If salt is not provided, we will generate one. And you should send it to your friend. */ salt = crypto . getRandomValues ( new Uint8Array ( 64 ) ) ) { const op = othersPublicKey ; const pr = privateKey ; const derivedKey = await CryptoWorker . aes_to_raw ( await derive_AES_GCM_256_Key_From_ECDH_256k1_Keys ( pr , op ) ) ; const _salt = typeof salt === 'string' ? decodeArrayBuffer ( salt ) : salt ; const UntitledUint8Array = concatArrayBuffer ( new Uint8Array ( derivedKey ) , _salt ) ; const password = await crypto . subtle . digest ( 'SHA-256' , concatArrayBuffer ( concatArrayBuffer ( UntitledUint8Array , _salt ) , decodeArrayBuffer ( 'KEY' ) ) ) ; const iv_pre = new Uint8Array ( await crypto . subtle . digest ( 'SHA-256' , concatArrayBuffer ( concatArrayBuffer ( UntitledUint8Array , _salt ) , decodeArrayBuffer ( 'IV' ) ) ) ) ; const iv = new Uint8Array ( 16 ) ; for ( let i = 0 ; i <= 16 ; i += 1 ) { iv [ i ] = iv_pre [ i ] ^ iv_pre [ 16 + i ] ; } const key = await CryptoWorker . raw_to_aes ( password ) ; return { key , salt : _salt , iv } ; } /**
 * Encrypt 1 to 1
 */ export async function encrypt1To1 ( info ) { const { version , privateKeyECDH , othersPublicKeyECDH } = info ; let { content } = info ; if ( typeof content === 'string' ) content = encodeText ( content ) ; const { iv , key , salt } = await deriveAESKey ( privateKeyECDH , othersPublicKeyECDH ) ; const encryptedContent = await CryptoWorker . encrypt_aes_gcm ( key , iv , content ) ; return { salt , encryptedContent , version : - 40 } ; } export async function generateOthersAESKeyEncrypted ( version , AESKey , privateKeyECDH , othersPublicKeyECDH ) { const exportedAESKey = encodeText ( JSON . stringify ( AESKey ) ) ; return Promise . all ( othersPublicKeyECDH . map < Promise < PublishedAESKeyRecordV40 > > ( async ( { key , name } ) => { const encrypted = await encrypt1To1 ( { version : - 40 as unknown as - 38 , content : exportedAESKey , othersPublicKeyECDH : key , privateKeyECDH : privateKeyECDH , } ) ; return { name , key : { version : - 40 , salt : encodeArrayBuffer ( encrypted . salt ) , encryptedKey : encodeArrayBuffer ( encrypted . encryptedContent ) , } , } ; } ) ) ; } /**
 * Encrypt 1 to N
 */ export async function encrypt1ToN ( info ) { const { version , content , othersPublicKeyECDH , privateKeyECDH , ownersLocalKey , iv } = info ; const AESKey = await CryptoWorker . generate_aes_gcm ( ) ; const encryptedContent = await CryptoWorker . encrypt_aes_gcm ( AESKey , iv , typeof content === 'string' ? encodeText ( content ) : content ) ; const exportedAESKey = encodeText ( JSON . stringify ( AESKey ) ) ; const ownersAESKeyEncrypted = ( await encryptWithAES ( { aesKey : ownersLocalKey , content : exportedAESKey , iv , } ) ) . content ; const othersAESKeyEncrypted = await generateOthersAESKeyEncrypted ( - 40 , AESKey , privateKeyECDH , othersPublicKeyECDH ) ; return { encryptedContent , iv , version : - 40 , ownersAESKeyEncrypted , othersAESKeyEncrypted , postAESKey : AESKey } ; } /**
 * Decrypt 1 to 1
 */ export async function decryptMessage1To1 ( info ) { const { anotherPublicKeyECDH , version , salt , encryptedContent , privateKeyECDH } = info ; const encrypted = typeof encryptedContent === 'string' ? decodeArrayBuffer ( encryptedContent ) : encryptedContent ; const { iv , key } = await deriveAESKey ( privateKeyECDH , anotherPublicKeyECDH , salt ) ; return CryptoWorker . decrypt_aes_gcm ( key , iv , encrypted ) ; } /**
 * Decrypt 1 to N message that send by other
 */ export async function decryptMessage1ToNByOther ( info ) { const { encryptedContent , privateKeyECDH , authorsPublicKeyECDH , iv } = info ; const AESKeyEncrypted = Array . isArray ( info . AESKeyEncrypted ) ? info . AESKeyEncrypted : [ info . AESKeyEncrypted ] ; let resolvedAESKey = null ; await Promise . all ( AESKeyEncrypted . map ( async ( key ) => { try { const result = await decryptMessage1To1 ( { version : - 40 , salt : key . salt , encryptedContent : key . encryptedKey , anotherPublicKeyECDH : authorsPublicKeyECDH , privateKeyECDH : privateKeyECDH , } ) ; resolvedAESKey = decodeText ( result ) ; } catch { } } ) ) ; if ( resolvedAESKey === null ) throw new Error ( i18n . t ( 'service_not_share_target' ) ) ; const aesKey = JSON . parse ( resolvedAESKey ) ; return [ await decryptWithAES ( { aesKey , iv , encrypted : encryptedContent } ) , aesKey ] ; } export async function extractAESKeyInMessage ( version , encodedEncryptedKey , _iv , myLocalKey ) { const iv = typeof _iv === 'string' ? decodeArrayBuffer ( _iv ) : _iv ; const encryptedKey = typeof encodedEncryptedKey === 'string' ? decodeArrayBuffer ( encodedEncryptedKey ) : encodedEncryptedKey ; const decryptedAESKeyJWK = JSON . parse ( decodeText ( await decryptWithAES ( { aesKey : myLocalKey , iv , encrypted : encryptedKey } ) ) ) ; return decryptedAESKeyJWK ; } /**
 * Decrypt 1 to N message that send by myself
 */ export async function decryptMessage1ToNByMyself ( info ) { const { encryptedContent , myLocalKey , iv , encryptedAESKey } = info ; const decryptedAESKey = await extractAESKeyInMessage ( - 40 , encryptedAESKey , iv , myLocalKey ) ; const post = await decryptWithAES ( { aesKey : decryptedAESKey , encrypted : encryptedContent , iv } ) ; return [ post , decryptedAESKey ] ; } /**
 * Decrypt the content encrypted by AES
 */ export async function decryptWithAES ( info ) { const { aesKey } = info ; const iv = typeof info . iv === 'string' ? decodeArrayBuffer ( info . iv ) : info . iv ; const encrypted = typeof info . encrypted === 'string' ? decodeArrayBuffer ( info . encrypted ) : info . encrypted ; return CryptoWorker . decrypt_aes_gcm ( aesKey , iv , encrypted ) ; } export async function encryptWithAES ( info ) { const iv = info . iv ? info . iv : crypto . getRandomValues ( new Uint8Array ( 16 ) ) ; const content = typeof info . content === 'string' ? encodeText ( info . content ) : info . content ; const encrypted = await CryptoWorker . encrypt_aes_gcm ( info . aesKey , iv , content ) ; return { content : encrypted , iv } ; } function extractCommentPayload ( text ) { const [ _ , toEnd ] = text . split ( '\u{1F3B6}2/4|' ) ; const [ content , _2 ] = ( toEnd || '' ) . split ( ':||' ) ; if ( content . length ) return content ; return ; } const getCommentKey = memoizePromise ( async function ( postIV , postContent ) { const pbkdf = await CryptoWorker . import_pbkdf2 ( encodeText ( postContent ) ) ; const aes = await derive_AES_GCM_256_Key_From_PBKDF2 ( pbkdf , encodeText ( postIV ) ) ; return aes ; } , ( a , b ) => a + b ) ; export async function encryptComment ( postIV , postContent , comment ) { if ( typeof postIV !== 'string' ) postIV = encodeArrayBuffer ( postIV ) ; if ( typeof postContent !== 'string' ) postContent = decodeText ( postContent ) ; const key = await getCommentKey ( postIV as string , postContent as string ) ; const x = await encryptWithAES ( { content : comment , aesKey : key , iv : decodeArrayBuffer ( postIV as string ) , } ) ; return `\u{1F3B6}2/4|${ encodeArrayBuffer ( x . content ) }:||` ; } export async function decryptComment ( postIV , postContent , encryptComment ) { if ( typeof postIV !== 'string' ) postIV = encodeArrayBuffer ( postIV ) ; if ( typeof postContent !== 'string' ) postContent = decodeText ( postContent ) ; if ( typeof encryptComment !== 'string' ) encryptComment = decodeText ( encryptComment ) ; const payload = extractCommentPayload ( encryptComment as string ) ; if ( ! payload ) return null ; const key = await getCommentKey ( postIV as string , postContent as string ) ; try { const x = await decryptWithAES ( { aesKey : key , iv : decodeArrayBuffer ( postIV as string ) , encrypted : payload , } ) ; return decodeText ( x ) ; } catch { return null ; } } export function typedMessageStringify ( x ) { throw new Error ( 'Not supported typed message in version older than v39.' ) ; } export function typedMessageParse ( x ) { return makeTypedMessageText ( x ) ; }