"use strict";
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
Object.defineProperty(exports, "__esModule", { value: true });
exports.deserializeHeaderV2Factory = void 0;
/*
 * This public interface for parsing the AWS Encryption SDK Message Header Format
 * is provided for the use of the Encryption SDK for JavaScript only.  It can be used
 * as a reference but is not intended to be use by any packages other than the
 * Encryption SDK for JavaScript.
 *
 * See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html#header-structure
 */
const material_management_1 = require("@aws-crypto/material-management");
const identifiers_1 = require("./identifiers");
// To deal with Browser and Node.js I inject a function to handle utf8 encoding.
function deserializeHeaderV2Factory({ decodeEncryptionContext, deserializeEncryptedDataKeys, SdkSuite, }) {
    return deserializeMessageHeaderV2;
    /**
     * deserializeMessageHeaderV2
     *
     * I need to be able to parse the MessageHeader, but since the data may be streamed
     * I may not have all the data yet.  The caller is expected to maintain and append
     * to the buffer and call this function with the same readPos until the function
     * returns a HeaderInfo.
     *
     * @param messageBuffer
     * @param deserializeOptions
     * @returns HeaderInfo|undefined
     */
    function deserializeMessageHeaderV2(messageBuffer, deserializeOptions = { maxEncryptedDataKeys: false }) {
        /* Uint8Array is a view on top of the underlying ArrayBuffer.
         * This means that raw underlying memory stored in the ArrayBuffer
         * may be larger than the Uint8Array.  This is especially true of
         * the Node.js Buffer object.  The offset and length *must* be
         * passed to the DataView otherwise I will get unexpected results.
         */
        const dataView = new DataView(messageBuffer.buffer, messageBuffer.byteOffset, messageBuffer.byteLength);
        /* Check for early return (Postcondition): Not Enough Data. Need to have at least 37 bytes of data to begin parsing.
         * The first 37 bytes of the header are fixed length.  After that
         * there are 2 variable length sections.
         */
        const fixedLengthHeaderPrefix = 1 + 2 + identifiers_1.MessageIdLength.V2 + 2;
        if (dataView.byteLength < fixedLengthHeaderPrefix)
            return false; // not enough data
        let headerReadPos = 0;
        const version = dataView.getUint8(headerReadPos);
        // Move pos Uint8 bytes
        headerReadPos += 1;
        /* Precondition: version must be the required value. */
        (0, material_management_1.needs)(version === material_management_1.MessageFormat.V2, version === 65
            ? 'Malformed Header: This blob may be base64 encoded.'
            : 'Malformed Header.');
        // Read second and third bytes
        const suiteId = dataView.getUint16(headerReadPos, false); // big endian
        /* Precondition: suiteId must be a committing algorithm suite. */
        (0, material_management_1.needs)(material_management_1.CommittingAlgorithmSuiteIdentifier[suiteId], 'Unsupported algorithm suite.');
        // Move pos Uint16 bytes
        headerReadPos += 2;
        const messageId = messageBuffer.slice(headerReadPos, headerReadPos + identifiers_1.MessageIdLength.V2);
        // Move pos MessageIdLength.V2 bytes
        headerReadPos += identifiers_1.MessageIdLength.V2;
        const contextLength = dataView.getUint16(headerReadPos, false); // big endian
        // Move pos Uint16 bytes
        headerReadPos += 2;
        /* Check for early return (Postcondition): Not Enough Data. Caller must buffer all of the context before we can parse the next section.
         * This is the first variable length section.
         */
        if (fixedLengthHeaderPrefix + contextLength > dataView.byteLength)
            return false; // not enough data
        const encryptionContext = decodeEncryptionContext(messageBuffer.slice(fixedLengthHeaderPrefix, fixedLengthHeaderPrefix + contextLength));
        const dataKeyInfo = deserializeEncryptedDataKeys(messageBuffer, fixedLengthHeaderPrefix + contextLength, deserializeOptions);
        /* Check for early return (Postcondition): Not Enough Data. Caller must buffer all of the encrypted data keys before we can parse the next section.
         * deserializeEncryptedDataKeys will return false if it does not have enough data.
         * This is the second variable length section.
         */
        if (!dataKeyInfo)
            return false; // not enough data
        const { encryptedDataKeys, readPos } = dataKeyInfo;
        /* I'm doing this here, after decodeEncryptionContext and deserializeEncryptedDataKeys
         * because they are the bulk of the header section.
         */
        const algorithmSuite = new SdkSuite(suiteId);
        const { tagLength, suiteDataLength, ivLength } = algorithmSuite;
        /* Precondition UNTESTED: suiteId must match supported algorithm suite.
         * I'm doing this here to double up the check on suiteDataLength.
         * Ideally the types would all match up,
         * since all CommittingAlgorithmSuiteIdentifier will have `suiteDataLength`.
         * But my typescript foo is still not strong enough.
         */
        (0, material_management_1.needs)(material_management_1.CommittingAlgorithmSuiteIdentifier[suiteId] && suiteDataLength, 'Unsupported algorithm suite.');
        const tagLengthBytes = tagLength / 8;
        const headerLength = readPos + 1 + 4 + suiteDataLength;
        /* Check for early return (Postcondition): Not Enough Data. Need to have the header auth section. */
        if (headerLength + tagLengthBytes > dataView.byteLength)
            return false; // not enough data
        // update to current position
        headerReadPos = readPos;
        const contentType = dataView.getUint8(headerReadPos);
        // Move pos Uint8 bytes
        headerReadPos += 1;
        const frameLength = dataView.getUint32(headerReadPos, false); // big endian
        // Move pos Uint32 bytes
        headerReadPos += 4;
        const suiteData = messageBuffer.slice(headerReadPos, headerReadPos + suiteDataLength);
        // Move pos suiteDataLength bytes
        headerReadPos += suiteDataLength;
        const rawHeader = messageBuffer.slice(0, headerLength);
        const messageHeader = {
            version,
            suiteId,
            messageId,
            encryptionContext,
            encryptedDataKeys,
            contentType,
            frameLength,
            suiteData,
        };
        /* The V2 format is explicit about the IV. */
        const headerIv = new Uint8Array(ivLength);
        const headerAuthTag = messageBuffer.slice(headerLength, headerLength + tagLengthBytes);
        return {
            messageHeader,
            headerLength,
            rawHeader,
            algorithmSuite,
            headerAuth: {
                headerIv,
                headerAuthTag,
                headerAuthLength: headerAuthTag.byteLength,
            },
        };
    }
}
exports.deserializeHeaderV2Factory = deserializeHeaderV2Factory;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVzZXJpYWxpemVfaGVhZGVyX3YyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2Rlc2VyaWFsaXplX2hlYWRlcl92Mi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsb0VBQW9FO0FBQ3BFLHNDQUFzQzs7O0FBRXRDOzs7Ozs7O0dBT0c7QUFFSCx5RUFPd0M7QUFDeEMsK0NBQStDO0FBUS9DLGdGQUFnRjtBQUNoRixTQUFnQiwwQkFBMEIsQ0FBK0IsRUFDdkUsdUJBQXVCLEVBQ3ZCLDRCQUE0QixFQUM1QixRQUFRLEdBZ0JUO0lBQ0MsT0FBTywwQkFBMEIsQ0FBQTtJQUVqQzs7Ozs7Ozs7Ozs7T0FXRztJQUNILFNBQVMsMEJBQTBCLENBQ2pDLGFBQXlCLEVBQ3pCLHFCQUF5QyxFQUFFLG9CQUFvQixFQUFFLEtBQUssRUFBRTtRQUV4RTs7Ozs7V0FLRztRQUNILE1BQU0sUUFBUSxHQUFHLElBQUksUUFBUSxDQUMzQixhQUFhLENBQUMsTUFBTSxFQUNwQixhQUFhLENBQUMsVUFBVSxFQUN4QixhQUFhLENBQUMsVUFBVSxDQUN6QixDQUFBO1FBRUQ7OztXQUdHO1FBQ0gsTUFBTSx1QkFBdUIsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLDZCQUFlLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQTtRQUM5RCxJQUFJLFFBQVEsQ0FBQyxVQUFVLEdBQUcsdUJBQXVCO1lBQUUsT0FBTyxLQUFLLENBQUEsQ0FBQyxrQkFBa0I7UUFFbEYsSUFBSSxhQUFhLEdBQUcsQ0FBQyxDQUFBO1FBQ3JCLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDLENBQUE7UUFDaEQsdUJBQXVCO1FBQ3ZCLGFBQWEsSUFBSSxDQUFDLENBQUE7UUFDbEIsdURBQXVEO1FBQ3ZELElBQUEsMkJBQUssRUFDSCxPQUFPLEtBQUssbUNBQWEsQ0FBQyxFQUFFLEVBQzVCLE9BQU8sS0FBSyxFQUFFO1lBQ1osQ0FBQyxDQUFDLG9E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