Skip to content

Commit 5097bd7

Browse files
committed
fix: Raw AES keyring handling of MasterKey
There are two sublte things going on here. First, in the case of a mixed backend, the non-zero backend should be the native implementation. I assert that it should be slightly harder to exfiltrate from the native implementation than a JS implementation. Second, if I force the import of the master key into only half of the mixed backend, then `getSubtleFunction` will complain about a mismatch. Because the crypto key and the backend are not both mixed. To resolve this the backend for the raw AES master key is issolated here.
1 parent 68e860d commit 5097bd7

File tree

1 file changed

+22
-6
lines changed

1 file changed

+22
-6
lines changed

modules/raw-aes-keyring-browser/src/raw_aes_keyring_browser.ts

+22-6
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import {
4646
} from '@aws-crypto/raw-keyring'
4747
import { fromUtf8, toUtf8 } from '@aws-sdk/util-utf8-browser'
4848
import { randomValuesOnly } from '@aws-crypto/random-source-browser'
49-
import { getWebCryptoBackend, getZeroByteSubtle } from '@aws-crypto/web-crypto-backend'
49+
import { getWebCryptoBackend, getNonZeroByteBackend } from '@aws-crypto/web-crypto-backend'
5050
const { serializeEncryptionContext } = serializeFactory(fromUtf8)
5151
const { rawAesEncryptedDataKey } = rawAesEncryptedDataKeyFactory(toUtf8, fromUtf8)
5252
const { rawAesEncryptedParts } = rawAesEncryptedPartsFactory(fromUtf8)
@@ -134,9 +134,8 @@ export class RawAesKeyringWebCrypto extends KeyringWebCrypto {
134134
* See: raw_aes_material.ts in @aws-crypto/raw-keyring for details
135135
*/
136136
.setUnencryptedDataKey(masterKey, { keyNamespace: 'importOnly', keyName: 'importOnly', flags: KeyringTraceFlag.WRAPPING_KEY_GENERATED_DATA_KEY })
137-
return getWebCryptoBackend()
138-
.then(getZeroByteSubtle)
139-
.then(backend => _importCryptoKey(backend, material, ['encrypt', 'decrypt']))
137+
return backendForRawAesMasterKey()
138+
.then(backend => _importCryptoKey(backend.subtle, material, ['encrypt', 'decrypt']))
140139
}
141140
}
142141
immutableClass(RawAesKeyringWebCrypto)
@@ -158,7 +157,7 @@ async function aesGcmWrapKey (
158157
aad: Uint8Array,
159158
wrappingMaterial: WebCryptoRawAesMaterial
160159
): Promise<WebCryptoEncryptionMaterial> {
161-
const backend = await getWebCryptoBackend()
160+
const backend = await backendForRawAesMasterKey()
162161
const iv = await backend.randomValues(material.suite.ivLength)
163162

164163
const kdfGetSubtleEncrypt = getSubtleFunction(wrappingMaterial, backend, 'encrypt')
@@ -194,7 +193,7 @@ async function aesGcmUnwrapKey (
194193
const { suite } = material
195194
const { iv, ciphertext, authTag } = rawAesEncryptedParts(suite, keyName, edk)
196195

197-
const backend = await getWebCryptoBackend()
196+
const backend = await backendForRawAesMasterKey()
198197

199198
const KdfGetSubtleDecrypt = getSubtleFunction(wrappingMaterial, backend, 'decrypt')
200199
const info = new Uint8Array()
@@ -203,3 +202,20 @@ async function aesGcmUnwrapKey (
203202
material.setUnencryptedDataKey(new Uint8Array(buffer), trace)
204203
return importForWebCryptoDecryptionMaterial(material)
205204
}
205+
206+
/**
207+
* The master key can not be zero length.
208+
* If the back end is mixed,
209+
* to support both zero and non-zero byte AES-GCM operations,
210+
* then the `NonZeroByteBackend` should be the native implementation.
211+
* I assert that it should be slightly harder to exfiltrate
212+
* from the native implementation than a JS implementation.
213+
* So I *force* the master key to be stored in the native implementation **only**.
214+
*/
215+
async function backendForRawAesMasterKey () {
216+
const backend = await getWebCryptoBackend()
217+
const { randomValues } = backend
218+
const subtle = getNonZeroByteBackend(backend)
219+
220+
return { randomValues, subtle }
221+
}

0 commit comments

Comments
 (0)