Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
/* Slicing an ArrayBuffer in a browser is suboptimal.
* It makes a copy.s
* So I just make a new view for the length of the frame.
*/
const framePlaintext = new Uint8Array(
plaintext.buffer,
(sequenceNumber - 1) * frameLength,
isFinalFrame ? finalFrameLength : frameLength
)
const cipherBufferAndAuthTag = await getSubtleEncrypt(frameIv, messageAdditionalData)(framePlaintext)
bodyContent.push(frameHeader, cipherBufferAndAuthTag)
}
const result = concatBuffers(
header,
headerAuthIv,
headerAuthTag,
...bodyContent
)
dispose()
if (typeof subtleSign === 'function') {
const signatureArrayBuffer = await subtleSign(result)
const derSignature = raw2der(new Uint8Array(signatureArrayBuffer), material.suite)
const signatureInfo = serializeSignatureInfo(derSignature)
return { result: concatBuffers(result, signatureInfo), messageHeader }
} else {
return { result: result, messageHeader }
}
keyNamespace: string,
keyName: string,
material: NodeDecryptionMaterial,
wrappingMaterial: NodeRawAesMaterial,
edk: EncryptedDataKey,
aad: Buffer
): NodeDecryptionMaterial {
const { authTag, ciphertext, iv } = rawAesEncryptedParts(material.suite, keyName, edk)
const { encryption } = wrappingMaterial.suite
// createDecipheriv is incorrectly typed in @types/node. It should take key: CipherKey, not key: BinaryLike
const decipher = createDecipheriv(encryption, wrappingMaterial.getUnencryptedDataKey() as any, iv)
.setAAD(aad)
.setAuthTag(authTag)
// Buffer.concat will use the shared buffer space, and the resultant buffer will have a byteOffset...
const unencryptedDataKey = concatBuffers(decipher.update(ciphertext), decipher.final())
const trace = { keyNamespace, keyName, flags: decryptFlags }
return material.setUnencryptedDataKey(unencryptedDataKey, trace)
}
sequenceNumber += 1
const { clearBlob, frameInfo } = await framedDecrypt({ buffer, getSubtleDecrypt, headerInfo, readPos })
/* Precondition: The sequenceNumber is required to monotonically increase, starting from 1.
* This is to avoid a bad actor from abusing the sequence number on un-signed algorithm suites.
* If the frame size matched the data format (say NDJSON),
* then the data could be significantly altered just by rearranging the frames.
* Non-framed data returns a sequenceNumber of 1.
*/
needs(frameInfo.sequenceNumber === sequenceNumber, 'Encrypted body sequence out of order.')
clearBuffers.push(clearBlob)
readPos = frameInfo.readPos
if (frameInfo.isFinalFrame) {
const plaintext = concatBuffers(...clearBuffers)
return { plaintext, readPos }
}
}
}
keyNamespace: string,
keyName: string,
material: NodeEncryptionMaterial,
aad: Buffer,
wrappingMaterial: NodeRawAesMaterial
): NodeEncryptionMaterial {
const { encryption, ivLength } = wrappingMaterial.suite
const iv = randomBytes(ivLength)
const wrappingDataKey = wrappingMaterial.getUnencryptedDataKey()
const dataKey = unwrapDataKey(material.getUnencryptedDataKey())
const cipher = createCipheriv(encryption, wrappingDataKey, iv)
.setAAD(aad)
// Buffer.concat will use the shared buffer space, and the resultant buffer will have a byteOffset...
const ciphertext = concatBuffers(cipher.update(dataKey), cipher.final())
const authTag = cipher.getAuthTag()
const edk = rawAesEncryptedDataKey(
keyNamespace,
keyName,
iv,
ciphertext,
authTag
)
return material.addEncryptedDataKey(edk, encryptFlags)
}
concatBuffers
} from '@aws-crypto/serialize'
import {
_onEncrypt,
_onDecrypt,
WebCryptoRawAesMaterial,
rawAesEncryptedDataKeyFactory,
rawAesEncryptedPartsFactory,
WrappingSuiteIdentifier, // eslint-disable-line no-unused-vars
WrapKey, // eslint-disable-line no-unused-vars
UnwrapKey // eslint-disable-line no-unused-vars
} from '@aws-crypto/raw-keyring'
import { fromUtf8, toUtf8 } from '@aws-sdk/util-utf8-browser'
import { randomValuesOnly } from '@aws-crypto/random-source-browser'
import { getWebCryptoBackend, getZeroByteSubtle } from '@aws-crypto/web-crypto-backend'
const { serializeEncryptionContext } = serializeFactory(fromUtf8)
const { rawAesEncryptedDataKey } = rawAesEncryptedDataKeyFactory(toUtf8, fromUtf8)
const { rawAesEncryptedParts } = rawAesEncryptedPartsFactory(fromUtf8)
const encryptFlags = KeyringTraceFlag.WRAPPING_KEY_ENCRYPTED_DATA_KEY | KeyringTraceFlag.WRAPPING_KEY_SIGNED_ENC_CTX
const decryptFlags = KeyringTraceFlag.WRAPPING_KEY_DECRYPTED_DATA_KEY | KeyringTraceFlag.WRAPPING_KEY_VERIFIED_ENC_CTX
export type RawAesKeyringWebCryptoInput = {
keyNamespace: string
keyName: string
masterKey: CryptoKey,
wrappingSuite: WrappingSuiteIdentifier
}
export class RawAesKeyringWebCrypto extends KeyringWebCrypto {
public keyNamespace!: string
public keyName!: string
_wrapKey!: WrapKey
import Duplexify from 'duplexify'
import { randomBytes } from 'crypto'
import {
MessageHeader, // eslint-disable-line no-unused-vars
serializeFactory, kdfInfo, ContentType, SerializationVersion, ObjectType,
FRAME_LENGTH,
MESSAGE_ID_LENGTH,
Maximum
} from '@aws-crypto/serialize'
// @ts-ignore
import { pipeline } from 'readable-stream'
import { Duplex } from 'stream' // eslint-disable-line no-unused-vars
const fromUtf8 = (input: string) => Buffer.from(input, 'utf8')
const { serializeMessageHeader, headerAuthIv } = serializeFactory(fromUtf8)
export interface EncryptStreamInput {
suiteId?: AlgorithmSuiteIdentifier
encryptionContext?: EncryptionContext
frameLength?: number
plaintextLength?: number
}
/**
* Takes a NodeDefaultCryptographicMaterialsManager or a KeyringNode that will
* be wrapped in a NodeDefaultCryptographicMaterialsManager and returns a stream.
*
* @param cmm NodeMaterialsManager|KeyringNode
* @param op EncryptStreamInput
*/
export function encryptStream (
kdfInfo,
concatBuffers,
MessageHeader, // eslint-disable-line no-unused-vars
SerializationVersion,
ObjectType,
ContentType,
serializeSignatureInfo,
FRAME_LENGTH,
MESSAGE_ID_LENGTH,
raw2der,
Maximum
} from '@aws-crypto/serialize'
import { fromUtf8 } from '@aws-sdk/util-utf8-browser'
import { getWebCryptoBackend } from '@aws-crypto/web-crypto-backend'
const serialize = serializeFactory(fromUtf8)
const { messageAADContentString, messageAAD } = aadFactory(fromUtf8)
export interface EncryptInput {
suiteId?: AlgorithmSuiteIdentifier
encryptionContext?: EncryptionContext
frameLength?: number
// plaintextLength?: number // Subtle Crypto functions are all one-shot, so frames and length are === plaintext.byteLength
}
export interface EncryptResult {
messageHeader: MessageHeader
result: Uint8Array
}
export async function encrypt (
cmm: KeyringWebCrypto|WebCryptoMaterialsManager,
} from '@aws-crypto/serialize'
import {
_onEncrypt,
_onDecrypt,
NodeRawAesMaterial,
rawAesEncryptedDataKeyFactory,
rawAesEncryptedPartsFactory,
WrappingSuiteIdentifier, // eslint-disable-line no-unused-vars
WrapKey, // eslint-disable-line no-unused-vars
UnwrapKey // eslint-disable-line no-unused-vars
} from '@aws-crypto/raw-keyring'
const fromUtf8 = (input: string) => Buffer.from(input, 'utf8')
const toUtf8 = (input: Uint8Array) => Buffer
.from(input.buffer, input.byteOffset, input.byteLength)
.toString('utf8')
const { serializeEncryptionContext } = serializeFactory(fromUtf8)
const { rawAesEncryptedDataKey } = rawAesEncryptedDataKeyFactory(toUtf8, fromUtf8)
const { rawAesEncryptedParts } = rawAesEncryptedPartsFactory(fromUtf8)
export type RawAesKeyringNodeInput = {
keyNamespace: string
keyName: string
unencryptedMasterKey: Uint8Array,
wrappingSuite: WrappingSuiteIdentifier
}
export class RawAesKeyringNode extends KeyringNode {
public keyNamespace!: string
public keyName!: string
_wrapKey!: WrapKey
_unwrapKey!: UnwrapKey
export async function encrypt (
cmm: KeyringWebCrypto|WebCryptoMaterialsManager,
plaintext: Uint8Array,
{ suiteId, encryptionContext = {}, frameLength = FRAME_LENGTH }: EncryptInput = {}
): Promise {
/* Precondition: The frameLength must be less than the maximum frame size for browser encryption. */
needs(frameLength > 0 && Maximum.FRAME_SIZE >= frameLength, `frameLength out of bounds: 0 > frameLength >= ${Maximum.FRAME_SIZE}`)
const backend = await getWebCryptoBackend()
if (!backend) throw new Error('No supported crypto backend')
/* If the cmm is a Keyring, wrap it with WebCryptoDefaultCryptographicMaterialsManager. */
cmm = cmm instanceof KeyringWebCrypto
? new WebCryptoDefaultCryptographicMaterialsManager(cmm)
: cmm
// Subtle Crypto functions are all one-shot so all the plaintext needs to be available.
const plaintextLength = plaintext.byteLength
const suite = suiteId ? new WebCryptoAlgorithmSuite(suiteId) : new WebCryptoAlgorithmSuite(AlgorithmSuiteIdentifier.ALG_AES256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384)
const encryptionRequest: WebCryptoEncryptionRequest = {
suite,
encryptionContext,
export function encryptStream (
cmm: KeyringNode|NodeMaterialsManager,
op: EncryptStreamInput = {}
): Duplex {
const { suiteId, encryptionContext = {}, frameLength = FRAME_LENGTH, plaintextLength } = op
/* Precondition: The frameLength must be less than the maximum frame size Node.js stream. */
needs(frameLength > 0 && Maximum.FRAME_SIZE >= frameLength, `frameLength out of bounds: 0 > frameLength >= ${Maximum.FRAME_SIZE}`)
/* If the cmm is a Keyring, wrap it with NodeDefaultCryptographicMaterialsManager. */
cmm = cmm instanceof KeyringNode
? new NodeDefaultCryptographicMaterialsManager(cmm)
: cmm
const suite = suiteId && new NodeAlgorithmSuite(suiteId)
const wrappingStream = new Duplexify()
cmm.getEncryptionMaterials({ suite, encryptionContext, plaintextLength })
.then(async (material) => {
const { dispose, getSigner } = getEncryptHelper(material)
const { getCipher, messageHeader, rawHeader } = getEncryptionInfo(material, frameLength)