@@ -51,12 +51,27 @@ import {
51
51
import { randomBytes } from 'crypto'
52
52
53
53
export interface KmsHierarchicalKeyRingNodeInput {
54
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#initialization
55
+ //= type=implication
56
+ //# - MUST provide either a Branch Key Identifier or a [Branch Key Supplier](#branch-key-supplier)
54
57
branchKeyId ?: string
55
58
branchKeyIdSupplier ?: BranchKeyIdSupplier
59
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#initialization
60
+ //= type=implication
61
+ //# - MUST provide a [Keystore](../branch-key-store.md)
56
62
keyStore : BranchKeyStoreNode
63
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#initialization
64
+ //= type=implication
65
+ //# - MUST provide a [cache limit TTL](#cache-limit-ttl)
57
66
cacheLimitTtl : number
67
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#initialization
68
+ //= type=exception
69
+ //# - MAY provide a [Cache Type](#cache-type)
58
70
cache ?: CryptographicMaterialsCache < NodeAlgorithmSuite >
59
71
maxCacheSize ?: number
72
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#initialization
73
+ //= type=implication
74
+ //# - MAY provide a [Partition ID](#partition-id)
60
75
partitionId ?: string
61
76
}
62
77
@@ -74,6 +89,9 @@ export interface IKmsHierarchicalKeyRingNode extends KeyringNode {
74
89
75
90
export class KmsHierarchicalKeyRingNode
76
91
extends KeyringNode
92
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#interface
93
+ //= type=implication
94
+ //# MUST implement the [AWS Encryption SDK Keyring interface](../keyring-interface.md#interface)
77
95
implements IKmsHierarchicalKeyRingNode
78
96
{
79
97
public declare branchKeyId ?: string
@@ -96,8 +114,33 @@ export class KmsHierarchicalKeyRingNode
96
114
} : KmsHierarchicalKeyRingNodeInput ) {
97
115
super ( )
98
116
99
- needs ( ! partitionId || typeof partitionId === 'string' , 'Partition id must be a string.' )
100
- readOnlyProperty ( this , '_partition' , partitionId ? stringToUtf8Bytes ( partitionId ) : randomBytes ( 64 ) )
117
+ needs (
118
+ ! partitionId || typeof partitionId === 'string' ,
119
+ 'Partition id must be a string.'
120
+ )
121
+
122
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#partition-id
123
+ //= type=implication
124
+ //# The Partition ID MUST NOT be changed after initialization.
125
+ readOnlyProperty (
126
+ this ,
127
+ '_partition' ,
128
+
129
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#partition-id-1
130
+ //# It can either be a String provided by the user, which MUST be interpreted as the bytes of
131
+ //# UTF-8 Encoding of the String, or a v4 UUID, which SHOULD be interpreted as the 16 byte representation of the UUID.
132
+
133
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#partition-id-1
134
+ //# The constructor of the Hierarchical Keyring MUST record these bytes at construction time.
135
+
136
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#partition-id
137
+ //# If provided, it MUST be interpreted as UTF8 bytes.
138
+
139
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#partition-id
140
+ //= type=exception
141
+ //# If the PartitionId is NOT provided by the user, it MUST be set to the 16 byte representation of a v4 UUID.
142
+ partitionId ? stringToUtf8Bytes ( partitionId ) : randomBytes ( 64 )
143
+ )
101
144
102
145
/* Precondition: The branch key id must be a string */
103
146
if ( branchKeyId ) {
@@ -128,6 +171,9 @@ export class KmsHierarchicalKeyRingNode
128
171
readOnlyProperty (
129
172
this ,
130
173
'_logicalKeyStoreName' ,
174
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#logical-key-store-name
175
+ //# Logical Key Store Name MUST be converted to UTF8 Bytes to be used in
176
+ //# the cache identifiers.
131
177
stringToUtf8Bytes ( keyStore . getKeyStoreInfo ( ) . logicalKeyStoreName )
132
178
)
133
179
@@ -179,9 +225,6 @@ export class KmsHierarchicalKeyRingNode
179
225
if ( cache ) {
180
226
needs ( ! maxCacheSize , 'Max cache size not supported when passing a cache.' )
181
227
} else {
182
-
183
- console . log ( maxCacheSize )
184
-
185
228
/* Precondition: The max cache size must be a number */
186
229
needs (
187
230
// Order is important, 0 is a number but also false.
@@ -199,6 +242,13 @@ export class KmsHierarchicalKeyRingNode
199
242
'Max cache size must be non-negative and less than or equal Number.MAX_SAFE_INTEGER'
200
243
)
201
244
245
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#initialization
246
+ //# On initialization the Hierarchical Keyring MUST initialize a [cryptographic-materials-cache](../local-cryptographic-materials-cache.md) with the configured cache limit TTL and the max cache size.
247
+
248
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#initialization
249
+ //# If the Hierarchical Keyring does NOT get a `Shared` cache on initialization,
250
+ //# it MUST initialize a [cryptographic-materials-cache](../local-cryptographic-materials-cache.md)
251
+ //# with the user provided cache limit TTL and the entry capacity.
202
252
cache = getLocalCryptographicMaterialsCache ( maxCacheSize )
203
253
}
204
254
readOnlyProperty ( this , 'maxCacheSize' , maxCacheSize )
@@ -209,41 +259,25 @@ export class KmsHierarchicalKeyRingNode
209
259
}
210
260
211
261
async _onEncrypt (
262
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#onencrypt
263
+ //= type=implication
264
+ //# OnEncrypt MUST take [encryption materials](../structures.md#encryption-materials) as input.
212
265
encryptionMaterial : NodeEncryptionMaterial
213
266
) : Promise < NodeEncryptionMaterial > {
214
267
//= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#onencrypt
215
268
//# The `branchKeyId` used in this operation is either the configured branchKeyId, if supplied, or the result of the `branchKeySupplier`'s
216
269
//# `getBranchKeyId` operation, using the encryption material's encryption context as input.
217
270
const branchKeyId = getBranchKeyId ( this , encryptionMaterial )
218
271
219
- // compute the cache entry id for the active branch key material that we
220
- // want
272
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#onencrypt
273
+ //# The hierarchical keyring MUST use the formulas specified in [Appendix A](#appendix-a-cache-entry-identifier-formulas)
274
+ //# to compute the [cache entry identifier](../cryptographic-materials-cache.md#cache-identifier).
221
275
const cacheEntryId = getCacheEntryId (
222
276
this . _logicalKeyStoreName ,
223
277
this . _partition ,
224
278
branchKeyId
225
279
)
226
280
227
- //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#onencrypt
228
- //# If a cache entry is found and the entry's TTL has not expired, the hierarchical keyring MUST use those branch key materials for key wrapping.
229
-
230
- //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#onencrypt
231
- //# If a cache entry is not found or the cache entry is expired, the hierarchical keyring MUST attempt to obtain the branch key materials
232
- //# by querying the backing branch keystore specified in the [retrieve OnEncrypt branch key materials](#query-branch-keystore-onencrypt) section.
233
- //# If the keyring is not able to retrieve [branch key materials](../structures.md#branch-key-materials)
234
- //# through the underlying cryptographic materials cache or
235
- //# it no longer has access to them through the backing keystore, OnEncrypt MUST fail.
236
-
237
- //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#query-branch-keystore-onencrypt
238
- //# The branch keystore persists [branch keys](#definitions) that are reused to derive unique data keys for envelope encryption to
239
- //# reduce the number of calls to AWS KMS through the use of the
240
- //# [cryptographic materials cache](../cryptographic-materials-cache.md).
241
- //# OnEncrypt MUST call the Keystore's [GetActiveBranchKey](../branch-key-store.md#getactivebranchkey) operation with the following inputs:
242
- //# - the `branchKeyId` used in this operation
243
- //# If the Keystore's GetActiveBranchKey operation succeeds
244
- //# the keyring MUST put the returned branch key materials in the cache using the
245
- //# formula defined in [Appendix A](#appendix-a-cache-entry-identifier-formulas).
246
- //# Otherwise, OnEncrypt MUST fail.
247
281
const branchKeyMaterials = await getBranchKeyMaterials (
248
282
this ,
249
283
this . _cmc ,
@@ -254,7 +288,12 @@ export class KmsHierarchicalKeyRingNode
254
288
// get a pdk (generate it if not already set)
255
289
const pdk = getPlaintextDataKey ( encryptionMaterial )
256
290
257
- // encrypt the pdk
291
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#onencrypt
292
+ //# If the keyring is unable to wrap a plaintext data key, OnEncrypt MUST fail
293
+ //# and MUST NOT modify the [decryption materials](structures.md#decryption-materials).
294
+
295
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#onencrypt
296
+ //# - MUST wrap a data key with the branch key materials according to the [branch key wrapping](#branch-key-wrapping) section.
258
297
const edk = wrapPlaintextDataKey (
259
298
pdk ,
260
299
branchKeyMaterials ,
@@ -267,6 +306,9 @@ export class KmsHierarchicalKeyRingNode
267
306
}
268
307
269
308
async onDecrypt (
309
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#ondecrypt
310
+ //= type=implication
311
+ //# OnDecrypt MUST take [decryption materials](../structures.md#decryption-materials) and a list of [encrypted data keys](../structures.md#encrypted-data-keys) as input.
270
312
material : NodeDecryptionMaterial ,
271
313
encryptedDataKeys : EncryptedDataKey [ ]
272
314
) : Promise < DecryptionMaterial < NodeAlgorithmSuite > > {
@@ -319,23 +361,30 @@ export class KmsHierarchicalKeyRingNode
319
361
for ( const { encryptedDataKey : ciphertext } of filteredEdkObjs ) {
320
362
let udk : Uint8Array | undefined = undefined
321
363
try {
364
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#getitem-branch-keystore-ondecrypt
365
+ //# - Deserialize the UUID string representation of the `version` from the [encrypted data key](../structures.md#encrypted-data-key) [ciphertext](#ciphertext).
322
366
// get the branch key version (as compressed bytes) from the
323
367
// destructured ciphertext of the edk
324
368
const { branchKeyVersionAsBytesCompressed } = destructureCiphertext (
325
369
ciphertext ,
326
370
decryptionMaterial . suite
327
371
)
328
372
373
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#getitem-branch-keystore-ondecrypt
374
+ //# - The deserialized UUID string representation of the `version`
329
375
// uncompress the branch key version into regular utf8 bytes
330
376
const branchKeyVersionAsBytes = stringToUtf8Bytes (
331
377
decompressBytesToUuidv4 ( branchKeyVersionAsBytesCompressed )
332
378
)
333
379
334
- // compute the cache entry id for the versioned branch key material that we
335
- // want
380
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#ondecrypt
381
+ //# The hierarchical keyring MUST use the OnDecrypt formula specified in [Appendix A](#decryption-materials)
382
+ //# in order to compute the [cache entry identifier](cryptographic-materials-cache.md#cache-identifier).
336
383
const cacheEntryId = getCacheEntryId (
337
384
this . _logicalKeyStoreName ,
338
385
this . _partition ,
386
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#getitem-branch-keystore-ondecrypt
387
+ //# OnDecrypt MUST calculate the following values:
339
388
branchKeyId ,
340
389
branchKeyVersionAsBytes
341
390
)
@@ -346,29 +395,9 @@ export class KmsHierarchicalKeyRingNode
346
395
)
347
396
348
397
//= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#ondecrypt
349
- //# If a cache entry is found and the entry's TTL has not expired, the hierarchical keyring MUST use those branch key materials for key unwrapping.
350
-
351
- //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#ondecrypt
352
- //# If a cache entry is not found or the cache entry is expired, the hierarchical keyring
353
- //# MUST attempt to obtain the branch key materials by calling the backing branch key
354
- //# store specified in the [retrieve OnDecrypt branch key materials](#getitem-branch-keystore-ondecrypt) section.
355
- //# If the keyring is not able to retrieve `branch key materials` from the backing keystore then OnDecrypt MUST fail.
356
-
357
- //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#getitem-branch-keystore-ondecrypt
358
- //# The branch keystore persists [branch keys](#definitions) that are reused to derive unique data keys for key wrapping to
359
- //# reduce the number of calls to AWS KMS through the use of the
360
- //# [cryptographic materials cache](../cryptographic-materials-cache.md).
361
- //# OnDecrypt MUST calculate the following values:
362
- //# - Deserialize the UTF8-Decoded `branch-key-id` from the [key provider info](../structures.md#key-provider-information) of the [encrypted data key](../structures.md#encrypted-data-key)
363
- //# and verify this is equal to the configured or supplied `branch-key-id`.
364
- //# - Deserialize the UUID string representation of the `version` from the [encrypted data key](../structures.md#encrypted-data-key) [ciphertext](#ciphertext).
365
- //# OnDecrypt MUST call the Keystore's [GetBranchKeyVersion](../branch-key-store.md#getbranchkeyversion) operation with the following inputs:
366
- //# - The deserialized, UTF8-Decoded `branch-key-id`
367
- //# - The deserialized UUID string representation of the `version`
368
- //# If the Keystore's GetBranchKeyVersion operation succeeds
369
- //# the keyring MUST put the returned branch key materials in the cache using the
370
- //# formula defined in [Appendix A](#appendix-a-cache-entry-identifier-formulas).
371
- //# Otherwise, OnDecrypt MUST fail.
398
+ //# To decrypt each encrypted data key in the filtered set, the hierarchical keyring MUST attempt
399
+ //# to find the corresponding [branch key materials](../structures.md#branch-key-materials)
400
+ //# from the underlying [cryptographic materials cache](../local-cryptographic-materials-cache.md).
372
401
const branchKeyMaterials = await getBranchKeyMaterials (
373
402
this ,
374
403
this . _cmc ,
@@ -377,7 +406,8 @@ export class KmsHierarchicalKeyRingNode
377
406
branchKeyVersionAsString
378
407
)
379
408
380
- // unwrap the edk to get the udk/pdk
409
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#ondecrypt
410
+ //# - MUST unwrap the encrypted data key with the branch key materials according to the [branch key unwrapping](#branch-key-unwrapping) section.
381
411
udk = unwrapEncryptedDataKey (
382
412
ciphertext ,
383
413
branchKeyMaterials ,
@@ -412,3 +442,29 @@ export class KmsHierarchicalKeyRingNode
412
442
}
413
443
414
444
immutableClass ( KmsHierarchicalKeyRingNode )
445
+
446
+ // The JS version has not been released with a Storm Tracking CMC
447
+
448
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#initialization
449
+ //= type=exception
450
+ //# If the cache to initialize is a [Storm Tracking Cryptographic Materials Cache](../storm-tracking-cryptographic-materials-cache.md#overview)
451
+ //# then the [Grace Period](../storm-tracking-cryptographic-materials-cache.md#grace-period) MUST be less than the [cache limit TTL](#cache-limit-ttl).
452
+
453
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#initialization
454
+ //= type=exception
455
+ //# If no `cache` is provided, a `DefaultCache` MUST be configured with entry capacity of 1000.
456
+
457
+ // These are not something we can enforce
458
+
459
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#logical-key-store-name
460
+ //= type=exception
461
+ //# > Note: Users MUST NEVER have two different physical Key Stores with the same Logical Key Store Name.
462
+
463
+ //= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#shared-cache-considerations
464
+ //= type=exception
465
+ //# Any keyring that has access to the `Shared` cache MAY be able to use materials
466
+ //# that it MAY or MAY NOT have direct access to.
467
+ //#
468
+ //# Users MUST make sure that all of Partition ID, Logical Key Store Name of the Key Store for the Hierarchical Keyring
469
+ //# and Branch Key ID are set to be the same for two Hierarchical Keyrings if and only they want the keyrings to share
470
+ //# cache entries.
0 commit comments