@@ -4,6 +4,7 @@ import {platform} from './platform'
44import {
55 formatTimeDiff ,
66 generateTransmissionKey ,
7+ generateHpkeTransmissionKey ,
78 getKeeperUrl ,
89 isTwoFactorResultCode , log ,
910 normal64Bytes ,
@@ -26,7 +27,7 @@ import WssClientResponse = Push.WssClientResponse;
2627import WssConnectionRequest = Push . WssConnectionRequest ;
2728import SsoCloudResponse = SsoCloud . SsoCloudResponse ;
2829import { KeeperHttpResponse , RestCommand } from './commands'
29- import { AllowedNumbers , isAllowedNumber } from './transmissionKeys'
30+ import { AllowedEcKeyIds , AllowedMlKemKeyIds , isAllowedEcKeyId , isAllowedMlKemKeyId } from './transmissionKeys'
3031
3132export class KeeperEndpoint {
3233 private _transmissionKey ?: TransmissionKey
@@ -37,21 +38,24 @@ 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 }
4748 }
4849
4950 async getTransmissionKey ( ) :Promise < TransmissionKey > {
50- const deviceConfigTransmissionKeyId = this . options . deviceConfig . transmissionKeyId || 7
51- if ( ! this . _transmissionKey && isAllowedNumber ( deviceConfigTransmissionKeyId ) ) {
52- this . _transmissionKey = await generateTransmissionKey ( deviceConfigTransmissionKeyId )
51+ const DEFAULT_PROD_EC_KEY_ID = 10
52+ const DEFAULT_PROD_ML_KEM_KEY_ID = 100
53+ const deviceConfigTransmissionKeyId = this . options . deviceConfig . transmissionKeyId || DEFAULT_PROD_EC_KEY_ID
54+ const deviceConfigMlKemKeyId = this . options . deviceConfig . mlKemPublicKeyId || DEFAULT_PROD_ML_KEM_KEY_ID
55+ if ( ! this . _transmissionKey && isAllowedEcKeyId ( deviceConfigTransmissionKeyId ) && isAllowedMlKemKeyId ( deviceConfigMlKemKeyId ) ) {
56+ this . _transmissionKey = await generateTransmissionKey ( deviceConfigTransmissionKeyId , deviceConfigMlKemKeyId )
5357 } else if ( ! this . _transmissionKey ) {
54- this . _transmissionKey = await generateTransmissionKey ( 7 )
58+ this . _transmissionKey = await generateTransmissionKey ( DEFAULT_PROD_EC_KEY_ID , DEFAULT_PROD_ML_KEM_KEY_ID )
5559 }
5660
5761 return this . _transmissionKey
@@ -159,7 +163,8 @@ export class KeeperEndpoint {
159163 while ( true ) {
160164 const payload = 'toBytes' in message ? message . toBytes ( ) : new Uint8Array ( )
161165 const apiVersion = message . apiVersion || 0
162- const request = await this . prepareRequest ( payload , sessionToken , apiVersion )
166+
167+ const request = await prepareApiRequest ( payload , this . _transmissionKey , sessionToken , this . locale , apiVersion )
163168 log ( `Calling REST URL: ${ this . getUrl ( message . path ) } ` , 'noCR' ) ;
164169 const startTime = Date . now ( )
165170 const response = await platform . post ( this . getUrl ( message . path ) , request )
@@ -183,8 +188,9 @@ export class KeeperEndpoint {
183188 const errorObj : KeeperError = JSON . parse ( errorMessage )
184189 switch ( errorObj . error ) {
185190 case 'key' :
186- if ( isAllowedNumber ( errorObj . key_id ! ) ) {
187- await this . updateTransmissionKey ( errorObj . key_id ! )
191+ // NOTE: I believe this is subject to change, because the new logic is not backward compatible
192+ if ( isAllowedEcKeyId ( errorObj . qrc_ec_key_id ! ) && isAllowedMlKemKeyId ( errorObj . key_id ! ) ) {
193+ await this . updateTransmissionKey ( errorObj . qrc_ec_key_id ! , errorObj . key_id ! )
188194 } else {
189195 throw new Error ( 'Incorrect Transmission Key ID being used.' )
190196 }
@@ -245,10 +251,11 @@ export class KeeperEndpoint {
245251 return platform . get ( this . getUrl ( path ) , { } )
246252 }
247253
248- public async updateTransmissionKey ( keyNumber : AllowedNumbers ) {
249- this . _transmissionKey = await generateTransmissionKey ( keyNumber )
254+ public async updateTransmissionKey ( ecKeyId : AllowedEcKeyIds , mlKemKeyId : AllowedMlKemKeyIds ) {
255+ this . _transmissionKey = await generateTransmissionKey ( ecKeyId , mlKemKeyId )
250256
251- this . options . deviceConfig . transmissionKeyId = keyNumber
257+ this . options . deviceConfig . transmissionKeyId = ecKeyId
258+ this . options . deviceConfig . mlKemPublicKeyId = mlKemKeyId
252259 if ( this . options . onDeviceConfig ) {
253260 await this . options . onDeviceConfig ( this . options . deviceConfig , this . options . host ) ;
254261 }
@@ -329,7 +336,13 @@ export async function getPushConnectionRequest(messageSessionUid: Uint8Array, tr
329336 return webSafe64FromBytes ( apiRequest )
330337}
331338
332- export async function prepareApiRequest ( payload : Uint8Array | unknown , transmissionKey : TransmissionKey , sessionToken ?: string , locale ?: string , apiVersion ?: number ) : Promise < Uint8Array > {
339+ export async function prepareApiRequest (
340+ payload : Uint8Array | unknown ,
341+ transmissionKey : TransmissionKey ,
342+ sessionToken ?: string ,
343+ locale ?: string ,
344+ apiVersion ?: number
345+ ) : Promise < Uint8Array > {
333346 const requestPayload = ApiRequestPayload . create ( )
334347 if ( payload ) {
335348 requestPayload . payload = payload instanceof Uint8Array
@@ -340,14 +353,23 @@ export async function prepareApiRequest(payload: Uint8Array | unknown, transmiss
340353 requestPayload . encryptedSessionToken = normal64Bytes ( sessionToken ) ;
341354 }
342355 requestPayload . apiVersion = apiVersion || 0
343- let requestPayloadBytes = ApiRequestPayload . encode ( requestPayload ) . finish ( )
344- let encryptedRequestPayload = await platform . aesGcmEncrypt ( requestPayloadBytes , transmissionKey . key )
345- let apiRequest = ApiRequest . create ( {
346- encryptedTransmissionKey : transmissionKey . encryptedKey ,
356+ const requestPayloadBytes = ApiRequestPayload . encode ( requestPayload ) . finish ( )
357+ const encryptedRequestPayload = await platform . aesGcmEncrypt ( requestPayloadBytes , transmissionKey . key )
358+
359+ // Use QRC to wrap the transmission key
360+ const hpkeTransmissionKey = await generateHpkeTransmissionKey (
361+ transmissionKey ,
362+ true // use optional data
363+ )
364+
365+ const apiRequest = ApiRequest . create ( {
366+ qrcMessageKey : hpkeTransmissionKey . qrcMessageKey ,
347367 encryptedPayload : encryptedRequestPayload ,
348- publicKeyId : transmissionKey . publicKeyId ,
368+ publicKeyId : hpkeTransmissionKey . publicKeyId , // ML-KEM key ID
369+ encryptedTransmissionKey : hpkeTransmissionKey . optionalData || null , // Optional data or empty
349370 locale : locale || 'en_US'
350- } )
371+ } ) ;
372+
351373 return ApiRequest . encode ( apiRequest ) . finish ( )
352374}
353375
0 commit comments