11import { bech32 } from "@scure/base"
2- import { hkdf , extract , expand } from "@noble/hashes/hkdf"
3- import { sha256 } from "@noble/hashes/sha2"
4- import { scrypt } from "@noble/hashes/scrypt"
5- import { chacha20poly1305 } from "@noble/ciphers/chacha"
6- import { XWing } from "@noble/post-quantum/hybrid.js"
7- import { randomBytes } from "@noble/hashes/utils"
2+ import { hkdf , extract , expand } from "@noble/hashes/hkdf.js "
3+ import { sha256 } from "@noble/hashes/sha2.js "
4+ import { scrypt } from "@noble/hashes/scrypt.js "
5+ import { chacha20poly1305 } from "@noble/ciphers/chacha.js "
6+ import { MLKEM768X25519 } from "@noble/post-quantum/hybrid.js"
7+ import { randomBytes } from "@noble/hashes/utils.js "
88import { base64nopad } from "@scure/base"
99import * as x25519 from "./x25519.js"
1010import { Stanza } from "./format.js"
@@ -73,7 +73,7 @@ export async function identityToRecipient(identity: string | CryptoKey): Promise
7373 const res = bech32 . decodeToBytes ( identity )
7474 if ( res . prefix . toUpperCase ( ) !== "AGE-SECRET-KEY-PQ-" ||
7575 res . bytes . length !== 32 ) { throw Error ( "invalid identity" ) }
76- const recipient = XWing . getPublicKey ( res . bytes )
76+ const recipient = MLKEM768X25519 . getPublicKey ( res . bytes )
7777 // Use encode directly to disable the 90 character bech32 limit.
7878 return bech32 . encode ( "age1pq" , bech32 . toWords ( recipient ) , false )
7979 } else {
@@ -99,7 +99,7 @@ export class HybridRecipient implements Recipient {
9999 }
100100
101101 wrapFileKey ( fileKey : Uint8Array ) : Stanza [ ] {
102- const { cipherText : encapsulatedKey , sharedSecret } = XWing . encapsulate ( this . recipient )
102+ const { cipherText : encapsulatedKey , sharedSecret } = MLKEM768X25519 . encapsulate ( this . recipient )
103103 const label = new TextEncoder ( ) . encode ( "age-encryption.org/mlkem768x25519" )
104104 const { key, nonce } = hpkeContext ( hpkeMLKEM768X25519 , sharedSecret , label )
105105 const ciphertext = chacha20poly1305 ( key , nonce ) . encrypt ( fileKey )
@@ -134,7 +134,7 @@ export class HybridIdentity implements Identity {
134134 throw Error ( "invalid mlkem768x25519 stanza" )
135135 }
136136
137- const sharedSecret = XWing . decapsulate ( share , this . identity )
137+ const sharedSecret = MLKEM768X25519 . decapsulate ( share , this . identity )
138138 const label = new TextEncoder ( ) . encode ( "age-encryption.org/mlkem768x25519" )
139139 const { key, nonce } = hpkeContext ( hpkeMLKEM768X25519 , sharedSecret , label )
140140 try {
@@ -226,7 +226,8 @@ export class X25519Recipient implements Recipient {
226226 salt . set ( share )
227227 salt . set ( this . recipient , share . length )
228228
229- const key = hkdf ( sha256 , secret , salt , "age-encryption.org/v1/X25519" , 32 )
229+ const label = new TextEncoder ( ) . encode ( "age-encryption.org/v1/X25519" )
230+ const key = hkdf ( sha256 , secret , salt , label , 32 )
230231 return [ new Stanza ( [ "X25519" , base64nopad . encode ( share ) ] , encryptFileKey ( fileKey , key ) ) ]
231232 }
232233}
@@ -269,7 +270,8 @@ export class X25519Identity implements Identity {
269270 salt . set ( share )
270271 salt . set ( recipient , share . length )
271272
272- const key = hkdf ( sha256 , secret , salt , "age-encryption.org/v1/X25519" , 32 )
273+ const label = new TextEncoder ( ) . encode ( "age-encryption.org/v1/X25519" )
274+ const key = hkdf ( sha256 , secret , salt , label , 32 )
273275 const fileKey = decryptFileKey ( s . body , key )
274276 if ( fileKey !== null ) return fileKey
275277 }
0 commit comments