Skip to content

Commit c07c5c5

Browse files
committed
first cut add storage
1 parent 0b1a025 commit c07c5c5

File tree

10 files changed

+762
-446
lines changed

10 files changed

+762
-446
lines changed

modules/branch-keystore-node/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
},
2020
"license": "Apache-2.0",
2121
"dependencies": {
22-
"@aws-crypto/kms-keyring": "file:.../kms-keyring",
22+
"@aws-crypto/kms-keyring": "file:../kms-keyring",
2323
"@aws-sdk/client-dynamodb": "^3.616.0",
2424
"@aws-sdk/util-dynamodb": "^3.616.0",
2525
"tslib": "^2.2.0"

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

Lines changed: 114 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -12,50 +12,15 @@ import {
1212
} from '@aws-crypto/material-management'
1313
import { v4 } from 'uuid'
1414
import {
15-
constructAuthenticatedEncryptionContext,
1615
constructBranchKeyMaterials,
1716
decryptBranchKey,
18-
getBranchKeyItem,
19-
validateBranchKeyRecord,
2017
} from './branch_keystore_helpers'
21-
import {
22-
BRANCH_KEY_ACTIVE_TYPE,
23-
BRANCH_KEY_TYPE_PREFIX,
24-
KMS_CLIENT_USER_AGENT,
25-
} from './constants'
26-
27-
//= aws-encryption-sdk-specification/framework/branch-key-store.md#initialization
28-
//# The following inputs MAY be specified to create a KeyStore:
29-
30-
//# - [ID](#keystore-id)
31-
//# - [AWS KMS Grant Tokens](#aws-kms-grant-tokens)
32-
//# - [DynamoDb Client](#dynamodb-client)
33-
//# - [KMS Client](#kms-client)
34-
35-
//# The following inputs MUST be specified to create a KeyStore:
36-
37-
//# - [Table Name](#table-name)
38-
//# - [AWS KMS Configuration](#aws-kms-configuration)
39-
//# - [Logical KeyStore Name](#logical-keystore-name)
40-
export interface BranchKeyStoreNodeInput {
41-
ddbTableName: string
42-
logicalKeyStoreName: string
43-
kmsConfiguration: KmsConfig
44-
kmsClient?: KMSClient
45-
ddbClient?: DynamoDBClient
46-
keyStoreId?: string
47-
grantTokens?: string[]
48-
}
18+
import { KMS_CLIENT_USER_AGENT } from './constants'
4919

50-
export interface IBranchKeyStoreNode {
51-
ddbTableName: string
52-
logicalKeyStoreName: string
53-
kmsConfiguration: Readonly<KmsConfig>
54-
kmsClient: KMSClient
55-
ddbClient: DynamoDBClient
56-
keyStoreId: string
57-
grantTokens?: ReadonlyArray<string>
20+
import { IBranchKeyStorage, BranchKeyStoreNodeInput } from './types'
21+
import { DynamoDBKeyStorage } from './dynamodb_key_storage'
5822

23+
interface IBranchKeyStoreNode {
5924
getActiveBranchKey(branchKeyId: string): Promise<NodeBranchKeyMaterial>
6025
getBranchKeyVersion(
6126
branchKeyId: string,
@@ -64,26 +29,23 @@ export interface IBranchKeyStoreNode {
6429
}
6530

6631
export class BranchKeyStoreNode implements IBranchKeyStoreNode {
67-
public declare ddbTableName: string
32+
// public declare ddbTableName: string
6833
public declare logicalKeyStoreName: string
6934
public declare kmsConfiguration: Readonly<KmsConfig>
7035
public declare kmsClient: KMSClient
71-
public declare ddbClient: DynamoDBClient
36+
// public declare ddbClient: DynamoDBClient
7237
public declare keyStoreId: string
7338
public declare grantTokens?: ReadonlyArray<string>
7439

40+
public declare storage: IBranchKeyStorage
41+
7542
constructor({
76-
ddbTableName,
7743
logicalKeyStoreName,
44+
storage,
45+
keyManagement,
7846
kmsConfiguration,
79-
kmsClient,
80-
ddbClient,
8147
keyStoreId,
82-
grantTokens,
8348
}: BranchKeyStoreNodeInput) {
84-
/* Precondition: DDB table name must be a string */
85-
needs(typeof ddbTableName === 'string', 'DDB table name must be a string')
86-
8749
/* Precondition: Logical keystore name must be a string */
8850
needs(
8951
typeof logicalKeyStoreName === 'string',
@@ -94,23 +56,36 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
9456
needs(isKmsConfig(kmsConfiguration), 'KMS Configuration must be SRK')
9557

9658
/* 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
59+
if (keyManagement?.kmsClient) {
60+
needs(
61+
keyManagement.kmsClient instanceof KMSClient,
62+
'KMS client must be a KMSClient'
63+
)
10264
}
10365

104-
/* Precondition: DDB client must be a DynamoDBClient */
105-
if (ddbClient) {
66+
if (
67+
'getEncryptedActiveBranchKey' in storage &&
68+
'getEncryptedBranchKeyVersion' in storage
69+
) {
70+
this.storage
71+
} else {
10672
needs(
107-
ddbClient instanceof DynamoDBClient,
73+
!storage.ddbClient ||
74+
(storage.ddbClient as any) instanceof DynamoDBClient,
10875
'DDB client must be a DynamoDBClient'
10976
)
110-
} else {
111-
// ensure it's strictly undefined and not some other falsey value
112-
ddbClient = undefined
77+
this.storage = new DynamoDBKeyStorage({
78+
ddbTableName: storage.ddbTableName,
79+
logicalKeyStoreName,
80+
ddbClient:
81+
storage.ddbClient instanceof DynamoDBClient
82+
? storage.ddbClient
83+
: new DynamoDBClient({
84+
region: (kmsConfiguration as RegionalKmsConfig).getRegion(),
85+
}),
86+
})
11387
}
88+
readOnlyProperty(this, 'storage', this.storage)
11489

11590
/* Precondition: Keystore id must be a string */
11691
if (keyStoreId) {
@@ -121,15 +96,14 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
12196
}
12297

12398
/* Precondition: Grant tokens must be a string array */
124-
if (grantTokens) {
99+
if (keyManagement?.grantTokens) {
125100
needs(
126-
Array.isArray(grantTokens) &&
127-
grantTokens.every((grantToken) => typeof grantToken === 'string'),
101+
Array.isArray(keyManagement.grantTokens) &&
102+
keyManagement.grantTokens.every(
103+
(grantToken) => typeof grantToken === 'string'
104+
),
128105
'Grant tokens must be a string array'
129106
)
130-
} else {
131-
// ensure it's strictly undefined and not some other falsey value
132-
grantTokens = undefined
133107
}
134108

135109
//= aws-encryption-sdk-specification/framework/branch-key-store.md#keystore-id
@@ -140,42 +114,16 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
140114

141115
//= aws-encryption-sdk-specification/framework/branch-key-store.md#aws-kms-grant-tokens
142116
//# A list of AWS KMS [grant tokens](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#grant_token).
143-
readOnlyProperty(this, 'grantTokens', grantTokens)
117+
readOnlyProperty(
118+
this,
119+
'grantTokens',
120+
keyManagement?.grantTokens || undefined
121+
)
144122
/* Postcondition: If unprovided, the grant tokens are undefined */
145123

146124
needs(kmsConfiguration, 'AWS KMS Configuration required')
147125
readOnlyProperty(this, 'kmsConfiguration', Object.freeze(kmsConfiguration))
148126

149-
//= aws-encryption-sdk-specification/framework/branch-key-store.md#dynamodb-client
150-
//# The DynamoDb Client used to put and get keys from the backing DDB table.
151-
152-
//# If the AWS KMS Configuration is KMS Key ARN or KMS MRKey ARN,
153-
//# and no DynamoDb Client is provided,
154-
//# a new DynamoDb Client MUST be created
155-
//# with the region of the supplied KMS ARN.
156-
157-
//# If the AWS KMS Configuration is Discovery,
158-
//# and no DynamoDb Client is provided,
159-
//# a new DynamoDb Client MUST be created
160-
//# with the default configuration.
161-
162-
//# If the AWS KMS Configuration is MRDiscovery,
163-
//# and no DynamoDb Client is provided,
164-
//# a new DynamoDb Client MUST be created
165-
//# with the region configured in the MRDiscovery.
166-
// TODO: when other KMS configuration types/classes are supported for the keystore,
167-
// verify the configuration object type to determine how we instantiate the
168-
// DDB client. This will ensure safe type casting.
169-
readOnlyProperty(
170-
this,
171-
'ddbClient',
172-
ddbClient ||
173-
new DynamoDBClient({
174-
region: (this.kmsConfiguration as RegionalKmsConfig).getRegion(),
175-
})
176-
)
177-
/* Postcondition: If unprovided, the DDB client is configured */
178-
179127
//= aws-encryption-sdk-specification/framework/branch-key-store.md#kms-client
180128
//# The KMS Client used when wrapping and unwrapping keys.
181129

@@ -203,19 +151,14 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
203151
readOnlyProperty(
204152
this,
205153
'kmsClient',
206-
kmsClient ||
154+
keyManagement?.kmsClient ||
207155
new KMSClient({
208156
region: (this.kmsConfiguration as RegionalKmsConfig).getRegion(),
209157
customUserAgent: KMS_CLIENT_USER_AGENT,
210158
})
211159
)
212160
/* Postcondition: If unprovided, the KMS client is configured */
213161

214-
//= aws-encryption-sdk-specification/framework/branch-key-store.md#table-name
215-
//# The table name of the DynamoDb table that backs this Keystore.
216-
needs(ddbTableName, 'DynamoDb table name required')
217-
readOnlyProperty(this, 'ddbTableName', ddbTableName)
218-
219162
//= aws-encryption-sdk-specification/framework/branch-key-store.md#logical-keystore-name
220163
//# This name is cryptographically bound to all data stored in this table,
221164
//# and logically separates data between different tables.
@@ -234,38 +177,38 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
234177
Object.freeze(this)
235178
}
236179

237-
/**
238-
* This is a utility method that encapsulates the overlapping logic for getActiveBranchKey
239-
* and getBranchKeyVersion to retreive the branch key materials
240-
* @param branchKeyId
241-
* @param type - this could indicate an active or versioned request
242-
* @returns branch key materials
243-
*/
244-
private async _getBranchKeyMaterials(
245-
branchKeyId: string,
246-
type: string
247-
): Promise<NodeBranchKeyMaterial> {
248-
// get the ddb response item using the partition & sort keys
249-
const ddbBranchKeyItem = await getBranchKeyItem(this, branchKeyId, type)
250-
// validate and form the branch key record
251-
const ddbBranchKeyRecord = validateBranchKeyRecord(ddbBranchKeyItem)
252-
// construct an encryption context from the record
253-
const authenticatedEncryptionContext =
254-
constructAuthenticatedEncryptionContext(this, ddbBranchKeyRecord)
255-
// decrypt the encrypted branch key
256-
const branchKey = await decryptBranchKey(
257-
this,
258-
ddbBranchKeyRecord,
259-
authenticatedEncryptionContext
260-
)
261-
// construct branch key materials from the authenticated encryption context
262-
const branchKeyMaterials = constructBranchKeyMaterials(
263-
branchKey,
264-
branchKeyId,
265-
authenticatedEncryptionContext
266-
)
267-
return branchKeyMaterials
268-
}
180+
// /**
181+
// * This is a utility method that encapsulates the overlapping logic for getActiveBranchKey
182+
// * and getBranchKeyVersion to retreive the branch key materials
183+
// * @param branchKeyId
184+
// * @param type - this could indicate an active or versioned request
185+
// * @returns branch key materials
186+
// */
187+
// private async _getBranchKeyMaterials(
188+
// branchKeyId: string,
189+
// type: string
190+
// ): Promise<NodeBranchKeyMaterial> {
191+
// // get the ddb response item using the partition & sort keys
192+
// const ddbBranchKeyItem = await getBranchKeyItem(this, branchKeyId, type)
193+
// // validate and form the branch key record
194+
// const ddbBranchKeyRecord = validateBranchKeyRecord(ddbBranchKeyItem)
195+
// // construct an encryption context from the record
196+
// const authenticatedEncryptionContext =
197+
// constructAuthenticatedEncryptionContext(this, ddbBranchKeyRecord)
198+
// // decrypt the encrypted branch key
199+
// const branchKey = await decryptBranchKey(
200+
// this,
201+
// ddbBranchKeyRecord,
202+
// authenticatedEncryptionContext
203+
// )
204+
// // construct branch key materials from the authenticated encryption context
205+
// const branchKeyMaterials = constructBranchKeyMaterials(
206+
// branchKey,
207+
// branchKeyId,
208+
// authenticatedEncryptionContext
209+
// )
210+
// return branchKeyMaterials
211+
// }
269212

270213
async getActiveBranchKey(
271214
branchKeyId: string
@@ -279,10 +222,17 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
279222
'MUST supply a string branch key id'
280223
)
281224

282-
return await this._getBranchKeyMaterials(
283-
branchKeyId,
284-
BRANCH_KEY_ACTIVE_TYPE
225+
const activeEncryptedBranchKey =
226+
await this.storage.getEncryptedActiveBranchKey(branchKeyId)
227+
228+
// decrypt the encrypted branch key
229+
const branchKey = await decryptBranchKey(this, activeEncryptedBranchKey)
230+
// construct branch key materials from the authenticated encryption context
231+
const branchKeyMaterials = constructBranchKeyMaterials(
232+
branchKey,
233+
activeEncryptedBranchKey
285234
)
235+
return branchKeyMaterials
286236
}
287237

288238
async getBranchKeyVersion(
@@ -303,10 +253,19 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
303253
'MUST supply a string branch key version'
304254
)
305255

306-
return await this._getBranchKeyMaterials(
256+
const encryptedBranchKey = await this.storage.getEncryptedBranchKeyVersion(
307257
branchKeyId,
308-
BRANCH_KEY_TYPE_PREFIX + branchKeyVersion
258+
branchKeyVersion
259+
)
260+
261+
// decrypt the encrypted branch key
262+
const branchKey = await decryptBranchKey(this, encryptedBranchKey)
263+
// construct branch key materials from the authenticated encryption context
264+
const branchKeyMaterials = constructBranchKeyMaterials(
265+
branchKey,
266+
encryptedBranchKey
309267
)
268+
return branchKeyMaterials
310269
}
311270
}
312271

@@ -318,3 +277,19 @@ export function isIBranchKeyStoreNode(
318277
): keyStore is BranchKeyStoreNode {
319278
return keyStore instanceof BranchKeyStoreNode
320279
}
280+
281+
// The JS implementation is not encumbered with the legacy construction
282+
// by passing DDB clients et al.
283+
// So it can be simplified.
284+
//= aws-encryption-sdk-specification/framework/branch-key-store.md#initialization
285+
//= type=exception
286+
//# - [AWS KMS Grant Tokens](#aws-kms-grant-tokens)
287+
288+
//= aws-encryption-sdk-specification/framework/branch-key-store.md#initialization
289+
//= type=exception
290+
//# - [DynamoDb Client](#dynamodb-client)
291+
292+
//= aws-encryption-sdk-specification/framework/branch-key-store.md#initialization
293+
//= type=exception
294+
//# - [Table Name](#table-name)
295+
//# - [KMS Client](#kms-client)

0 commit comments

Comments
 (0)