Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 4 additions & 19 deletions .github/workflows/prod-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ jobs:
submodules: true
persist-credentials: false

- name: Setup Node.js 16
- name: Setup Node.js 22
uses: actions/setup-node@v4
with:
node-version: '16'
node-version: '22'
cache: 'npm'

- name: Install dependencies
Expand Down Expand Up @@ -97,7 +97,7 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
registry-url: 'https://registry.npmjs.org'

- name: Configure AWS Credentials for Publish
Expand All @@ -118,22 +118,7 @@ jobs:
run: npm install -g npm@latest
- run: npm ci --unsafe-perm
- run: npm run build --if-present

# Generate OTP from the 2FA secret key, waiting for next TOTP window to maximize validity
- name: Generate OTP and publish
run: |
npm install otplib@12 --no-save
OTP=$(node -e "
const { authenticator } = require('otplib');
const remaining = authenticator.timeRemaining();
setTimeout(() => {
console.log(authenticator.generate(process.env.OTP_SECRET_KEY));
}, remaining * 1000);
")
npx lerna publish from-package --yes --otp $OTP --dist-tag ${{ github.event.inputs.dist_tag }}
env:
NODE_AUTH_TOKEN: ${{ env.NPM_AWS_CRYPTO_TOOLS_CI_BOT_2FA_NPM_TOKEN }}
OTP_SECRET_KEY: ${{ env.NPM_AWS_CRYPTO_TOOLS_CI_BOT_2FA_OTP_SECRET_KEY }}
- run: npx lerna publish from-package --yes --dist-tag ${{ github.event.inputs.dist_tag }}

# Once publishing is complete, validate that the published packages are useable
validate:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/shared-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: ['18.x', '20.x', '22.x', '24.x', 'latest']
node-version: ['22.x', '24.x', 'latest']
test-type: ['node', 'browser']
# Determine test categories based on whether testing published packages or source code:
# - Testing published packages: only run vector tests (don't have build artifacts to test coverage or compliance)
Expand Down
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.d.ts
*.d.ts
/.nx/workspace-data
3 changes: 2 additions & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
}
},
"hoist": true,
"nohoist": ["typedoc"]
"nohoist": ["typedoc"],
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
103 changes: 102 additions & 1 deletion modules/branch-keystore-node/src/branch_keystore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import {
decryptBranchKey,
} from './branch_keystore_helpers'
import { KMS_CLIENT_USER_AGENT, TABLE_FIELD } from './constants'
import {
createBranchAndBeaconKeys,
versionActiveBranchKey,
} from './key_helpers'

import {
IBranchKeyStorage,
Expand Down Expand Up @@ -45,8 +49,15 @@ interface IBranchKeyStoreNode {
//= type=implication
//# - [GetKeyStoreInfo](#getkeystoreinfo)
getKeyStoreInfo(): KeyStoreInfoOutput
//= aws-encryption-sdk-specification/framework/branch-key-store.md#operations
//= type=implication
//# - [VersionKey](#versionkey)
versionKey(input: VersionKeyInput): Promise<void>
//= aws-encryption-sdk-specification/framework/branch-key-store.md#operations
//= type=implication
//# - [CreateKey](#createkey)
createKey(input?: CreateKeyInput): Promise<CreateKeyOutput>
}

//= aws-encryption-sdk-specification/framework/branch-key-store.md#getkeystoreinfo
//= type=implication
//# This MUST include:
Expand All @@ -64,6 +75,19 @@ export interface KeyStoreInfoOutput {
kmsConfiguration: KmsConfig
}

export interface VersionKeyInput {
branchKeyIdentifier: string
}

export interface CreateKeyInput {
branchKeyIdentifier?: string
encryptionContext?: { [key: string]: string }
}

export interface CreateKeyOutput {
branchKeyIdentifier: string
}

export class BranchKeyStoreNode implements IBranchKeyStoreNode {
public declare readonly logicalKeyStoreName: string
public declare readonly kmsConfiguration: Readonly<KmsKeyConfig>
Expand Down Expand Up @@ -383,6 +407,83 @@ export class BranchKeyStoreNode implements IBranchKeyStoreNode {
return branchKeyMaterials
}

//= aws-encryption-sdk-specification/framework/branch-key-store.md#createkey
//# The CreateKey caller MUST provide:
//# - An optional branch key id
//# - An optional encryption context
async createKey(input?: CreateKeyInput): Promise<CreateKeyOutput> {
//= aws-encryption-sdk-specification/framework/branch-key-store.md#createkey
//# If the Keystore's KMS Configuration is `Discovery` or `MRDiscovery`,
//# this operation MUST fail.
needs(
typeof this.kmsConfiguration._config === 'object' &&
('identifier' in this.kmsConfiguration._config ||
'mrkIdentifier' in this.kmsConfiguration._config),
'CreateKey is not supported with Discovery or MRDiscovery KMS Configuration'
)

//= aws-encryption-sdk-specification/framework/branch-key-store.md#createkey
//# If an optional branch key id is provided and no encryption context is provided
//# this operation MUST fail.
if (input?.branchKeyIdentifier) {
needs(
input.encryptionContext &&
Object.keys(input.encryptionContext).length > 0,
'If branch key identifier is provided, encryption context must also be provided'
)
}

//= aws-encryption-sdk-specification/framework/branch-key-store.md#createkey
//# If no branch key id is provided, then this operation MUST create a
//# version 4 UUID to be used as the branch key id.
const branchKeyIdentifier = input?.branchKeyIdentifier || v4()
const customEncryptionContext = input?.encryptionContext || {}

await createBranchAndBeaconKeys({
branchKeyIdentifier,
customEncryptionContext,
logicalKeyStoreName: this.logicalKeyStoreName,
kmsConfiguration: this.kmsConfiguration,
grantTokens: this.grantTokens,
kmsClient: this.kmsClient,
ddbClient: (this.storage as any).ddbClient,
ddbTableName: (this.storage as any).ddbTableName,
})

//= aws-encryption-sdk-specification/framework/branch-key-store.md#createkey
//# If writing to the keystore succeeds,
//# the operation MUST return the branch-key-id that maps to both the branch key and the beacon key.
return { branchKeyIdentifier }
}

//= aws-encryption-sdk-specification/framework/branch-key-store.md#versionkey
//# On invocation, the caller:
//# - MUST supply a `branch-key-id`
async versionKey(input: VersionKeyInput): Promise<void> {
needs(input.branchKeyIdentifier, 'MUST supply a branch-key-id')

//= aws-encryption-sdk-specification/framework/branch-key-store.md#versionkey
//# If the Keystore's KMS Configuration is `Discovery` or `MRDiscovery`,
//# this operation MUST immediately fail.
needs(
typeof this.kmsConfiguration._config === 'object' &&
('identifier' in this.kmsConfiguration._config ||
'mrkIdentifier' in this.kmsConfiguration._config),
'VersionKey is not supported with Discovery or MRDiscovery KMS Configuration'
)

await versionActiveBranchKey({
branchKeyIdentifier: input.branchKeyIdentifier,
logicalKeyStoreName: this.logicalKeyStoreName,
kmsConfiguration: this.kmsConfiguration,
grantTokens: this.grantTokens,
kmsClient: this.kmsClient,
ddbClient: (this.storage as any).ddbClient,
ddbTableName: (this.storage as any).ddbTableName,
storage: this.storage,
})
}

//= aws-encryption-sdk-specification/framework/branch-key-store.md#getkeystoreinfo
//= type=implication
//# This operation MUST return the keystore information in this keystore configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,9 @@ function constructCustomEncryptionContext(
//# for the constructed key.
for (const [key, value] of Object.entries(authenticatedEncryptionContext)) {
if (key.startsWith(CUSTOM_ENCRYPTION_CONTEXT_FIELD_PREFIX)) {
customEncryptionContext[key] = value
customEncryptionContext[
key.slice(CUSTOM_ENCRYPTION_CONTEXT_FIELD_PREFIX.length)
] = value
}
}

Expand Down
Loading
Loading