Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 406dee7

Browse files
nvobilisseebees
authored andcommittedDec 12, 2024
type checks to class constructors and methods (#637)
* type checks to class constructors and methods * modify grant token initialization
1 parent 6fc741d commit 406dee7

File tree

10 files changed

+459
-138
lines changed

10 files changed

+459
-138
lines changed
 

‎modules/branch-keystore-node/src/branch_keystore.ts

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import { KmsConfig, RegionalKmsConfig } from './kms_config'
4+
import { isKmsConfig, KmsConfig, RegionalKmsConfig } from './kms_config'
55
import { KMSClient } from '@aws-sdk/client-kms'
66
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
77
import {
@@ -54,7 +54,7 @@ export interface IBranchKeyStoreNode {
5454
kmsClient: KMSClient
5555
ddbClient: DynamoDBClient
5656
keyStoreId: string
57-
grantTokens: ReadonlyArray<string>
57+
grantTokens?: ReadonlyArray<string>
5858

5959
getActiveBranchKey(branchKeyId: string): Promise<NodeBranchKeyMaterial>
6060
getBranchKeyVersion(
@@ -70,7 +70,7 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
7070
public declare kmsClient: KMSClient
7171
public declare ddbClient: DynamoDBClient
7272
public declare keyStoreId: string
73-
public declare grantTokens: ReadonlyArray<string>
73+
public declare grantTokens?: ReadonlyArray<string>
7474

7575
constructor({
7676
ddbTableName,
@@ -81,18 +81,67 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
8181
keyStoreId,
8282
grantTokens,
8383
}: BranchKeyStoreNodeInput) {
84+
/* Precondition: DDB table name must be a string */
85+
needs(typeof ddbTableName === 'string', 'DDB table name must be a string')
86+
87+
/* Precondition: Logical keystore name must be a string */
88+
needs(
89+
typeof logicalKeyStoreName === 'string',
90+
'Logical keystore name must be a string'
91+
)
92+
93+
/* Precondition: KMS Configuration must be SRK */
94+
needs(isKmsConfig(kmsConfiguration), 'KMS Configuration must be SRK')
95+
96+
/* Precondition: KMS client must be a KMSClient */
97+
if (kmsClient) {
98+
needs(kmsClient instanceof KMSClient, 'KMS client must be a KMSClient')
99+
} else {
100+
// ensure it's strictly undefined and not some other falsey value
101+
kmsClient = undefined
102+
}
103+
104+
/* Precondition: DDB client must be a DynamoDBClient */
105+
if (ddbClient) {
106+
needs(
107+
ddbClient instanceof DynamoDBClient,
108+
'DDB client must be a DynamoDBClient'
109+
)
110+
} else {
111+
// ensure it's strictly undefined and not some other falsey value
112+
ddbClient = undefined
113+
}
114+
115+
/* Precondition: Keystore id must be a string */
116+
if (keyStoreId) {
117+
needs(typeof keyStoreId === 'string', 'Keystore id must be a string')
118+
} else {
119+
// ensure it's strictly undefined and not some other falsey value
120+
keyStoreId = undefined
121+
}
122+
123+
/* Precondition: Grant tokens must be a string array */
124+
if (grantTokens) {
125+
needs(
126+
Array.isArray(grantTokens) &&
127+
grantTokens.every((grantToken) => typeof grantToken === 'string'),
128+
'Grant tokens must be a string array'
129+
)
130+
} else {
131+
// ensure it's strictly undefined and not some other falsey value
132+
grantTokens = undefined
133+
}
134+
84135
//= aws-encryption-sdk-specification/framework/branch-key-store.md#keystore-id
85136
//# The Identifier for this KeyStore.
86137
//# If one is not supplied, then a [version 4 UUID](https://www.ietf.org/rfc/rfc4122.txt) MUST be used.
87138
readOnlyProperty(this, 'keyStoreId', keyStoreId ? keyStoreId : v4())
139+
/* Postcondition: If unprovided, the keystore id is a generated valid uuidv4 */
88140

89141
//= aws-encryption-sdk-specification/framework/branch-key-store.md#aws-kms-grant-tokens
90142
//# A list of AWS KMS [grant tokens](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token).
91-
readOnlyProperty(
92-
this,
93-
'grantTokens',
94-
Object.freeze(grantTokens ? grantTokens : [])
95-
)
143+
readOnlyProperty(this, 'grantTokens', grantTokens)
144+
/* Postcondition: If unprovided, the grant tokens are undefined */
96145

97146
needs(kmsConfiguration, 'AWS KMS Configuration required')
98147
readOnlyProperty(this, 'kmsConfiguration', Object.freeze(kmsConfiguration))
@@ -125,6 +174,7 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
125174
region: (this.kmsConfiguration as RegionalKmsConfig).getRegion(),
126175
})
127176
)
177+
/* Postcondition: If unprovided, the DDB client is configured */
128178

129179
//= aws-encryption-sdk-specification/framework/branch-key-store.md#kms-client
130180
//# The KMS Client used when wrapping and unwrapping keys.
@@ -159,6 +209,7 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
159209
customUserAgent: KMS_CLIENT_USER_AGENT,
160210
})
161211
)
212+
/* Postcondition: If unprovided, the KMS client is configured */
162213

163214
//= aws-encryption-sdk-specification/framework/branch-key-store.md#table-name
164215
//# The table name of the DynamoDb table that backs this Keystore.
@@ -223,7 +274,10 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
223274
//# On invocation, the caller:
224275

225276
//# - MUST supply a `branch-key-id`
226-
needs(branchKeyId, 'MUST supply a branch key id')
277+
needs(
278+
branchKeyId && typeof branchKeyId === 'string',
279+
'MUST supply a string branch key id'
280+
)
227281

228282
//= aws-encryption-sdk-specification/framework/branch-key-store.md#getactivebranchkey
229283
//# To get the active version for the branch key id from the keystore
@@ -244,9 +298,13 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
244298

245299
//# - MUST supply a `branch-key-id`
246300
//# - MUST supply a `branchKeyVersion`
301+
needs(
302+
branchKeyId && typeof branchKeyId === 'string',
303+
'MUST supply a string branch key id'
304+
)
247305
needs(
248306
branchKeyId && branchKeyVersion,
249-
'MUST supply a branch key id and branch key version'
307+
'MUST supply a string branch key version'
250308
)
251309

252310
//= aws-encryption-sdk-specification/framework/branch-key-store.md#getbranchkeyversion
@@ -260,3 +318,10 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
260318
}
261319

262320
immutableClass(BranchKeyStoreNode)
321+
322+
// type guard
323+
export function isIBranchKeyStoreNode(
324+
keyStore: any
325+
): keyStore is BranchKeyStoreNode {
326+
return keyStore instanceof BranchKeyStoreNode
327+
}

‎modules/branch-keystore-node/src/branch_keystore_helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ export async function decryptBranchKey(
269269
KeyId: (kmsConfiguration as KmsKeyArnConfig).getArn(), // make this type casting assumption since only SRK Compatibility is supported currently
270270
CiphertextBlob: branchKeyRecord[BRANCH_KEY_FIELD],
271271
EncryptionContext: authenticatedEncryptionContext,
272-
GrantTokens: grantTokens.slice(),
272+
GrantTokens: grantTokens ? grantTokens.slice() : grantTokens,
273273
})
274274
)
275275

‎modules/branch-keystore-node/src/kms_config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ export abstract class KmsKeyArnConfig implements RegionalKmsConfig {
3737
//# `KMS Key ARN` and `KMS MRKey ARN` MUST take an additional argument
3838
//# that is a KMS ARN.
3939
constructor(arn: string) {
40+
/* Precondition: ARN must be a string */
41+
needs(typeof arn === 'string', 'ARN must be a string')
42+
4043
//= aws-encryption-sdk-specification/framework/branch-key-store.md#aws-kms-configuration
4144
//# This ARN MUST NOT be an Alias.
4245
//# This ARN MUST be a valid
@@ -76,3 +79,7 @@ export class SrkCompatibilityKmsConfig extends KmsKeyArnConfig {
7679
return this.getArn() === otherArn
7780
}
7881
}
82+
83+
export function isKmsConfig(config: any): config is SrkCompatibilityKmsConfig {
84+
return config instanceof SrkCompatibilityKmsConfig
85+
}

‎modules/branch-keystore-node/test/branch_keystore.test.ts

Lines changed: 204 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import chai, { expect } from 'chai'
5-
import { BranchKeyStoreNode } from '../src/branch_keystore'
5+
import {
6+
BranchKeyStoreNode,
7+
isIBranchKeyStoreNode,
8+
} from '../src/branch_keystore'
69
import { validate, v4, version } from 'uuid'
710
import chaiAsPromised from 'chai-as-promised'
811
import {
@@ -36,6 +39,12 @@ import {
3639

3740
chai.use(chaiAsPromised)
3841
describe('Test Branch keystore', () => {
42+
it('Test type guard', () => {
43+
for (const keyStore of [null, undefined, 0, {}, '']) {
44+
expect(isIBranchKeyStoreNode(keyStore as any)).to.be.false
45+
}
46+
})
47+
3948
describe('Test constructor', () => {
4049
const KMS_CONFIGURATION = new SrkCompatibilityKmsConfig(KEY_ARN)
4150

@@ -56,6 +65,139 @@ describe('Test Branch keystore', () => {
5665
kmsConfiguration: KMS_CONFIGURATION,
5766
})
5867

68+
const falseyValues = [false, 0, -0, 0n, '', null, undefined, NaN]
69+
const truthyValues = [[3, [], true], {}, 1, true, 'string']
70+
71+
it('Precondition: DDB table name must be a string', () => {
72+
// all types of values except strings
73+
const badVals = [...falseyValues, ...truthyValues].filter(
74+
(v) => typeof v !== 'string'
75+
)
76+
77+
for (const ddbTableName of badVals) {
78+
expect(
79+
() =>
80+
new BranchKeyStoreNode({
81+
ddbTableName: ddbTableName as any,
82+
logicalKeyStoreName: LOGICAL_KEYSTORE_NAME,
83+
kmsConfiguration: KMS_CONFIGURATION,
84+
})
85+
).to.throw('DDB table name must be a string')
86+
}
87+
})
88+
89+
it('Precondition: Logical keystore name must be a string', () => {
90+
// all types of values except strings
91+
const badVals = [...falseyValues, ...truthyValues].filter(
92+
(v) => typeof v !== 'string'
93+
)
94+
95+
for (const logicalKeyStoreName of badVals) {
96+
expect(
97+
() =>
98+
new BranchKeyStoreNode({
99+
ddbTableName: DDB_TABLE_NAME,
100+
logicalKeyStoreName: logicalKeyStoreName as any,
101+
kmsConfiguration: KMS_CONFIGURATION,
102+
})
103+
).to.throw('Logical keystore name must be a string')
104+
}
105+
})
106+
107+
it('Precondition: KMS Configuration must be SRK', () => {
108+
// all types of values
109+
const badVals = [...falseyValues, ...truthyValues]
110+
111+
for (const kmsConfiguration of badVals) {
112+
expect(
113+
() =>
114+
new BranchKeyStoreNode({
115+
ddbTableName: DDB_TABLE_NAME,
116+
logicalKeyStoreName: LOGICAL_KEYSTORE_NAME,
117+
kmsConfiguration: kmsConfiguration as any,
118+
})
119+
).to.throw('KMS Configuration must be SRK')
120+
}
121+
})
122+
123+
it('Precondition: KMS client must be a KMSClient', () => {
124+
// only truthy values because KMS client may be falsey
125+
for (const kmsClient of truthyValues) {
126+
expect(
127+
() =>
128+
new BranchKeyStoreNode({
129+
ddbTableName: DDB_TABLE_NAME,
130+
logicalKeyStoreName: LOGICAL_KEYSTORE_NAME,
131+
kmsConfiguration: KMS_CONFIGURATION,
132+
kmsClient: kmsClient as any,
133+
})
134+
).to.throw('KMS client must be a KMSClient')
135+
}
136+
})
137+
138+
it('Precondition: DDB client must be a DynamoDBClient', () => {
139+
// only truthy values because DDB client may be falsey
140+
for (const ddbClient of truthyValues) {
141+
expect(
142+
() =>
143+
new BranchKeyStoreNode({
144+
ddbTableName: DDB_TABLE_NAME,
145+
logicalKeyStoreName: LOGICAL_KEYSTORE_NAME,
146+
kmsConfiguration: KMS_CONFIGURATION,
147+
ddbClient: ddbClient as any,
148+
})
149+
).to.throw('DDB client must be a DynamoDBClient')
150+
}
151+
})
152+
153+
it('Precondition: Keystore id must be a string', () => {
154+
// only truthy values that are not strings because keystore id may be
155+
// falsey
156+
const badVals = truthyValues.filter((v) => typeof v !== 'string')
157+
for (const keyStoreId of badVals) {
158+
expect(
159+
() =>
160+
new BranchKeyStoreNode({
161+
ddbTableName: DDB_TABLE_NAME,
162+
logicalKeyStoreName: LOGICAL_KEYSTORE_NAME,
163+
kmsConfiguration: KMS_CONFIGURATION,
164+
keyStoreId: keyStoreId as any,
165+
})
166+
).to.throw('Keystore id must be a string')
167+
}
168+
})
169+
170+
it('Precondition: Grant tokens must be a string array', () => {
171+
// use only truthy values because grantTokens may be falsey
172+
for (const grantTokens of truthyValues) {
173+
expect(
174+
() =>
175+
new BranchKeyStoreNode({
176+
ddbTableName: DDB_TABLE_NAME,
177+
logicalKeyStoreName: LOGICAL_KEYSTORE_NAME,
178+
kmsConfiguration: KMS_CONFIGURATION,
179+
grantTokens: grantTokens as any,
180+
})
181+
).to.throw('Grant tokens must be a string array')
182+
}
183+
})
184+
185+
//= aws-encryption-sdk-specification/framework/branch-key-store.md#aws-kms-grant-tokens
186+
//= type=test
187+
//# A list of AWS KMS [grant tokens](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token).
188+
it('Postcondition: If unprovided, the grant tokens are undefined', () => {
189+
for (const grantTokens of falseyValues) {
190+
expect(
191+
new BranchKeyStoreNode({
192+
ddbTableName: DDB_TABLE_NAME,
193+
logicalKeyStoreName: LOGICAL_KEYSTORE_NAME,
194+
kmsConfiguration: KMS_CONFIGURATION,
195+
grantTokens: grantTokens as any,
196+
}).grantTokens
197+
).to.equal(undefined)
198+
}
199+
})
200+
59201
it('Invalid KmsKeyArn config', () => {
60202
const kmsClient = new KMSClient({})
61203
const ddbClient = new DynamoDBClient({})
@@ -151,18 +293,17 @@ describe('Test Branch keystore', () => {
151293
//= type=test
152294
//# The Identifier for this KeyStore.
153295
//# If one is not supplied, then a [version 4 UUID](https://www.ietf.org/rfc/rfc4122.txt) MUST be used.
154-
it('Keystore ID not provided', () => {
155-
expect(
156-
validate(BRANCH_KEYSTORE.keyStoreId) &&
157-
version(BRANCH_KEYSTORE.keyStoreId) === 4
158-
).equals(true)
159-
})
296+
it('Postcondition: If unprovided, the keystore id is a generated valid uuidv4', () => {
297+
for (const keyStoreId of falseyValues) {
298+
const { keyStoreId: id } = new BranchKeyStoreNode({
299+
ddbTableName: DDB_TABLE_NAME,
300+
logicalKeyStoreName: LOGICAL_KEYSTORE_NAME,
301+
kmsConfiguration: KMS_CONFIGURATION,
302+
keyStoreId: keyStoreId as any,
303+
})
160304

161-
//= aws-encryption-sdk-specification/framework/branch-key-store.md#aws-kms-grant-tokens
162-
//= type=test
163-
//# A list of AWS KMS [grant tokens](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token).
164-
it('Grant tokens not provided', () => {
165-
expect(BRANCH_KEYSTORE.grantTokens).deep.equals([])
305+
expect(validate(id) && version(id) === 4).equals(true)
306+
}
166307
})
167308

168309
//= aws-encryption-sdk-specification/framework/branch-key-store.md#dynamodb-client
@@ -180,12 +321,19 @@ describe('Test Branch keystore', () => {
180321
//# and no DynamoDb Client is provided,
181322
//# a new DynamoDb Client MUST be created
182323
//# with the region configured in the MRDiscovery.
183-
describe('DDB Client not provided', () => {
184-
it('KMS config is not discovery', async () => {
185-
expect(await BRANCH_KEYSTORE.ddbClient.config.region()).to.equal(
324+
it('Postcondition: If unprovided, the DDB client is configured', async () => {
325+
for (const ddbClient of falseyValues) {
326+
const { ddbClient: client } = new BranchKeyStoreNode({
327+
ddbTableName: DDB_TABLE_NAME,
328+
logicalKeyStoreName: LOGICAL_KEYSTORE_NAME,
329+
kmsConfiguration: KMS_CONFIGURATION,
330+
ddbClient: ddbClient as any,
331+
})
332+
333+
expect(await client.config.region()).to.equal(
186334
getRegionFromIdentifier(KEY_ARN)
187335
)
188-
})
336+
}
189337
})
190338

191339
//= aws-encryption-sdk-specification/framework/branch-key-store.md#kms-client
@@ -206,12 +354,19 @@ describe('Test Branch keystore', () => {
206354
//# On initialization the KeyStore SHOULD
207355
//# append a user agent string to the AWS KMS SDK Client with
208356
//# the value `aws-kms-hierarchy`.
209-
describe('KMS Client not provided', () => {
210-
it('KMS config is not discovery', async () => {
211-
expect(await BRANCH_KEYSTORE.kmsClient.config.region()).to.equal(
357+
it('Postcondition: If unprovided, the KMS client is configured', async () => {
358+
for (const kmsClient of falseyValues) {
359+
const { kmsClient: client } = new BranchKeyStoreNode({
360+
ddbTableName: DDB_TABLE_NAME,
361+
logicalKeyStoreName: LOGICAL_KEYSTORE_NAME,
362+
kmsConfiguration: KMS_CONFIGURATION,
363+
kmsClient: kmsClient as any,
364+
})
365+
366+
expect(await client.config.region()).to.equal(
212367
getRegionFromIdentifier(KEY_ARN)
213368
)
214-
})
369+
}
215370
})
216371

217372
//= aws-encryption-sdk-specification/framework/branch-key-store.md#table-name
@@ -249,10 +404,6 @@ describe('Test Branch keystore', () => {
249404
})
250405

251406
describe('Test proper init', () => {
252-
it('Grant tokens are immutable', () => {
253-
expect(Object.isFrozen(BRANCH_KEYSTORE.grantTokens)).equals(true)
254-
})
255-
256407
it('KMS Configuration is immutable', () => {
257408
expect(Object.isFrozen(BRANCH_KEYSTORE.kmsConfiguration)).equals(true)
258409
})
@@ -310,9 +461,17 @@ describe('Test Branch keystore', () => {
310461
//= type=test
311462
//# On invocation, the caller:
312463
//# - MUST supply a `branch-key-id`
313-
void (await expect(keyStore.getActiveBranchKey('')).to.be.rejectedWith(
314-
'MUST supply a branch key id'
315-
))
464+
await expect(keyStore.getActiveBranchKey('')).to.be.rejectedWith(
465+
'MUST supply a string branch key id'
466+
)
467+
468+
// test type checks
469+
await expect(
470+
keyStore.getActiveBranchKey(undefined as any)
471+
).to.be.rejectedWith('MUST supply a string branch key id')
472+
await expect(keyStore.getActiveBranchKey(null as any)).to.be.rejectedWith(
473+
'MUST supply a string branch key id'
474+
)
316475

317476
const branchKeyMaterials = await keyStore.getActiveBranchKey(BRANCH_KEY_ID)
318477
expect(branchKeyMaterials.branchKeyIdentifier).equals(BRANCH_KEY_ID)
@@ -340,12 +499,26 @@ describe('Test Branch keystore', () => {
340499
//# On invocation, the caller:
341500
//# - MUST supply a `branch-key-id`
342501
//# - MUST supply a `branchKeyVersion`
343-
void (await expect(
502+
await expect(
344503
keyStore.getBranchKeyVersion('', BRANCH_KEY_ACTIVE_VERSION)
345-
).to.be.rejectedWith('MUST supply a branch key id'))
346-
void (await expect(
504+
).to.be.rejectedWith('MUST supply a string branch key id')
505+
await expect(
347506
keyStore.getBranchKeyVersion(BRANCH_KEY_ID, '')
348-
).to.be.rejectedWith('MUST supply a branch key id'))
507+
).to.be.rejectedWith('MUST supply a string branch key version')
508+
509+
// test type checks
510+
await expect(
511+
keyStore.getBranchKeyVersion(undefined as any, BRANCH_KEY_ACTIVE_VERSION)
512+
).to.be.rejectedWith('MUST supply a string branch key id')
513+
await expect(
514+
keyStore.getBranchKeyVersion(null as any, BRANCH_KEY_ACTIVE_VERSION)
515+
).to.be.rejectedWith('MUST supply a string branch key id')
516+
await expect(
517+
keyStore.getBranchKeyVersion(BRANCH_KEY_ID, undefined as any)
518+
).to.be.rejectedWith('MUST supply a string branch key version')
519+
await expect(
520+
keyStore.getBranchKeyVersion(BRANCH_KEY_ID, null as any)
521+
).to.be.rejectedWith('MUST supply a string branch key version')
349522

350523
const branchKeyMaterials = await keyStore.getBranchKeyVersion(
351524
BRANCH_KEY_ID,

‎modules/branch-keystore-node/test/branch_keystore_helpers.test.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,6 @@ describe('Test keystore helpers', () => {
528528
KeyId: configArn,
529529
CiphertextBlob: activeBranchKeyRecord[BRANCH_KEY_FIELD],
530530
EncryptionContext: activeAuthEc,
531-
GrantTokens: BRANCH_KEYSTORE.grantTokens.slice(),
532531
})
533532
)
534533
const expectedActiveBranchKey = Buffer.from(
@@ -550,7 +549,6 @@ describe('Test keystore helpers', () => {
550549
KeyId: configArn,
551550
CiphertextBlob: VERSION_BRANCH_KEY[BRANCH_KEY_FIELD],
552551
EncryptionContext: VERSION_BRANCH_AUTHENTICATED_ENCRYPTION_CONTEXT,
553-
GrantTokens: BRANCH_KEYSTORE.grantTokens.slice(),
554552
})
555553
)
556554
const expectedVersionedBranchKey = Buffer.from(
@@ -648,7 +646,6 @@ describe('Test keystore helpers', () => {
648646
KeyId: configArn,
649647
CiphertextBlob: activeBranchKeyRecord[BRANCH_KEY_FIELD],
650648
EncryptionContext: activeAuthEc,
651-
GrantTokens: branchKeyStore.grantTokens.slice(),
652649
})
653650
)
654651
const expectedActiveBranchKey = Buffer.from(
@@ -660,7 +657,6 @@ describe('Test keystore helpers', () => {
660657
KeyId: configArn,
661658
CiphertextBlob: versionedBranchKeyRecord[BRANCH_KEY_FIELD],
662659
EncryptionContext: versionedAuthEc,
663-
GrantTokens: branchKeyStore.grantTokens.slice(),
664660
})
665661
)
666662
const expectedVersionedBranchKey = Buffer.from(

‎modules/branch-keystore-node/test/kms_config.test.ts

Lines changed: 80 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
RegionalKmsConfig,
88
KmsConfig,
99
KmsKeyArnConfig,
10+
isKmsConfig,
1011
} from '../src/kms_config'
1112

1213
function supplySrkKmsConfig(arn: string): KmsConfig {
@@ -27,88 +28,100 @@ export const WELL_FORMED_SRK_ALIAS_ARN =
2728
export const WELL_FORMED_MRK_ALIAS_ARN =
2829
'arn:aws:kms:us-west-2:123456789012:alias/mrk/my-mrk-alias'
2930

30-
describe('Test kms config', () => {
31-
describe('Test SrkCompatibilityKmsConfig class', () => {
32-
describe('Given a well formed SRK arn', () => {
33-
//= aws-encryption-sdk-specification/framework/branch-key-store.md#aws-kms-configuration
34-
//= type=test
35-
//# `KMS Key ARN` and `KMS MRKey ARN` MUST take an additional argument
36-
//# that is a KMS ARN.
37-
const config = supplySrkKmsConfig(WELL_FORMED_SRK_ARN)
38-
39-
it('Test getRegion', () => {
40-
expect((config as RegionalKmsConfig).getRegion()).equals('us-west-2')
41-
})
31+
describe('Test SrkCompatibilityKmsConfig class', () => {
32+
it('Precondition: ARN must be a string', () => {
33+
for (const arn of [null, undefined, 0, {}]) {
34+
expect(() => supplySrkKmsConfig(arn as any)).to.throw(
35+
'ARN must be a string'
36+
)
37+
}
38+
})
4239

43-
it('Test getArn', () => {
44-
expect((config as KmsKeyArnConfig).getArn()).equals(WELL_FORMED_SRK_ARN)
45-
})
40+
it('Type gaurd function', () => {
41+
for (const config of [null, undefined, 0, {}, '']) {
42+
expect(isKmsConfig(config as any)).to.be.false
43+
}
44+
})
4645

47-
//= aws-encryption-sdk-specification/framework/branch-key-store.md#aws-key-arn-compatibility
48-
//= type=test
49-
//# For two ARNs to be compatible:
50-
51-
//# If the [AWS KMS Configuration](#aws-kms-configuration) designates single region ARN compatibility,
52-
//# then two ARNs are compatible if they are exactly equal.
53-
describe('Test isCompatibleWithArn', () => {
54-
it('Given an equal arn', () => {
55-
expect(config.isCompatibleWithArn(WELL_FORMED_SRK_ARN)).equals(true)
56-
})
57-
58-
it('Given a non-equal arn', () => {
59-
expect(config.isCompatibleWithArn(WELL_FORMED_SRK_ALIAS_ARN)).equals(
60-
false
61-
)
62-
})
63-
})
46+
describe('Given a well formed SRK arn', () => {
47+
//= aws-encryption-sdk-specification/framework/branch-key-store.md#aws-kms-configuration
48+
//= type=test
49+
//# `KMS Key ARN` and `KMS MRKey ARN` MUST take an additional argument
50+
//# that is a KMS ARN.
51+
const config = supplySrkKmsConfig(WELL_FORMED_SRK_ARN)
52+
53+
it('Test getRegion', () => {
54+
expect((config as RegionalKmsConfig).getRegion()).equals('us-west-2')
6455
})
6556

66-
describe('Given a well formed MRK arn', () => {
67-
const config = supplySrkKmsConfig(WELL_FORMED_MRK_ARN)
57+
it('Test getArn', () => {
58+
expect((config as KmsKeyArnConfig).getArn()).equals(WELL_FORMED_SRK_ARN)
59+
})
6860

69-
it('Test getRegion', () => {
70-
expect((config as RegionalKmsConfig).getRegion()).equals('us-west-2')
71-
})
61+
//= aws-encryption-sdk-specification/framework/branch-key-store.md#aws-key-arn-compatibility
62+
//= type=test
63+
//# For two ARNs to be compatible:
7264

73-
it('Test getArn', () => {
74-
expect((config as KmsKeyArnConfig).getArn()).equals(WELL_FORMED_MRK_ARN)
65+
//# If the [AWS KMS Configuration](#aws-kms-configuration) designates single region ARN compatibility,
66+
//# then two ARNs are compatible if they are exactly equal.
67+
describe('Test isCompatibleWithArn', () => {
68+
it('Given an equal arn', () => {
69+
expect(config.isCompatibleWithArn(WELL_FORMED_SRK_ARN)).equals(true)
7570
})
7671

77-
describe('Test isCompatibleWithArn', () => {
78-
it('Given an equal arn', () => {
79-
expect(config.isCompatibleWithArn(WELL_FORMED_MRK_ARN)).equals(true)
80-
})
81-
82-
it('Given a non-equal arn', () => {
83-
expect(config.isCompatibleWithArn(WELL_FORMED_MRK_ALIAS_ARN)).equals(
84-
false
85-
)
86-
})
72+
it('Given a non-equal arn', () => {
73+
expect(config.isCompatibleWithArn(WELL_FORMED_SRK_ALIAS_ARN)).equals(
74+
false
75+
)
8776
})
8877
})
78+
})
8979

90-
//= aws-encryption-sdk-specification/framework/branch-key-store.md#aws-kms-configuration
91-
//= type=test
92-
//# This ARN MUST NOT be an Alias.
93-
//# This ARN MUST be a valid
94-
//# [AWS KMS Key ARN](./aws-kms/aws-kms-key-arn.md#a-valid-aws-kms-arn).
95-
it('Given arns that are not parseable AWS KMS arns', () => {
96-
expect(() => supplySrkKmsConfig(MALFORMED_ARN)).to.throw('Malformed arn.')
97-
expect(() => supplySrkKmsConfig(ONE_PART_ARN)).to.throw(
98-
`${ONE_PART_ARN} must be a well-formed AWS KMS non-alias resource arn`
99-
)
80+
describe('Given a well formed MRK arn', () => {
81+
const config = supplySrkKmsConfig(WELL_FORMED_MRK_ARN)
82+
83+
it('Test getRegion', () => {
84+
expect((config as RegionalKmsConfig).getRegion()).equals('us-west-2')
10085
})
10186

102-
it('Given a well formed SRK alias arn', () => {
103-
expect(() => supplySrkKmsConfig(WELL_FORMED_SRK_ALIAS_ARN)).to.throw(
104-
`${WELL_FORMED_SRK_ALIAS_ARN} must be a well-formed AWS KMS non-alias resource arn`
105-
)
87+
it('Test getArn', () => {
88+
expect((config as KmsKeyArnConfig).getArn()).equals(WELL_FORMED_MRK_ARN)
10689
})
10790

108-
it('Given a well formed MRK alias arn', () => {
109-
expect(() => supplySrkKmsConfig(WELL_FORMED_MRK_ALIAS_ARN)).to.throw(
110-
`${WELL_FORMED_MRK_ALIAS_ARN} must be a well-formed AWS KMS non-alias resource arn`
111-
)
91+
describe('Test isCompatibleWithArn', () => {
92+
it('Given an equal arn', () => {
93+
expect(config.isCompatibleWithArn(WELL_FORMED_MRK_ARN)).equals(true)
94+
})
95+
96+
it('Given a non-equal arn', () => {
97+
expect(config.isCompatibleWithArn(WELL_FORMED_MRK_ALIAS_ARN)).equals(
98+
false
99+
)
100+
})
112101
})
113102
})
103+
104+
//= aws-encryption-sdk-specification/framework/branch-key-store.md#aws-kms-configuration
105+
//= type=test
106+
//# This ARN MUST NOT be an Alias.
107+
//# This ARN MUST be a valid
108+
//# [AWS KMS Key ARN](./aws-kms/aws-kms-key-arn.md#a-valid-aws-kms-arn).
109+
it('Given arns that are not parseable AWS KMS arns', () => {
110+
expect(() => supplySrkKmsConfig(MALFORMED_ARN)).to.throw('Malformed arn.')
111+
expect(() => supplySrkKmsConfig(ONE_PART_ARN)).to.throw(
112+
`${ONE_PART_ARN} must be a well-formed AWS KMS non-alias resource arn`
113+
)
114+
})
115+
116+
it('Given a well formed SRK alias arn', () => {
117+
expect(() => supplySrkKmsConfig(WELL_FORMED_SRK_ALIAS_ARN)).to.throw(
118+
`${WELL_FORMED_SRK_ALIAS_ARN} must be a well-formed AWS KMS non-alias resource arn`
119+
)
120+
})
121+
122+
it('Given a well formed MRK alias arn', () => {
123+
expect(() => supplySrkKmsConfig(WELL_FORMED_MRK_ALIAS_ARN)).to.throw(
124+
`${WELL_FORMED_MRK_ALIAS_ARN} must be a well-formed AWS KMS non-alias resource arn`
125+
)
126+
})
114127
})

‎modules/kms-keyring/src/branch_key_id_supplier.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,14 @@ import { EncryptionContext } from '@aws-crypto/material-management'
1010
export interface BranchKeyIdSupplier {
1111
getBranchKeyId(encryptionContext: EncryptionContext): string
1212
}
13+
14+
// type guard
15+
export function isBranchKeyIdSupplier(
16+
supplier: any
17+
): supplier is BranchKeyIdSupplier {
18+
return (
19+
typeof supplier === 'object' &&
20+
supplier !== null &&
21+
typeof supplier.getBranchKeyId === 'function'
22+
)
23+
}

‎modules/kms-keyring/test/branch_key_id_supplier.test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import { EncryptionContext } from '@aws-crypto/material-management'
5-
import { BranchKeyIdSupplier } from '../src'
5+
import { BranchKeyIdSupplier, isBranchKeyIdSupplier } from '../src'
66
import { expect } from 'chai'
77

88
describe('Branch key id supplier', () => {
@@ -20,4 +20,10 @@ describe('Branch key id supplier', () => {
2020

2121
expect(new Example().getBranchKeyId({})).to.equal('')
2222
})
23+
24+
it('Type guard', () => {
25+
expect(isBranchKeyIdSupplier(undefined as any)).to.be.false
26+
expect(isBranchKeyIdSupplier(null as any)).to.be.false
27+
expect(isBranchKeyIdSupplier({} as any)).to.be.false
28+
})
2329
})

‎modules/material-management/src/cryptographic_material.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,30 @@ export class NodeBranchKeyMaterial implements BranchKeyMaterial {
151151
branchKeyVersion: string,
152152
encryptionContext: EncryptionContext
153153
) {
154-
/* Precondition: branchKey must be a 32 byte-long buffer */
155-
needs(branchKey.length === 32, 'Branch key must be 32 bytes long')
154+
/* Precondition: Branch key must be a Buffer */
155+
needs(branchKey instanceof Buffer, 'Branch key must be a Buffer')
156+
157+
/* Precondition: Branch key id must be a string */
158+
needs(
159+
typeof branchKeyIdentifier === 'string',
160+
'Branch key id must be a string'
161+
)
162+
163+
/* Precondition: Branch key version must be a string */
164+
needs(
165+
typeof branchKeyVersion === 'string',
166+
'Branch key version must be a string'
167+
)
156168

157169
/* Precondition: encryptionContext must be an object, even if it is empty */
158170
needs(
159171
encryptionContext && typeof encryptionContext === 'object',
160-
'Encryption context must be set'
172+
'Encryption context must be an object'
161173
)
162174

175+
/* Precondition: branchKey must be a 32 byte-long buffer */
176+
needs(branchKey.length === 32, 'Branch key must be 32 bytes long')
177+
163178
/* Precondition: branch key ID is required */
164179
needs(branchKeyIdentifier, 'Empty branch key ID')
165180

‎modules/material-management/test/cryptographic_material.test.ts

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,62 @@ describe('NodeBranchKeyMaterial', () => {
10031003
encryptionContext
10041004
)
10051005

1006+
it('Precondition: Branch key must be a Buffer', () => {
1007+
for (const branchKey of [null, undefined, {}, 0, '']) {
1008+
expect(
1009+
() =>
1010+
new NodeBranchKeyMaterial(
1011+
branchKey as any,
1012+
branchKeyId,
1013+
branchKeyVersion,
1014+
encryptionContext
1015+
)
1016+
).to.throw('Branch key must be a Buffer')
1017+
}
1018+
})
1019+
1020+
it('Precondition: Branch key id must be a string', () => {
1021+
for (const branchKeyId of [null, undefined, {}, 0]) {
1022+
expect(
1023+
() =>
1024+
new NodeBranchKeyMaterial(
1025+
branchKey,
1026+
branchKeyId as any,
1027+
branchKeyVersion,
1028+
encryptionContext
1029+
)
1030+
).to.throw('Branch key id must be a string')
1031+
}
1032+
})
1033+
1034+
it('Precondition: Branch key version must be a string', () => {
1035+
for (const branchKeyVersion of [null, undefined, {}, 0]) {
1036+
expect(
1037+
() =>
1038+
new NodeBranchKeyMaterial(
1039+
branchKey,
1040+
branchKeyId,
1041+
branchKeyVersion as any,
1042+
encryptionContext
1043+
)
1044+
).to.throw('Branch key version must be a string')
1045+
}
1046+
})
1047+
1048+
it('Precondition: encryptionContext must be an object, even if it is empty', () => {
1049+
for (const encryptionContext of [null, undefined, 0, '']) {
1050+
expect(
1051+
() =>
1052+
new NodeBranchKeyMaterial(
1053+
branchKey,
1054+
branchKeyId,
1055+
branchKeyVersion,
1056+
encryptionContext as any
1057+
)
1058+
).to.throw('Encryption context must be an object')
1059+
}
1060+
})
1061+
10061062
it('Precondition: branchKey must be a 32 byte-long buffer', () => {
10071063
expect(
10081064
() =>
@@ -1015,27 +1071,6 @@ describe('NodeBranchKeyMaterial', () => {
10151071
).to.throw('Branch key must be 32 bytes long')
10161072
})
10171073

1018-
it('Precondition: encryptionContext must be an object, even if it is empty', () => {
1019-
expect(
1020-
() =>
1021-
new NodeBranchKeyMaterial(
1022-
branchKey,
1023-
branchKeyId,
1024-
branchKeyVersion,
1025-
undefined as any
1026-
)
1027-
).to.throw('Encryption context must be set')
1028-
expect(
1029-
() =>
1030-
new NodeBranchKeyMaterial(
1031-
branchKey,
1032-
branchKeyId,
1033-
branchKeyVersion,
1034-
true as any
1035-
)
1036-
).to.throw('Encryption context must be set')
1037-
})
1038-
10391074
it('Precondition: branch key ID is required', () => {
10401075
expect(
10411076
() =>

0 commit comments

Comments
 (0)
Please sign in to comment.