Skip to content

Commit 656c257

Browse files
committed
fix: add serializationOptions flag for AAD UTF8 sorting
1 parent c5ce506 commit 656c257

File tree

19 files changed

+147
-71
lines changed

19 files changed

+147
-71
lines changed

modules/cache-material/src/build_cryptographic_materials_cache_key_helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function buildCryptographicMaterialsCacheKeyHelpers<
2222
sha512: (...data: (Uint8Array | string)[]) => Promise<Uint8Array>
2323
): CryptographicMaterialsCacheKeyHelpersInterface<S> {
2424
const { serializeEncryptionContext, serializeEncryptedDataKey } =
25-
serializeFactory(fromUtf8)
25+
serializeFactory(fromUtf8, {utf8Sorting: false})
2626

2727
return {
2828
buildEncryptionMaterialCacheKey,
@@ -80,7 +80,7 @@ export function buildCryptographicMaterialsCacheKeyHelpers<
8080
* However, the RAW Keyring wants _only_ the ADD.
8181
* So, I just slice off the length.
8282
*/
83-
const serializedContext = serializeEncryptionContext(context).slice(2)
83+
const serializedContext = serializeEncryptionContext(context, {utf8Sorting: false}).slice(2)
8484
return sha512(serializedContext)
8585
}
8686
}

modules/decrypt-browser/src/decrypt_client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function buildDecrypt(
2222
} {
2323
const {
2424
commitmentPolicy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT,
25-
maxEncryptedDataKeys = false,
25+
maxEncryptedDataKeys = false
2626
} = typeof options === 'string' ? { commitmentPolicy: options } : options
2727

2828
/* Precondition: browser buildDecrypt needs a valid commitmentPolicy. */
@@ -35,7 +35,7 @@ export function buildDecrypt(
3535

3636
const clientOptions: ClientOptions = {
3737
commitmentPolicy,
38-
maxEncryptedDataKeys,
38+
maxEncryptedDataKeys
3939
}
4040
return {
4141
decrypt: _decrypt.bind({}, clientOptions),

modules/decrypt-browser/test/decrypt.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe('decrypt', () => {
4646
it('Precondition: _decrypt needs a valid commitmentPolicy.', async () => {
4747
await expect(
4848
_decrypt(
49-
{ commitmentPolicy: 'fake_policy' as any, maxEncryptedDataKeys: false },
49+
{ commitmentPolicy: 'fake_policy' as any, maxEncryptedDataKeys: false},
5050
{} as any,
5151
{} as any
5252
)

modules/decrypt-node/src/decrypt_client.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export function buildDecrypt(
3131
const {
3232
commitmentPolicy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT,
3333
maxEncryptedDataKeys = false,
34+
utf8Sorting = false
3435
} = typeof options === 'string' ? { commitmentPolicy: options } : options
3536

3637
/* Precondition: node buildDecrypt needs a valid commitmentPolicy. */
@@ -44,6 +45,7 @@ export function buildDecrypt(
4445
const clientOptions: ClientOptions = {
4546
commitmentPolicy,
4647
maxEncryptedDataKeys,
48+
utf8Sorting
4749
}
4850
return {
4951
decryptUnsignedMessageStream: _decryptStream.bind(

modules/encrypt-browser/src/encrypt.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ 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)
3534
const { messageAADContentString, messageAAD } = aadFactory(fromUtf8)
3635

3736
export interface EncryptInput {
@@ -47,7 +46,7 @@ export interface EncryptResult {
4746
}
4847

4948
export async function _encrypt(
50-
{ commitmentPolicy, maxEncryptedDataKeys }: ClientOptions,
49+
{ commitmentPolicy, maxEncryptedDataKeys, utf8Sorting }: ClientOptions,
5150
cmm: KeyringWebCrypto | WebCryptoMaterialsManager,
5251
plaintext: Uint8Array,
5352
{
@@ -122,6 +121,10 @@ export async function _encrypt(
122121

123122
const { getSubtleEncrypt, keyCommitment } = await getEncryptInfo(messageId)
124123

124+
const maybeUtf8Sorting = utf8Sorting ?? false;
125+
126+
const serialize = serializeFactory(fromUtf8, {utf8Sorting: maybeUtf8Sorting})
127+
125128
const messageHeader = serialize.buildMessageHeader({
126129
suite: material.suite,
127130
encryptedDataKeys: material.encryptedDataKeys,

modules/encrypt-browser/src/encrypt_client.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export function buildEncrypt(
2323
const {
2424
commitmentPolicy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT,
2525
maxEncryptedDataKeys = false,
26+
utf8Sorting = false
2627
} = typeof options === 'string' ? { commitmentPolicy: options } : options
2728

2829
/* Precondition: browser buildEncrypt needs a valid commitmentPolicy. */
@@ -36,6 +37,7 @@ export function buildEncrypt(
3637
const clientOptions: ClientOptions = {
3738
commitmentPolicy,
3839
maxEncryptedDataKeys,
40+
utf8Sorting
3941
}
4042
return {
4143
encrypt: _encrypt.bind({}, clientOptions),

modules/encrypt-node/src/encrypt_client.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export function buildEncrypt(
2727
const {
2828
commitmentPolicy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT,
2929
maxEncryptedDataKeys = false,
30+
utf8Sorting = false
3031
} = typeof options === 'string' ? { commitmentPolicy: options } : options
3132

3233
/* Precondition: node buildEncrypt needs a valid commitmentPolicy. */
@@ -40,6 +41,7 @@ export function buildEncrypt(
4041
const clientOptions: ClientOptions = {
4142
commitmentPolicy,
4243
maxEncryptedDataKeys,
44+
utf8Sorting
4345
}
4446
return {
4547
encryptStream: _encryptStream.bind({}, clientOptions),

modules/encrypt-node/src/encrypt_stream.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ import { pipeline } from 'readable-stream'
3333
import { Duplex } from 'stream'
3434

3535
const fromUtf8 = (input: string) => Buffer.from(input, 'utf8')
36-
const { serializeMessageHeader, headerAuthIv, buildMessageHeader } =
37-
serializeFactory(fromUtf8)
3836

3937
export interface EncryptStreamInput {
4038
suiteId?: AlgorithmSuiteIdentifier
@@ -53,7 +51,7 @@ export interface EncryptStreamInput {
5351
* @param op EncryptStreamInput
5452
*/
5553
export function _encryptStream(
56-
{ commitmentPolicy, maxEncryptedDataKeys }: ClientOptions,
54+
{ commitmentPolicy, maxEncryptedDataKeys, utf8Sorting }: ClientOptions,
5755
cmm: KeyringNode | NodeMaterialsManager,
5856
op: EncryptStreamInput = {}
5957
): Duplex {
@@ -111,15 +109,17 @@ export function _encryptStream(
111109
'maxEncryptedDataKeys exceeded.'
112110
)
113111

112+
const maybeUtf8Sorting = utf8Sorting ?? false;
114113
const { getCipher, messageHeader, rawHeader, dispose, getSigner } =
115-
getEncryptionInfo(material, frameLength)
114+
getEncryptionInfo(material, frameLength, maybeUtf8Sorting)
116115

117116
wrappingStream.emit('MessageHeader', messageHeader)
118117

119118
const encryptStream = getFramedEncryptStream(
120119
getCipher,
121120
messageHeader,
122121
dispose,
122+
maybeUtf8Sorting,
123123
{ plaintextLength, suite: material.suite }
124124
)
125125
const signatureStream = new SignatureStream(getSigner)
@@ -140,9 +140,12 @@ export function _encryptStream(
140140

141141
export function getEncryptionInfo(
142142
material: NodeEncryptionMaterial,
143-
frameLength: number
143+
frameLength: number,
144+
utf8Sorting: boolean
144145
) {
145146
const { getCipherInfo, dispose, getSigner } = getEncryptHelper(material)
147+
const { serializeMessageHeader, headerAuthIv, buildMessageHeader } =
148+
serializeFactory(fromUtf8, {utf8Sorting})
146149
const { suite, encryptionContext, encryptedDataKeys } = material
147150
const { ivLength, messageFormat } = material.suite
148151

modules/encrypt-node/src/framed_encrypt_stream.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ import {
1818
} from '@aws-crypto/material-management-node'
1919

2020
const fromUtf8 = (input: string) => Buffer.from(input, 'utf8')
21-
const serialize = serializeFactory(fromUtf8)
22-
const { finalFrameHeader, frameHeader } = serialize
2321
const aadUtility = aadFactory(fromUtf8)
2422

2523
interface AccumulatingFrame {
@@ -47,6 +45,7 @@ export function getFramedEncryptStream(
4745
getCipher: GetCipher,
4846
messageHeader: MessageHeader,
4947
dispose: () => void,
48+
utf8Sorting: boolean,
5049
{
5150
plaintextLength,
5251
suite,
@@ -107,6 +106,7 @@ export function getFramedEncryptStream(
107106
getCipher,
108107
isFinalFrame: false,
109108
suite,
109+
utf8Sorting
110110
})
111111

112112
// Reset frame state for next frame
@@ -129,6 +129,7 @@ export function getFramedEncryptStream(
129129
getCipher,
130130
isFinalFrame: true,
131131
suite,
132+
utf8Sorting
132133
})
133134

134135
this._flushEncryptFrame(encryptFrame)
@@ -205,10 +206,11 @@ type EncryptFrameInput = {
205206
getCipher: GetCipher
206207
isFinalFrame: boolean
207208
suite: NodeAlgorithmSuite
209+
utf8Sorting: boolean
208210
}
209211

210212
export function getEncryptFrame(input: EncryptFrameInput): EncryptFrame {
211-
const { pendingFrame, messageHeader, getCipher, isFinalFrame, suite } = input
213+
const { pendingFrame, messageHeader, getCipher, isFinalFrame, suite, utf8Sorting } = input
212214
const { sequenceNumber, contentLength, content } = pendingFrame
213215
const { frameLength, contentType, messageId } = messageHeader
214216
/* Precondition: The content length MUST correlate with the frameLength.
@@ -226,6 +228,9 @@ export function getEncryptFrame(input: EncryptFrameInput): EncryptFrame {
226228
isFinalFrame,
227229
})}`
228230
)
231+
const serialize = serializeFactory(fromUtf8, {utf8Sorting})
232+
const { finalFrameHeader, frameHeader } = serialize
233+
229234
const frameIv = serialize.frameIv(suite.ivLength, sequenceNumber)
230235
const bodyHeader = Buffer.from(
231236
isFinalFrame

modules/encrypt-node/test/framed_encrypt_stream.test.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('getFramedEncryptStream', () => {
2828
getCipher,
2929
{} as any,
3030
() => {},
31+
false,
3132
{} as any
3233
)
3334
expect(test._transform).is.a('function')
@@ -36,13 +37,13 @@ describe('getFramedEncryptStream', () => {
3637
it('Precondition: plaintextLength must be within bounds.', () => {
3738
const getCipher: any = () => {}
3839
expect(() =>
39-
getFramedEncryptStream(getCipher, {} as any, () => {}, {
40+
getFramedEncryptStream(getCipher, {} as any, () => {}, false, {
4041
plaintextLength: -1,
4142
suite,
4243
})
4344
).to.throw(Error, 'plaintextLength out of bounds.')
4445
expect(() =>
45-
getFramedEncryptStream(getCipher, {} as any, () => {}, {
46+
getFramedEncryptStream(getCipher, {} as any, () => {}, false, {
4647
plaintextLength: Number.MAX_SAFE_INTEGER + 1,
4748
suite,
4849
})
@@ -52,7 +53,7 @@ describe('getFramedEncryptStream', () => {
5253
* I want to make sure that I don't have an errant off by 1 error.
5354
*/
5455
expect(() =>
55-
getFramedEncryptStream(getCipher, {} as any, () => {}, {
56+
getFramedEncryptStream(getCipher, {} as any, () => {}, false, {
5657
plaintextLength: Number.MAX_SAFE_INTEGER,
5758
suite,
5859
})
@@ -61,7 +62,7 @@ describe('getFramedEncryptStream', () => {
6162

6263
it('Precondition: Must not process more than plaintextLength.', () => {
6364
const getCipher: any = () => {}
64-
const test = getFramedEncryptStream(getCipher, {} as any, () => {}, {
65+
const test = getFramedEncryptStream(getCipher, {} as any, () => {}, false, {
6566
plaintextLength: 8,
6667
suite,
6768
})
@@ -78,6 +79,7 @@ describe('getFramedEncryptStream', () => {
7879
getCipher,
7980
{ frameLength } as any,
8081
() => {},
82+
false,
8183
{} as any
8284
)
8385

@@ -112,6 +114,7 @@ describe('getEncryptFrame', () => {
112114
encryptedDataKeys: [],
113115
},
114116
suite,
117+
utf8Sorting: false,
115118
}
116119
const test1 = getEncryptFrame(input)
117120
expect(test1.content).to.equal(input.pendingFrame.content)
@@ -146,6 +149,7 @@ describe('getEncryptFrame', () => {
146149
encryptedDataKeys: [],
147150
},
148151
suite,
152+
utf8Sorting: false
149153
}
150154

151155
expect(() => getEncryptFrame(inputFinalFrameToLarge)).to.throw(
@@ -172,6 +176,7 @@ describe('getEncryptFrame', () => {
172176
encryptedDataKeys: [],
173177
},
174178
suite,
179+
utf8Sorting: false
175180
}
176181

177182
// Make sure that it must be equal as long as we are here...

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
PROVIDER_ID_HIERARCHY_AS_BYTES,
3434
} from './constants'
3535
import { BranchKeyIdSupplier } from '@aws-crypto/kms-keyring'
36-
import { serializeFactory, uuidv4Factory } from '@aws-crypto/serialize'
36+
import { serializeFactory, SerializeOptions, uuidv4Factory } from '@aws-crypto/serialize'
3737

3838
export const stringToUtf8Bytes = (input: string): Buffer =>
3939
Buffer.from(input, 'utf-8')
@@ -45,8 +45,9 @@ const hexBytesToString = (input: Uint8Array): string =>
4545
Buffer.from(input).toString('hex')
4646
export const { uuidv4ToCompressedBytes, decompressBytesToUuidv4 } =
4747
uuidv4Factory(stringToHexBytes, hexBytesToString)
48+
export const utf8Sorting: SerializeOptions = {utf8Sorting: false}
4849
export const { serializeEncryptionContext } =
49-
serializeFactory(stringToUtf8Bytes)
50+
serializeFactory(stringToUtf8Bytes, utf8Sorting)
5051

5152
export function getBranchKeyId(
5253
{ branchKeyId, branchKeyIdSupplier }: IKmsHierarchicalKeyRingNode,
@@ -372,7 +373,7 @@ export function wrapAad(
372373
* So, I just slice off the length.
373374
*/
374375
const aad = Buffer.from(
375-
serializeEncryptionContext(encryptionContext).slice(2)
376+
serializeEncryptionContext(encryptionContext, utf8Sorting).slice(2)
376377
)
377378

378379
return Buffer.concat([

modules/kms-keyring-node/test/kms_hkeyring_node.helpers.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
wrapAad,
77
destructureCiphertext,
88
serializeEncryptionContext,
9+
utf8Sorting,
910
unwrapEncryptedDataKey,
1011
wrapPlaintextDataKey,
1112
} from '../src/kms_hkeyring_node_helpers'
@@ -156,7 +157,7 @@ describe('KmsHierarchicalKeyRingNode: helpers', () => {
156157
).to.deep.equal(branchKeyVersionAsBytes)
157158

158159
startIdx += branchKeyVersionAsBytes.length
159-
const expectedAad = serializeEncryptionContext(encryptionContext).slice(2)
160+
const expectedAad = serializeEncryptionContext(encryptionContext, utf8Sorting).slice(2)
160161
expect(wrappedAad.subarray(startIdx)).to.deep.equal(expectedAad)
161162
})
162163
})

modules/material-management/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ export type AwsEsdkCreateSecretKey = (key: Uint8Array) => AwsEsdkKeyObject
122122
export interface ClientOptions {
123123
commitmentPolicy: CommitmentPolicy
124124
maxEncryptedDataKeys: number | false
125+
utf8Sorting?: boolean | false
125126
}
126127

127128
export type Newable<T> = { new (...args: any[]): T }

0 commit comments

Comments
 (0)