import { XChaCha20Poly1305 , NONCE_LENGTH , TAG_LENGTH , } from '@stablelib/xchacha20poly1305' ; import { generateKeyPair , sharedKey } from '@stablelib/x25519' ; import { randomBytes } from '@stablelib/random' ; import { concatKDF } from './utils' ; import * as u8a from 'uint8arrays' ; export const XC20P_IV_LENGTH = NONCE_LENGTH ; export const XC20P_TAG_LENGTH = TAG_LENGTH ; type Envelope = { ciphertext : Uint8Array ; tag : Uint8Array ; iv : Uint8Array ; aad ? : Uint8Array ; } ; type EncryptedKey = { encryptedKey : Uint8Array ; tag : Uint8Array ; iv : Uint8Array ; epPubKey : Uint8Array ; } ; type Encrypter = ( cleartext : Uint8Array , aad ? : Uint8Array ) => Envelope ; type Decrypter = ( ciphertext : Uint8Array , tag : Uint8Array , iv : Uint8Array , aad ? : Uint8Array ) => Uint8Array | null ; export const xc20pEncrypter = ( key ) : Encrypter => { const cipher = new XChaCha20Poly1305 ( key ) ; return ( cleartext , aad ? ) : Envelope => { const iv = randomBytes ( XC20P_IV_LENGTH ) ; const sealed = cipher . seal ( iv , cleartext , aad ) ; return { ciphertext : sealed . subarray ( 0 , sealed . length - XC20P_TAG_LENGTH ) , tag : sealed . subarray ( sealed . length - XC20P_TAG_LENGTH ) , iv , } ; } ; } ; export const xc20pDecrypter = ( key ) : Decrypter => { const cipher = new XChaCha20Poly1305 ( key ) ; return ( ciphertext , tag , iv , aad ? ) : Uint8Array | null => cipher . open ( iv , u8a . concat ( [ ciphertext , tag ] ) , aad ) ; } ; const ECDH_ES_XC20PKW_ALG = 'ECDH-ES+XC20PKW' ; const ECDH_ES_XC20PKW_KEYLEN = 256 ; export const x25519xc20pKeyWrap = ( publicKey ) => ( wrappedKey ) : EncryptedKey => { const epk = generateKeyPair ( ) ; const sharedSecret = sharedKey ( epk . secretKey , publicKey ) ; const kek = concatKDF ( sharedSecret , ECDH_ES_XC20PKW_KEYLEN , ECDH_ES_XC20PKW_ALG ) ; const res = xc20pEncrypter ( kek ) ( wrappedKey ) ; return { encryptedKey : res . ciphertext , tag : res . tag , iv : res . iv , epPubKey : epk . publicKey , } ; } ; export const x25519xc20pKeyUnwrap = ( secretKey ) => ( ciphertext , tag , iv , epPubKey ) : Uint8Array | null => { const sharedSecret = sharedKey ( secretKey , epPubKey ) ; const kek = concatKDF ( sharedSecret , ECDH_ES_XC20PKW_KEYLEN , ECDH_ES_XC20PKW_ALG ) ; return xc20pDecrypter ( kek ) ( ciphertext , tag , iv ) ; } ;