/**
 * Copyright (c) AXA Assistance France
 *
 * Licensed under the AXA Assistance France License (the "License"); you
 * may not use this file except in compliance with the License.
 * A copy of the License can be found in the LICENSE.md file distributed
 * together with this file.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ module import { KeyObject , randomBytes } from 'crypto' ; import { exportJWK , importJWK , generateKeyPair , GenerateKeyPairOptions , } from 'jose' ; import { JWK } from './types' ; import { JWKWithKid } from './types-internals' ; const generateRandomKid = ( ) => { return randomBytes ( 40 ) . toString ( 'hex' ) ; } ; type JwkTransformer = ( jwk : JWK ) => JWK ; const RsaPrivateFieldsRemover = ( jwk ) => { const x = { ... jwk } ; delete x . d ; delete x . p ; delete x . q ; delete x . dp ; delete x . dq ; delete x . qi ; return x ; } ; const EcdsaPrivateFieldsRemover = ( jwk ) => { const x = { ... jwk } ; delete x . d ; return x ; } ; const EddsaPrivateFieldsRemover = ( jwk ) => { const x = { ... jwk } ; delete x . d ; return x ; } ; const privateToPublicTransformerMap = { RS256 : RsaPrivateFieldsRemover , RS384 : RsaPrivateFieldsRemover , RS512 : RsaPrivateFieldsRemover , PS256 : RsaPrivateFieldsRemover , PS384 : RsaPrivateFieldsRemover , PS512 : RsaPrivateFieldsRemover , ES256 : EcdsaPrivateFieldsRemover , ES256K : EcdsaPrivateFieldsRemover , ES384 : EcdsaPrivateFieldsRemover , ES512 : EcdsaPrivateFieldsRemover , EdDSA : EddsaPrivateFieldsRemover , } ; const supportedAlgs = Object . keys ( privateToPublicTransformerMap ) ; function normalizeKeyKid ( jwk , opts ? ) { if ( jwk . kid !== undefined ) { return ; } if ( opts !== undefined && opts . kid !== undefined ) { jwk . kid = opts . kid ; } else { jwk . kid = generateRandomKid ( ) ; } } /**
 * Simple JWK store
 */ export class JWKStore { #keyRotator : KeyRotator ; /**
     * Creates a new instance of the keystore.
     */ constructor ( ) { this . #keyRotator = new KeyRotator ( ) ; } param { string } alg param opts param { string } opts . kid param { string } opts . crv returns { Promise < JWK > } async generate ( alg , opts ? ) { const generateOpts = opts !== undefined && opts . crv !== undefined ? { crv : opts . crv } : { } ; const pair = await generateKeyPair ( alg , generateOpts ) ; const joseJwk = await exportJWK ( pair . privateKey ) ; normalizeKeyKid ( joseJwk , opts ) ; joseJwk . alg = alg ; const jwk = joseJwk as JWK ; this . #keyRotator . add ( jwk ) ; return jwk ; } param { object } maybeJwk returns { Promise < JWK > } async add ( maybeJwk ) { const tempJwk = { ... maybeJwk } ; normalizeKeyKid ( tempJwk ) ; if ( tempJwk . alg === undefined ) { throw new Error ( 'Unspecified JWK "alg" property' ) ; } if ( ! supportedAlgs . includes ( tempJwk . alg ) ) { throw new Error ( `Unsupported JWK "alg" value ("${ tempJwk . alg }")` ) ; } const jwk = tempJwk as JWK ; const privateKey = await importJWK ( jwk ) ; if ( ! ( privateKey instanceof KeyObject ) || privateKey . type !== 'private' ) { throw new Error ( `Invalid JWK type. No "private" key related data has been found.` ) ; } this . #keyRotator . add ( jwk ) ; return jwk ; } param { string } kid returns { JWK . Key | null } get ( kid ? ) { return this . #keyRotator . next ( kid ) ; } param { boolean } includePrivateFields returns { JWK [ ] } toJSON ( includePrivateFields = false ) { return this . #keyRotator . toJSON ( includePrivateFields ) ; } } class KeyRotator { #keys : JWK [ ] = [ ] ; add ( key ) { const pos = this . findNext ( key . kid ) ; if ( pos > - 1 ) { this . #keys . splice ( pos , 1 ) ; } this . #keys . push ( key ) ; } next ( kid ? ) { const i = this . findNext ( kid ) ; if ( i === - 1 ) { return undefined ; } return this . moveToTheEnd ( i ) ; } toJSON ( includePrivateFields ) { const keys = [ ] ; for ( const key of this . #keys ) { if ( includePrivateFields ) { keys . push ( { ... key } ) ; continue ; } const cleaner = privateToPublicTransformerMap [ key . alg ] ; keys . push ( cleaner ( key ) ) ; } return keys ; } private findNext ( kid ? ) { if ( this . #keys . length === 0 ) { return - 1 ; } if ( kid === undefined ) { return 0 ; } return this . #keys . findIndex ( ( x ) => x . kid === kid ) ; } private moveToTheEnd ( i ) { const [ key ] = this . #keys . splice ( i , 1 ) ; this . #keys . push ( key ) ; return key ; } }