@@ -33,6 +33,8 @@ const ENCRYPT_KMS_TYPE = 'encrypt.kms.type'
33
33
const ENCRYPT_DEK_ALGORITHM = 'encrypt.dek.algorithm'
34
34
// EncryptDekExpiryDays represents dek expiry days
35
35
const ENCRYPT_DEK_EXPIRY_DAYS = 'encrypt.dek.expiry.days'
36
+ // EncryptAlternateKmsKeyIds represents alternate kms key IDs
37
+ const ENCRYPT_ALTERNATE_KMS_KEY_IDS = 'encrypt.alternate.kms.key.ids'
36
38
37
39
// MillisInDay represents number of milliseconds in a day
38
40
const MILLIS_IN_DAY = 24 * 60 * 60 * 1000
@@ -387,7 +389,7 @@ export class EncryptionExecutorTransform {
387
389
}
388
390
let encryptedDek : Buffer | null = null
389
391
if ( ! kek . shared ) {
390
- kmsClient = getKmsClient ( this . executor . config ! , kek )
392
+ kmsClient = new KmsClientWrapper ( this . executor . config ! , kek )
391
393
// Generate new dek
392
394
const rawDek = this . cryptor . generateKey ( )
393
395
encryptedDek = await kmsClient . encrypt ( rawDek )
@@ -407,7 +409,7 @@ export class EncryptionExecutorTransform {
407
409
const keyMaterialBytes = await this . executor . client ! . getDekKeyMaterialBytes ( dek )
408
410
if ( keyMaterialBytes == null ) {
409
411
if ( kmsClient == null ) {
410
- kmsClient = getKmsClient ( this . executor . config ! , kek )
412
+ kmsClient = new KmsClientWrapper ( this . executor . config ! , kek )
411
413
}
412
414
const encryptedKeyMaterialBytes = await this . executor . client ! . getDekEncryptedKeyMaterialBytes ( dek )
413
415
const rawDek = await kmsClient . decrypt ( encryptedKeyMaterialBytes ! )
@@ -579,8 +581,8 @@ export class EncryptionExecutorTransform {
579
581
}
580
582
}
581
583
582
- function getKmsClient ( config : Map < string , string > , kek : Kek ) : KmsClient {
583
- let keyUrl = kek . kmsType + '://' + kek . kmsKeyId
584
+ function getKmsClient ( config : Map < string , string > , kmsType : string , kmsKeyId : string ) : KmsClient {
585
+ let keyUrl = kmsType + '://' + kmsKeyId
584
586
let kmsClient = Registry . getKmsClient ( keyUrl )
585
587
if ( kmsClient == null ) {
586
588
let kmsDriver = Registry . getKmsDriver ( keyUrl )
@@ -641,3 +643,60 @@ export class FieldEncryptionExecutorTransform implements FieldTransform {
641
643
}
642
644
}
643
645
646
+ export class KmsClientWrapper implements KmsClient {
647
+ private config : Map < string , string >
648
+ private kek : Kek
649
+ private kekId : string
650
+ private kmsKeyIds : string [ ]
651
+
652
+ constructor ( config : Map < string , string > , kek : Kek ) {
653
+ this . config = config
654
+ this . kek = kek
655
+ this . kekId = kek . kmsType + '://' + kek . kmsKeyId
656
+ this . kmsKeyIds = this . getKmsKeyIds ( )
657
+ }
658
+
659
+ getKmsKeyIds ( ) : string [ ] {
660
+ let kmsKeyIds = [ this . kek . kmsKeyId ! ]
661
+ if ( this . kek . kmsProps != null ) {
662
+ let alternateKmsKeyIds = this . kek . kmsProps [ ENCRYPT_ALTERNATE_KMS_KEY_IDS ]
663
+ if ( alternateKmsKeyIds != null ) {
664
+ kmsKeyIds = kmsKeyIds . concat ( alternateKmsKeyIds . split ( ',' ) . map ( id => id . trim ( ) ) )
665
+ }
666
+ }
667
+ return kmsKeyIds
668
+ }
669
+
670
+ supported ( keyUri : string ) : boolean {
671
+ return this . kekId === keyUri
672
+ }
673
+
674
+ async encrypt ( rawKey : Buffer ) : Promise < Buffer > {
675
+ for ( let i = 0 ; i < this . kmsKeyIds . length ; i ++ ) {
676
+ try {
677
+ let kmsClient = getKmsClient ( this . config , this . kek . kmsType ! , this . kmsKeyIds [ i ] )
678
+ return await kmsClient . encrypt ( rawKey )
679
+ } catch ( e ) {
680
+ if ( i === this . kmsKeyIds . length - 1 ) {
681
+ throw new RuleError ( `failed to encrypt key with all KMS keys: ${ e } ` )
682
+ }
683
+ }
684
+ }
685
+ throw new RuleError ( 'failed to encrypt key with all KMS keys' )
686
+ }
687
+
688
+ async decrypt ( encryptedKey : Buffer ) : Promise < Buffer > {
689
+ for ( let i = 0 ; i < this . kmsKeyIds . length ; i ++ ) {
690
+ try {
691
+ let kmsClient = getKmsClient ( this . config , this . kek . kmsType ! , this . kmsKeyIds [ i ] )
692
+ return await kmsClient . decrypt ( encryptedKey )
693
+ } catch ( e ) {
694
+ if ( i === this . kmsKeyIds . length - 1 ) {
695
+ throw new RuleError ( `failed to decrypt key with all KMS keys: ${ e } ` )
696
+ }
697
+ }
698
+ }
699
+ throw new RuleError ( 'failed to decrypt key with all KMS keys' )
700
+ }
701
+ }
702
+
0 commit comments