import { NodeDefaultCryptographicMaterialsManager , NodeAlgorithmSuite , AlgorithmSuiteIdentifier , KeyringNode , NodeEncryptionMaterial , getEncryptHelper , EncryptionContext , NodeMaterialsManager , needs , CommitmentPolicy , CommitmentPolicySuites , MessageFormat , ClientOptions , } from '@aws-crypto/material-management-node' ; import { getFramedEncryptStream } from './framed_encrypt_stream' ; import { SignatureStream } from './signature_stream' ; import Duplexify from 'duplexify' ; import { randomBytes } from 'crypto' ; import { serializeFactory , FRAME_LENGTH , Maximum , MessageIdLength , serializeMessageHeaderAuth , } from '@aws-crypto/serialize' ; import { pipeline } from 'readable-stream' ; import { Duplex } from 'stream' ; const fromUtf8 = ( input ) => Buffer . from ( input , 'utf8' ) ; const { serializeMessageHeader , headerAuthIv , buildMessageHeader } = serializeFactory ( fromUtf8 ) ; export interface EncryptStreamInput { suiteId ? : AlgorithmSuiteIdentifier ; encryptionContext ? : EncryptionContext ; frameLength ? : number ; plaintextLength ? : number ; } param commitmentPolicy param maxEncryptedDataKeys param cmm param op export function _encryptStream ( { commitmentPolicy , maxEncryptedDataKeys } , cmm , op = { } ) { needs ( CommitmentPolicy [ commitmentPolicy ] , 'Invalid commitment policy.' ) ; needs ( maxEncryptedDataKeys === false || maxEncryptedDataKeys >= 1 , 'Invalid maxEncryptedDataKeys value.' ) ; const { suiteId , encryptionContext = { } , frameLength = FRAME_LENGTH , plaintextLength , } = op ; needs ( frameLength > 0 && Maximum . FRAME_SIZE >= frameLength , `frameLength out of bounds: 0 > frameLength >= ${ Maximum . FRAME_SIZE }` ) ; cmm = cmm instanceof KeyringNode ? new NodeDefaultCryptographicMaterialsManager ( cmm ) : cmm ; const suite = suiteId && new NodeAlgorithmSuite ( suiteId ) ; CommitmentPolicySuites . isEncryptEnabled ( commitmentPolicy , suite ) ; const wrappingStream = new Duplexify ( ) ; cmm . getEncryptionMaterials ( { suite , encryptionContext , plaintextLength , commitmentPolicy , } ) . then ( async ( material ) => { CommitmentPolicySuites . isEncryptEnabled ( commitmentPolicy , material . suite ) ; needs ( maxEncryptedDataKeys === false || material . encryptedDataKeys . length <= maxEncryptedDataKeys , 'maxEncryptedDataKeys exceeded.' ) ; const { getCipher , messageHeader , rawHeader , dispose , getSigner } = getEncryptionInfo ( material , frameLength ) ; wrappingStream . emit ( 'MessageHeader' , messageHeader ) ; const encryptStream = getFramedEncryptStream ( getCipher , messageHeader , dispose , { plaintextLength , suite : material . suite } ) ; const signatureStream = new SignatureStream ( getSigner ) ; pipeline ( encryptStream , signatureStream ) ; wrappingStream . setReadable ( signatureStream ) ; rawHeader . forEach ( ( buff ) => signatureStream . write ( buff ) ) ; wrappingStream . setWritable ( encryptStream ) ; } ) . catch ( ( err ) => wrappingStream . emit ( 'error' , err ) ) ; return wrappingStream ; } export function getEncryptionInfo ( material , frameLength ) { const { getCipherInfo , dispose , getSigner } = getEncryptHelper ( material ) ; const { suite , encryptionContext , encryptedDataKeys } = material ; const { ivLength , messageFormat } = material . suite ; const versionString = MessageFormat [ messageFormat ] as any ; const messageIdLength = parseInt ( MessageIdLength [ versionString ] , 10 ) ; needs ( messageIdLength > 0 , 'Algorithm suite has unknown message format.' ) ; const messageId = randomBytes ( messageIdLength ) ; const { getCipher , keyCommitment } = getCipherInfo ( messageId ) ; const messageHeader = buildMessageHeader ( { suite : suite , encryptedDataKeys , encryptionContext , messageId , frameLength , suiteData : keyCommitment , } ) ; const { buffer , byteOffset , byteLength } = serializeMessageHeader ( messageHeader ) ; const headerBuffer = Buffer . from ( buffer , byteOffset , byteLength ) ; const headerIv = headerAuthIv ( ivLength ) ; const validateHeader = getCipher ( headerIv ) ; validateHeader . setAAD ( headerBuffer ) ; validateHeader . update ( Buffer . alloc ( 0 ) ) ; validateHeader . final ( ) ; const headerAuthTag = validateHeader . getAuthTag ( ) ; return { getCipher , dispose , getSigner , messageHeader , rawHeader : [ headerBuffer , serializeMessageHeaderAuth ( { headerIv , headerAuthTag , messageHeader } ) , ] , } ; }