Skip to content

Commit b80cad1

Browse files
authored
fix: add serializationOptions flag for AAD UTF8 sorting (#1581)
1 parent 2577a21 commit b80cad1

28 files changed

+560
-62
lines changed

modules/branch-keystore-node/test/fixtures.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import {
1717

1818
export const DDB_TABLE_NAME = 'KeyStoreDdbTable'
1919
export const LOGICAL_KEYSTORE_NAME = DDB_TABLE_NAME
20-
export const BRANCH_KEY_ID = '75789115-1deb-4fe3-a2ec-be9e885d1945'
21-
export const BRANCH_KEY_ACTIVE_VERSION = 'fed7ad33-0774-4f97-aa5e-6c766fc8af9f'
20+
export const BRANCH_KEY_ID = '3f43a9af-08c5-4317-b694-3d3e883dcaef'
21+
export const BRANCH_KEY_ACTIVE_VERSION = 'a4905627-4b7f-4272-a847-f50dae245737'
2222
export const BRANCH_KEY_ID_WITH_EC = '4bb57643-07c1-419e-92ad-0df0df149d7c'
2323
export const BRANCH_KEY_ACTIVE_VERSION_UTF8_BYTES = Buffer.from(
2424
BRANCH_KEY_ACTIVE_VERSION,
@@ -51,7 +51,7 @@ export const ENCRYPTED_ACTIVE_BRANCH_KEY = new EncryptedHierarchicalKey(
5151
[TYPE_FIELD]: BRANCH_KEY_ACTIVE_TYPE,
5252
[BRANCH_KEY_ACTIVE_VERSION_FIELD]:
5353
`branch:version:${BRANCH_KEY_ACTIVE_VERSION}` as BranchKeyVersionType,
54-
[KEY_CREATE_TIME_FIELD]: '2023-07-12T17:34:06:000290Z',
54+
[KEY_CREATE_TIME_FIELD]: '2025-04-04T22:29:59.000549Z',
5555
[HIERARCHY_VERSION_FIELD]: '1',
5656
[KMS_FIELD]: KEY_ARN,
5757
[TABLE_FIELD]: LOGICAL_KEYSTORE_NAME,
@@ -60,7 +60,7 @@ export const ENCRYPTED_ACTIVE_BRANCH_KEY = new EncryptedHierarchicalKey(
6060
)
6161

6262
const ENCRYPTED_VERSION_BRANCH_KEY_CIPHERTEXT_BASE64 =
63-
'AQIBAHhTIzkciiF5TDB8qaCjctFmv6Dx+4yjarauOA4MtH0jwgFcb8VH4blkX0w7e59l8tl4AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM2tJUaqT5i07TTV9FAgEQgDsWBTM/N+rN+N7A1Js6TXVxbb64vt8eQ+G2LUs5yy98l11pXe78HZKnD+/YoUevUY1YDskV3ATRE+x2+g=='
63+
'AQIBAHhTIzkciiF5TDB8qaCjctFmv6Dx+4yjarauOA4MtH0jwgHZhG1KfZ/k1VQMBZzo0X+GAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMG5wDTuB2qzfR/mOKAgEQgDtbcAO39/bHj6BGaqgZTd3DSKHmpORsoaHLilWhAHryOlSjAiXK1NZxil7hOLcxjBzKE0QsMAaWJVtwag=='
6464
const ENCRYPTED_VERSION_BRANCH_KEY_CIPHERTEXT = new Uint8Array(
6565
// @ts-ignore
6666
Buffer.from(ENCRYPTED_VERSION_BRANCH_KEY_CIPHERTEXT_BASE64, 'base64')
@@ -71,7 +71,7 @@ export const ENCRYPTED_VERSION_BRANCH_KEY = new EncryptedHierarchicalKey(
7171
[BRANCH_KEY_IDENTIFIER_FIELD]: BRANCH_KEY_ID,
7272
[TYPE_FIELD]:
7373
`branch:version:${BRANCH_KEY_ACTIVE_VERSION}` as BranchKeyVersionType,
74-
[KEY_CREATE_TIME_FIELD]: '2023-07-12T17:34:06:000290Z',
74+
[KEY_CREATE_TIME_FIELD]: '2025-04-04T22:29:59.000549Z',
7575
[HIERARCHY_VERSION_FIELD]: '1',
7676
[KMS_FIELD]: KEY_ARN,
7777
[TABLE_FIELD]: LOGICAL_KEYSTORE_NAME,
@@ -84,7 +84,7 @@ export const ACTIVE_BRANCH_KEY: BranchKeyRecord = {
8484
[TYPE_FIELD]: BRANCH_KEY_ACTIVE_TYPE,
8585
[BRANCH_KEY_ACTIVE_VERSION_FIELD]:
8686
`branch:version:${BRANCH_KEY_ACTIVE_VERSION}` as BranchKeyVersionType,
87-
[KEY_CREATE_TIME_FIELD]: '2023-07-12T17:34:06:000290Z',
87+
[KEY_CREATE_TIME_FIELD]: '2025-04-04T22:29:59.000549Z',
8888
[HIERARCHY_VERSION_FIELD]: 1,
8989
[KMS_FIELD]: KEY_ARN,
9090
[BRANCH_KEY_FIELD]: ENCRYPTED_ACTIVE_BRANCH_KEY_CIPHERTEXT,
@@ -94,7 +94,7 @@ export const VERSION_BRANCH_KEY: BranchKeyRecord = {
9494
[BRANCH_KEY_IDENTIFIER_FIELD]: BRANCH_KEY_ID,
9595
[TYPE_FIELD]:
9696
`branch:version:${BRANCH_KEY_ACTIVE_VERSION}` as BranchKeyVersionType,
97-
[KEY_CREATE_TIME_FIELD]: '2023-07-12T17:34:06:000290Z',
97+
[KEY_CREATE_TIME_FIELD]: '2025-04-04T22:29:59.000549Z',
9898
[HIERARCHY_VERSION_FIELD]: 1,
9999
[KMS_FIELD]: KEY_ARN,
100100
[BRANCH_KEY_FIELD]: ENCRYPTED_VERSION_BRANCH_KEY_CIPHERTEXT,

modules/cache-material/src/build_cryptographic_materials_cache_key_helpers.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import {
88
EncryptedDataKey,
99
EncryptionContext,
1010
} from '@aws-crypto/material-management'
11-
import { serializeFactory, uInt16BE } from '@aws-crypto/serialize'
11+
import {
12+
serializeFactory,
13+
uInt16BE,
14+
SerializeOptions,
15+
} from '@aws-crypto/serialize'
1216
import { compare } from './portable_compare'
1317

1418
// 512 bits of 0 for padding between hashes in decryption materials cache ID generation.
@@ -21,8 +25,9 @@ export function buildCryptographicMaterialsCacheKeyHelpers<
2125
toUtf8: (input: Uint8Array) => string,
2226
sha512: (...data: (Uint8Array | string)[]) => Promise<Uint8Array>
2327
): CryptographicMaterialsCacheKeyHelpersInterface<S> {
28+
const sorting: SerializeOptions = { utf8Sorting: true }
2429
const { serializeEncryptionContext, serializeEncryptedDataKey } =
25-
serializeFactory(fromUtf8)
30+
serializeFactory(fromUtf8, sorting)
2631

2732
return {
2833
buildEncryptionMaterialCacheKey,

modules/decrypt-node/test/decrypt.test.ts

+44
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,50 @@ describe('decrypt', () => {
214214
})
215215
).to.rejectedWith(Error, 'maxEncryptedDataKeys exceeded.')
216216
})
217+
218+
it('will fail to decrypt ciphertext with high utf8 codepoints and no utf8 sorting on keyring', async () => {
219+
const { decrypt } = buildDecrypt({
220+
commitmentPolicy: CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT,
221+
maxEncryptedDataKeys: 3,
222+
})
223+
const hkeyring = fixtures.hKeyring(false)
224+
const ciphertext = fixtures.hierarchyMessageWithHighUtf8CodePoints()
225+
226+
await expect(
227+
decrypt(hkeyring, ciphertext, {
228+
encoding: 'base64',
229+
})
230+
).to.rejectedWith(Error, 'Unsupported state or unable to authenticate data')
231+
})
232+
233+
it('will decrypt ciphertext with high utf8 codepoints with a keyring configured to do utf8 sorting', async () => {
234+
const { decrypt } = buildDecrypt({
235+
commitmentPolicy: CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT,
236+
maxEncryptedDataKeys: 3,
237+
})
238+
const hkeyring = fixtures.hKeyring(true)
239+
const ciphertext = fixtures.hierarchyMessageWithHighUtf8CodePoints()
240+
const { plaintext } = await decrypt(hkeyring, ciphertext, {
241+
encoding: 'base64',
242+
})
243+
expect(plaintext).to.deep.equal(Buffer.from('Hello World'))
244+
})
245+
246+
it('will decrypt ciphertext with high utf8 codepoints with a multikeyring with both utf8 and no utf8 sorting', async () => {
247+
const { decrypt } = buildDecrypt({
248+
commitmentPolicy: CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT,
249+
maxEncryptedDataKeys: 3,
250+
})
251+
const hkeyringUtf8 = fixtures.hKeyring(true)
252+
const hkeyringNoUtf8 = fixtures.hKeyring(false)
253+
const multikeyring = fixtures.multiKeyring(hkeyringUtf8, hkeyringNoUtf8)
254+
255+
const ciphertext = fixtures.hierarchyMessageWithHighUtf8CodePoints()
256+
const { plaintext } = await decrypt(multikeyring, ciphertext, {
257+
encoding: 'base64',
258+
})
259+
expect(plaintext).to.deep.equal(Buffer.from('Hello World'))
260+
})
217261
})
218262

219263
function chunkCipherTextStream(ciphertext: Buffer, { size }: { size: number }) {

modules/decrypt-node/test/fixtures.ts

+36-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import {
88
NodeEncryptionMaterial,
99
KeyringNode,
1010
KeyringTraceFlag,
11+
MultiKeyringNode,
1112
} from '@aws-crypto/material-management-node'
12-
13+
import { KmsHierarchicalKeyRingNode } from '@aws-crypto/kms-keyring-node'
14+
import { BranchKeyStoreNode } from '@aws-crypto/branch-keystore-node'
1315
export function base64CiphertextAlgAes256GcmIv12Tag16HkdfSha384EcdsaP384() {
1416
return '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'
1517
}
@@ -74,6 +76,10 @@ export function fourEdksMessage() {
7476
return 'AYAAFO/VP38MeBJoKQshvIaF8TgAAAAEAAR0ZW1wABhrZXkzAAAAgAAAAAzIotUMc7SaFTbzk0EAIKgxmVzmYedtQj5od6KglliFAx7G3OBYHMgyvbGQVJSyAAR0ZW1wABhrZXkwAAAAgAAAAAwBe+86+eb8+uYOeoIAIFmT8yZvThnsJigzsRen9OJc0kuGE+rJyalk+yF5VdNBAAR0ZW1wABhrZXkyAAAAgAAAAAy939QOrzUF3XKc0m8AICSGMg1tdgULYD15Jr7RWkFgqCXjtwyUK86xqrU+OzV9AAR0ZW1wABhrZXkxAAAAgAAAAAxE6lJVWjxWLtvnkBYAIJUl4vhbLEjNS/3g3of4T/QvAR7TGPJZgv7cLqOP0T7uAgAAAAAMAAAQAAAAAAAAAAAAAAAAAOMcqPpQVjBzbYAHIPjMM1T/////AAAAAQAAAAAAAAAAAAAAAQAAAAQPcr1WkUGY1IDMmCgdibk0zwg4Yg=='
7577
}
7678

79+
export function hierarchyMessageWithHighUtf8CodePoints() {
80+
return 'AgV4PI3AdgfggyWInxz6XkfmQMbOd7/RgCN9HTTg7KeiczgAaQACABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREE2MTQvUGlNNTkyNkZwN3NWeUltVjhNZVZ4eEJNeFRpajI5Y3ozeFVZaFIwT29wbHJjT0sxNWFrR25BcENiOGkxZz09AALCtgAE8JOJqQABABFhd3Mta21zLWhpZXJhcmNoeQAkNmUxZjVlZGMtOWE3MC00YThlLWFkZmItMzdhZWVmZGVhOWU2AFx9XILjG4l3+qJ1BzpOHfZ5mf0eHmX4r3+q16U1LRGGFoVxS2mPKAqwWMUSwe2tNCe3G6kANAmRi4pyD2FK0VpuHx5FbpNgP3BR+U3cHYXiSg1vMMYEMl3usnss1QIAABAA1/mKQ+4BMR0aajtum3RQQxKfvFpi5DeCTVt0V8x7ibGHP7FCZzQWujM7M/rcfkeo/////wAAAAEAAAAAAAAAAAAAAAEAAAALxddd43JEIrllMceZonlyQGtPTX1zTsf+vwChAGcwZQIxALKuLqVHZNuvXhyjjRzs8ysgtJqvcVgvIgX1ExBKHueZLP7XsZOSUG/4SqxMFXGzuQIwOf1zLim/I65beZF1p1az1gyD+UzpWIa/vg9y0wjsYPDUEb9sUyUVU3etQ+y0LwI2'
81+
}
82+
7783
export function decryptKeyring(): KeyringNode {
7884
class TestKeyring extends KeyringNode {
7985
async _onEncrypt(): Promise<NodeEncryptionMaterial> {
@@ -95,6 +101,35 @@ export function decryptKeyring(): KeyringNode {
95101
return new TestKeyring()
96102
}
97103

104+
export function hKeyring(utf8Sorting: boolean): KeyringNode {
105+
const keyStoreTableName = 'KeyStoreDdbTable'
106+
const logicalKeyStoreName = keyStoreTableName
107+
const kmsKeyId =
108+
'arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126'
109+
const keyStore = new BranchKeyStoreNode({
110+
storage: { ddbTableName: keyStoreTableName },
111+
logicalKeyStoreName: logicalKeyStoreName,
112+
kmsConfiguration: { identifier: kmsKeyId },
113+
})
114+
const branchKeyId = '6e1f5edc-9a70-4a8e-adfb-37aeefdea9e6'
115+
return new KmsHierarchicalKeyRingNode({
116+
branchKeyId,
117+
keyStore,
118+
cacheLimitTtl: 600, // 10 min
119+
utf8Sorting,
120+
})
121+
}
122+
123+
export function multiKeyring(
124+
keyring1: KeyringNode,
125+
keyring2: KeyringNode
126+
): KeyringNode {
127+
return new MultiKeyringNode({
128+
generator: keyring1,
129+
children: [keyring2],
130+
})
131+
}
132+
98133
export interface VectorTest {
99134
ciphertext: string
100135
commitment: string

modules/encrypt-browser/src/encrypt.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
import { fromUtf8 } from '@aws-sdk/util-utf8-browser'
3232
import { getWebCryptoBackend } from '@aws-crypto/web-crypto-backend'
3333

34-
const serialize = serializeFactory(fromUtf8)
34+
const serialize = serializeFactory(fromUtf8, { utf8Sorting: true })
3535
const { messageAADContentString, messageAAD } = aadFactory(fromUtf8)
3636

3737
export interface EncryptInput {

modules/encrypt-node/src/encrypt_stream.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { Duplex } from 'stream'
3434

3535
const fromUtf8 = (input: string) => Buffer.from(input, 'utf8')
3636
const { serializeMessageHeader, headerAuthIv, buildMessageHeader } =
37-
serializeFactory(fromUtf8)
37+
serializeFactory(fromUtf8, { utf8Sorting: true })
3838

3939
export interface EncryptStreamInput {
4040
suiteId?: AlgorithmSuiteIdentifier

modules/encrypt-node/src/framed_encrypt_stream.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
} from '@aws-crypto/material-management-node'
1919

2020
const fromUtf8 = (input: string) => Buffer.from(input, 'utf8')
21-
const serialize = serializeFactory(fromUtf8)
21+
const serialize = serializeFactory(fromUtf8, { utf8Sorting: true })
2222
const { finalFrameHeader, frameHeader } = serialize
2323
const aadUtility = aadFactory(fromUtf8)
2424

modules/integration-node/src/decrypt_materials_manager_node.ts

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export function aesKeyring(keyInfo: AesKeyInfo, key: AESKey) {
8989
keyNamespace,
9090
unencryptedMasterKey,
9191
wrappingSuite,
92+
utf8Sorting: true,
9293
})
9394
}
9495

modules/integration-node/src/integration_tests.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,12 @@ import {
2222
MessageHeader,
2323
needs,
2424
DecryptOutput,
25+
getCompatibleCommitmentPolicy,
2526
} from '@aws-crypto/client-node'
2627
import { version } from './version'
2728
import { URL } from 'url'
2829
import got from 'got'
2930
import streamToPromise from 'stream-to-promise'
30-
const { encrypt, decrypt, decryptUnsignedMessageStream } = buildClient(
31-
CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT
32-
)
3331
import { ZipFile } from 'yazl'
3432
import { createWriteStream } from 'fs'
3533
import { v4 } from 'uuid'
@@ -58,6 +56,9 @@ async function runDecryption(
5856
testVectorInfo: TestVectorInfo
5957
): Promise<DecryptOutput> {
6058
const cmm = decryptMaterialsManagerNode(testVectorInfo.keysInfo)
59+
const { decrypt, decryptUnsignedMessageStream } = buildClient(
60+
CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT
61+
)
6162
if (testVectorInfo.decryptionMethod == 'streaming-unsigned-only') {
6263
const plaintext: Buffer[] = []
6364
let messageHeader: MessageHeader | false = false
@@ -147,6 +148,8 @@ export async function testEncryptVector(
147148
handleEncryptResult: HandleEncryptResult
148149
): Promise<TestVectorResult> {
149150
const { name, keysInfo, encryptOp, plainTextData } = info
151+
const commitmentPolicy = getCompatibleCommitmentPolicy(encryptOp.suiteId)
152+
const { encrypt } = buildClient(commitmentPolicy)
150153
try {
151154
const cmm = encryptMaterialsManagerNode(keysInfo)
152155
const { result: encryptResult } = await encrypt(

modules/kms-keyring-node/src/kms_hkeyring_node.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export interface KmsHierarchicalKeyRingNodeInput {
7474
//= type=implication
7575
//# - MAY provide a [Partition ID](#partition-id)
7676
partitionId?: string
77+
utf8Sorting?: boolean
7778
}
7879

7980
export interface IKmsHierarchicalKeyRingNode extends KeyringNode {
@@ -104,6 +105,7 @@ export class KmsHierarchicalKeyRingNode
104105
public declare maxCacheSize?: number
105106
public declare _cmc: CryptographicMaterialsCache<NodeAlgorithmSuite>
106107
declare readonly _partition: Buffer
108+
public declare _utf8Sorting: boolean
107109

108110
constructor({
109111
branchKeyId,
@@ -113,6 +115,7 @@ export class KmsHierarchicalKeyRingNode
113115
cache,
114116
maxCacheSize,
115117
partitionId,
118+
utf8Sorting,
116119
}: KmsHierarchicalKeyRingNodeInput) {
117120
super()
118121

@@ -256,6 +259,12 @@ export class KmsHierarchicalKeyRingNode
256259
readOnlyProperty(this, 'maxCacheSize', maxCacheSize)
257260
readOnlyProperty(this, '_cmc', cache)
258261

262+
if (utf8Sorting === undefined) {
263+
readOnlyProperty(this, '_utf8Sorting', false)
264+
} else {
265+
readOnlyProperty(this, '_utf8Sorting', utf8Sorting)
266+
}
267+
259268
Object.freeze(this)
260269
/* Postcondition: The HKR object must be frozen */
261270
}
@@ -299,7 +308,8 @@ export class KmsHierarchicalKeyRingNode
299308
const edk = wrapPlaintextDataKey(
300309
pdk,
301310
branchKeyMaterials,
302-
encryptionMaterial
311+
encryptionMaterial,
312+
this._utf8Sorting
303313
)
304314

305315
// return the modified encryption material with the new edk and newly
@@ -428,7 +438,8 @@ export class KmsHierarchicalKeyRingNode
428438
udk = unwrapEncryptedDataKey(
429439
ciphertext,
430440
branchKeyMaterials,
431-
decryptionMaterial
441+
decryptionMaterial,
442+
this._utf8Sorting
432443
)
433444
} catch (e) {
434445
//= aws-encryption-sdk-specification/framework/aws-kms/aws-kms-hierarchical-keyring.md#ondecrypt

modules/kms-keyring-node/src/kms_hkeyring_node_helpers.ts

+13-7
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ const hexBytesToString = (input: Uint8Array): string =>
4545
Buffer.from(input).toString('hex')
4646
export const { uuidv4ToCompressedBytes, decompressBytesToUuidv4 } =
4747
uuidv4Factory(stringToHexBytes, hexBytesToString)
48-
export const { serializeEncryptionContext } =
49-
serializeFactory(stringToUtf8Bytes)
5048

5149
export function getBranchKeyId(
5250
{ branchKeyId, branchKeyIdSupplier }: IKmsHierarchicalKeyRingNode,
@@ -290,7 +288,8 @@ export function getPlaintextDataKey(material: NodeEncryptionMaterial) {
290288
export function wrapPlaintextDataKey(
291289
pdk: Uint8Array,
292290
branchKeyMaterials: NodeBranchKeyMaterial,
293-
{ encryptionContext }: NodeEncryptionMaterial
291+
{ encryptionContext }: NodeEncryptionMaterial,
292+
utf8Sorting: boolean
294293
): Uint8Array {
295294
// get what we need from branch key material to wrap the pdk
296295
const branchKey = branchKeyMaterials.branchKey()
@@ -319,7 +318,8 @@ export function wrapPlaintextDataKey(
319318
const wrappedAad = wrapAad(
320319
branchKeyIdAsBytes,
321320
branchKeyVersionAsBytesCompressed,
322-
encryptionContext
321+
encryptionContext,
322+
utf8Sorting
323323
)
324324

325325
// encrypt the pdk into an edk
@@ -361,10 +361,14 @@ export function wrapPlaintextDataKey(
361361
export function wrapAad(
362362
branchKeyIdAsBytes: Buffer,
363363
version: Buffer,
364-
encryptionContext: EncryptionContext
364+
encryptionContext: EncryptionContext,
365+
utf8Sorting: boolean
365366
) {
366367
/* Precondition: Branch key version must be 16 bytes */
367368
needs(version.length === 16, 'Branch key version must be 16 bytes')
369+
const { serializeEncryptionContext } = serializeFactory(stringToUtf8Bytes, {
370+
utf8Sorting: utf8Sorting,
371+
})
368372

369373
/* The AAD section is uInt16BE(length) + AAD
370374
* see: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html#header-aad
@@ -528,7 +532,8 @@ export function destructureCiphertext(
528532
export function unwrapEncryptedDataKey(
529533
ciphertext: Uint8Array,
530534
branchKeyMaterials: NodeBranchKeyMaterial,
531-
{ encryptionContext, suite }: NodeDecryptionMaterial
535+
{ encryptionContext, suite }: NodeDecryptionMaterial,
536+
utf8Sorting: boolean
532537
) {
533538
// get what we need from the branch key materials to unwrap the edk
534539
const branchKey = branchKeyMaterials.branchKey()
@@ -557,7 +562,8 @@ export function unwrapEncryptedDataKey(
557562
const wrappedAad = wrapAad(
558563
branchKeyIdAsBytes,
559564
branchKeyVersionAsBytesCompressed,
560-
encryptionContext
565+
encryptionContext,
566+
utf8Sorting
561567
)
562568

563569
// decipher the edk to get the udk/pdk

modules/kms-keyring-node/test/fixtures.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import {
1010

1111
export const DDB_TABLE_NAME = 'KeyStoreDdbTable'
1212
export const LOGICAL_KEYSTORE_NAME = DDB_TABLE_NAME
13-
export const BRANCH_KEY_ID = '75789115-1deb-4fe3-a2ec-be9e885d1945'
14-
export const BRANCH_KEY_ACTIVE_VERSION = 'fed7ad33-0774-4f97-aa5e-6c766fc8af9f'
13+
export const BRANCH_KEY_ID = '3f43a9af-08c5-4317-b694-3d3e883dcaef'
14+
export const BRANCH_KEY_ACTIVE_VERSION = 'a4905627-4b7f-4272-a847-f50dae245737'
1515
export const BRANCH_KEY_ID_WITH_EC = '4bb57643-07c1-419e-92ad-0df0df149d7c'
1616

1717
export const KEY_ARN =

0 commit comments

Comments
 (0)