@@ -4,6 +4,7 @@ import {platform} from './platform'
44import {
55 formatTimeDiff ,
66 generateTransmissionKey ,
7+ generateTransmissionKeyQrc ,
78 getKeeperUrl ,
89 isTwoFactorResultCode , log ,
910 normal64Bytes ,
@@ -17,7 +18,7 @@ import {
1718 registerDeviceInRegionMessage ,
1819 updateDeviceMessage
1920} from './restMessages'
20- import { ClientConfigurationInternal , TransmissionKey } from './configuration' ;
21+ import { ClientConfigurationInternal , TransmissionKey , TransmissionKeyQrc } from './configuration' ;
2122import ApiRequestPayload = Authentication . ApiRequestPayload ;
2223import ApiRequest = Authentication . ApiRequest ;
2324import IDeviceResponse = Authentication . IDeviceResponse ;
@@ -37,10 +38,10 @@ export class KeeperEndpoint {
3738 private onsitePrivateKey : Uint8Array | null = null
3839 private onsitePublicKey : Uint8Array | null = null
3940
40- constructor ( private options : ClientConfigurationInternal ) {
41+ constructor ( private options : ClientConfigurationInternal ) {
4142 if ( options . deviceToken ) {
4243 this . deviceToken = options . deviceToken
43- }
44+ }
4445 if ( options . locale ) {
4546 this . locale = options . locale
4647 }
@@ -159,7 +160,35 @@ export class KeeperEndpoint {
159160 while ( true ) {
160161 const payload = 'toBytes' in message ? message . toBytes ( ) : new Uint8Array ( )
161162 const apiVersion = message . apiVersion || 0
162- const request = await this . prepareRequest ( payload , sessionToken , apiVersion )
163+
164+ // Determine whether to use QRC or traditional EC encryption
165+ const mlKemPublicKeyId = this . options . deviceConfig . mlKemPublicKeyId || 100
166+
167+ // Use QRC to wrap the transmission key
168+ const ecKeyId = this . options . deviceConfig . transmissionKeyId || 7
169+ if ( ! isAllowedNumber ( ecKeyId ) ) {
170+ throw new Error ( `Invalid EC key ID: ${ ecKeyId } ` )
171+ }
172+
173+ const serverEcPublicKey = platform . keys [ ecKeyId ]
174+ const serverMlKemPublicKey = platform . mlKemKeys [ mlKemPublicKeyId ]
175+
176+ if ( ! serverEcPublicKey ) {
177+ throw new Error ( `EC public key not found for ID: ${ ecKeyId } ` )
178+ }
179+ if ( ! serverMlKemPublicKey ) {
180+ throw new Error ( `ML-KEM public key not found for ID: ${ mlKemPublicKeyId } ` )
181+ }
182+
183+ const requestTransmissionKey = await generateTransmissionKeyQrc (
184+ mlKemPublicKeyId ,
185+ serverEcPublicKey ,
186+ serverMlKemPublicKey ,
187+ ecKeyId ,
188+ true // use optional data
189+ )
190+
191+ const request = await prepareApiRequest ( payload , requestTransmissionKey , sessionToken , this . locale , apiVersion )
163192 log ( `Calling REST URL: ${ this . getUrl ( message . path ) } ` , 'noCR' ) ;
164193 const startTime = Date . now ( )
165194 const response = await platform . post ( this . getUrl ( message . path ) , request )
@@ -174,7 +203,7 @@ export class KeeperEndpoint {
174203 console . log ( "Response code:" , response . statusCode ) ;
175204 }
176205 try {
177- const decrypted = await platform . aesGcmDecrypt ( response . data , this . _transmissionKey . key )
206+ const decrypted = await platform . aesGcmDecrypt ( response . data , requestTransmissionKey . key )
178207 if ( 'fromBytes' in message ) return message . fromBytes ( decrypted )
179208 return
180209 } catch {
@@ -256,7 +285,41 @@ export class KeeperEndpoint {
256285
257286 public async prepareRequest ( payload : Uint8Array | unknown , sessionToken ?: string , apiVersion ?: number ) : Promise < Uint8Array > {
258287 this . _transmissionKey = await this . getTransmissionKey ( )
259- return prepareApiRequest ( payload , this . _transmissionKey , sessionToken , this . locale , apiVersion )
288+
289+ // Determine whether to use QRC or traditional EC encryption
290+ const mlKemPublicKeyId = this . options . deviceConfig . mlKemPublicKeyId
291+ let requestTransmissionKey : TransmissionKey | TransmissionKeyQrc
292+
293+ if ( mlKemPublicKeyId ) {
294+ // Use QRC to wrap the transmission key
295+ const ecKeyId = this . options . deviceConfig . transmissionKeyId || 7
296+ if ( ! isAllowedNumber ( ecKeyId ) ) {
297+ throw new Error ( `Invalid EC key ID: ${ ecKeyId } ` )
298+ }
299+
300+ const serverEcPublicKey = platform . keys [ ecKeyId ]
301+ const serverMlKemPublicKey = platform . mlKemKeys [ mlKemPublicKeyId ]
302+
303+ if ( ! serverEcPublicKey ) {
304+ throw new Error ( `EC public key not found for ID: ${ ecKeyId } ` )
305+ }
306+ if ( ! serverMlKemPublicKey ) {
307+ throw new Error ( `ML-KEM public key not found for ID: ${ mlKemPublicKeyId } ` )
308+ }
309+
310+ requestTransmissionKey = await generateTransmissionKeyQrc (
311+ mlKemPublicKeyId ,
312+ serverEcPublicKey ,
313+ serverMlKemPublicKey ,
314+ ecKeyId ,
315+ true // use optional data
316+ )
317+ } else {
318+ // Use traditional EC encryption
319+ requestTransmissionKey = this . _transmissionKey
320+ }
321+
322+ return prepareApiRequest ( payload , requestTransmissionKey , sessionToken , this . locale , apiVersion )
260323 }
261324
262325 async decryptPushMessage ( pushMessageData : Uint8Array ) : Promise < WssClientResponse > {
@@ -329,7 +392,13 @@ export async function getPushConnectionRequest(messageSessionUid: Uint8Array, tr
329392 return webSafe64FromBytes ( apiRequest )
330393}
331394
332- export async function prepareApiRequest ( payload : Uint8Array | unknown , transmissionKey : TransmissionKey , sessionToken ?: string , locale ?: string , apiVersion ?: number ) : Promise < Uint8Array > {
395+ export async function prepareApiRequest (
396+ payload : Uint8Array | unknown ,
397+ transmissionKey : TransmissionKey | TransmissionKeyQrc ,
398+ sessionToken ?: string ,
399+ locale ?: string ,
400+ apiVersion ?: number
401+ ) : Promise < Uint8Array > {
333402 const requestPayload = ApiRequestPayload . create ( )
334403 if ( payload ) {
335404 requestPayload . payload = payload instanceof Uint8Array
@@ -342,12 +411,37 @@ export async function prepareApiRequest(payload: Uint8Array | unknown, transmiss
342411 requestPayload . apiVersion = apiVersion || 0
343412 let requestPayloadBytes = ApiRequestPayload . encode ( requestPayload ) . finish ( )
344413 let encryptedRequestPayload = await platform . aesGcmEncrypt ( requestPayloadBytes , transmissionKey . key )
345- let apiRequest = ApiRequest . create ( {
346- encryptedTransmissionKey : transmissionKey . encryptedKey ,
347- encryptedPayload : encryptedRequestPayload ,
348- publicKeyId : transmissionKey . publicKeyId ,
349- locale : locale || 'en_US'
350- } )
414+
415+ // Check if this is a QRC transmission key
416+ const isQrc = 'qrcMessageKey' in transmissionKey ;
417+
418+ let apiRequest : Authentication . IApiRequest ;
419+ if ( isQrc ) {
420+ // Use QRC encryption
421+ const qrcKey = transmissionKey as TransmissionKeyQrc ;
422+ apiRequest = ApiRequest . create ( {
423+ qrcMessageKey : {
424+ clientEcPublicKey : qrcKey . qrcMessageKey . clientEcPublicKey ,
425+ mlKemEncapsulatedKey : qrcKey . qrcMessageKey . mlKemEncapsulatedKey ,
426+ data : qrcKey . qrcMessageKey . data ,
427+ msgVersion : qrcKey . qrcMessageKey . msgVersion ,
428+ ecKeyId : qrcKey . qrcMessageKey . ecKeyId
429+ } ,
430+ encryptedPayload : encryptedRequestPayload ,
431+ publicKeyId : qrcKey . publicKeyId , // ML-KEM key ID
432+ encryptedTransmissionKey : qrcKey . optionalData || new Uint8Array ( 0 ) , // Optional data or empty
433+ locale : locale || 'en_US'
434+ } ) ;
435+ } else {
436+ // Use traditional EC encryption
437+ apiRequest = ApiRequest . create ( {
438+ encryptedTransmissionKey : transmissionKey . encryptedKey ,
439+ encryptedPayload : encryptedRequestPayload ,
440+ publicKeyId : transmissionKey . publicKeyId ,
441+ locale : locale || 'en_US'
442+ } ) ;
443+ }
444+
351445 return ApiRequest . encode ( apiRequest ) . finish ( )
352446}
353447
0 commit comments