"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EmberTokensManager = void 0;
/* istanbul ignore file */
const debug_1 = __importDefault(require("debug"));
const initters_1 = require("../utils/initters");
const consts_1 = require("../consts");
const consts_2 = require("../ezsp/consts");
const enums_1 = require("../enums");
const enums_2 = require("../ezsp/enums");
const debug = (0, debug_1.default)('zigbee-herdsman:adapter:ember:adapter:tokens');
/* eslint-disable @typescript-eslint/no-unused-vars */
//------------------------------------------------------------------------------
// Definitions for stack tokens.
// protocol\zigbee\stack\config\token-stack.h
/**
 * Creator Codes
 *
 * The CREATOR is used as a distinct identifier tag for the token.
 *
 * The CREATOR is necessary because the token name is defined differently depending on the hardware platform.
 * Therefore, the CREATOR ensures that token definitions and data stay tagged and known.
 * The only requirement is that each creator definition must be unique.
 * See hal/micro/token.h for a more complete explanation.
 *
 */
// STACK CREATORS
const CREATOR_STACK_NVDATA_VERSION = 0xFF01;
const CREATOR_STACK_BOOT_COUNTER = 0xE263;
const CREATOR_STACK_NONCE_COUNTER = 0xE563;
const CREATOR_STACK_ANALYSIS_REBOOT = 0xE162;
const CREATOR_STACK_KEYS = 0xEB79;
const CREATOR_STACK_NODE_DATA = 0xEE64;
const CREATOR_STACK_CLASSIC_DATA = 0xE364;
const CREATOR_STACK_ALTERNATE_KEY = 0xE475;
const CREATOR_STACK_APS_FRAME_COUNTER = 0xE123;
const CREATOR_STACK_TRUST_CENTER = 0xE124;
const CREATOR_STACK_NETWORK_MANAGEMENT = 0xE125;
const CREATOR_STACK_PARENT_INFO = 0xE126;
const CREATOR_STACK_PARENT_ADDITIONAL_INFO = 0xE127;
const CREATOR_STACK_MULTI_PHY_NWK_INFO = 0xE128;
const CREATOR_STACK_MIN_RECEIVED_RSSI = 0xE129;
// Restored EUI64
const CREATOR_STACK_RESTORED_EUI64 = 0xE12A;
// MULTI-NETWORK STACK CREATORS
const CREATOR_MULTI_NETWORK_STACK_KEYS = 0xE210;
const CREATOR_MULTI_NETWORK_STACK_NODE_DATA = 0xE211;
const CREATOR_MULTI_NETWORK_STACK_ALTERNATE_KEY = 0xE212;
const CREATOR_MULTI_NETWORK_STACK_TRUST_CENTER = 0xE213;
const CREATOR_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT = 0xE214;
const CREATOR_MULTI_NETWORK_STACK_PARENT_INFO = 0xE215;
// A temporary solution for multi-network nwk counters:
// This counter will be used on the network with index 1.
const CREATOR_MULTI_NETWORK_STACK_NONCE_COUNTER = 0xE220;
const CREATOR_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO = 0xE221;
// GP stack tokens.
const CREATOR_STACK_GP_DATA = 0xE258;
const CREATOR_STACK_GP_PROXY_TABLE = 0xE259;
const CREATOR_STACK_GP_SINK_TABLE = 0xE25A;
const CREATOR_STACK_GP_INCOMING_FC = 0xE25B;
const CREATOR_STACK_GP_INCOMING_FC_IN_SINK = 0xE25C;
// APP CREATORS
const CREATOR_STACK_BINDING_TABLE = 0xE274;
const CREATOR_STACK_CHILD_TABLE = 0xFF0D;
const CREATOR_STACK_KEY_TABLE = 0xE456;
const CREATOR_STACK_CERTIFICATE_TABLE = 0xE500;
const CREATOR_STACK_ZLL_DATA = 0xE501;
const CREATOR_STACK_ZLL_SECURITY = 0xE502;
const CREATOR_STACK_ADDITIONAL_CHILD_DATA = 0xE503;
/**
 * NVM3 Object Keys
 *
 * The NVM3 object key is used as a distinct identifier tag for a token stored in NVM3.
 *
 * Every token must have a defined NVM3 object key and the object key must be unique.
 * The object key defined must be in the following format:
 *
 * NVM3KEY_tokenname where tokenname is the name of the token without NVM3KEY_ or TOKEN_ prefix.
 *
 */
// NVM3KEY domain base keys
const NVM3KEY_DOMAIN_USER = 0x00000;
const NVM3KEY_DOMAIN_ZIGBEE = 0x10000;
const NVM3KEY_DOMAIN_COMMON = 0x80000;
// STACK KEYS
const NVM3KEY_STACK_NVDATA_VERSION = (NVM3KEY_DOMAIN_ZIGBEE | 0xFF01);
const NVM3KEY_STACK_BOOT_COUNTER = (NVM3KEY_DOMAIN_ZIGBEE | 0xE263);
const NVM3KEY_STACK_NONCE_COUNTER = (NVM3KEY_DOMAIN_ZIGBEE | 0xE563);
const NVM3KEY_STACK_ANALYSIS_REBOOT = (NVM3KEY_DOMAIN_ZIGBEE | 0xE162);
const NVM3KEY_STACK_KEYS = (NVM3KEY_DOMAIN_ZIGBEE | 0xEB79);
const NVM3KEY_STACK_NODE_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0xEE64);
const NVM3KEY_STACK_CLASSIC_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0xE364);
const NVM3KEY_STACK_ALTERNATE_KEY = (NVM3KEY_DOMAIN_ZIGBEE | 0xE475);
const NVM3KEY_STACK_APS_FRAME_COUNTER = (NVM3KEY_DOMAIN_ZIGBEE | 0xE123);
const NVM3KEY_STACK_TRUST_CENTER = (NVM3KEY_DOMAIN_ZIGBEE | 0xE124);
const NVM3KEY_STACK_NETWORK_MANAGEMENT = (NVM3KEY_DOMAIN_ZIGBEE | 0xE125);
const NVM3KEY_STACK_PARENT_INFO = (NVM3KEY_DOMAIN_ZIGBEE | 0xE126);
const NVM3KEY_STACK_PARENT_ADDITIONAL_INFO = (NVM3KEY_DOMAIN_ZIGBEE | 0xE127);
const NVM3KEY_STACK_MULTI_PHY_NWK_INFO = (NVM3KEY_DOMAIN_ZIGBEE | 0xE128);
const NVM3KEY_STACK_MIN_RECEIVED_RSSI = (NVM3KEY_DOMAIN_ZIGBEE | 0xE129);
// Restored EUI64
const NVM3KEY_STACK_RESTORED_EUI64 = (NVM3KEY_DOMAIN_ZIGBEE | 0xE12A);
// MULTI-NETWORK STACK KEYS
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_MULTI_NETWORK_STACK_KEYS = (NVM3KEY_DOMAIN_ZIGBEE | 0x0000);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_MULTI_NETWORK_STACK_NODE_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0x0080);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_MULTI_NETWORK_STACK_ALTERNATE_KEY = (NVM3KEY_DOMAIN_ZIGBEE | 0x0100);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_MULTI_NETWORK_STACK_TRUST_CENTER = (NVM3KEY_DOMAIN_ZIGBEE | 0x0180);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT = (NVM3KEY_DOMAIN_ZIGBEE | 0x0200);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_MULTI_NETWORK_STACK_PARENT_INFO = (NVM3KEY_DOMAIN_ZIGBEE | 0x0280);
// Temporary solution for multi-network nwk counters:
// This counter will be used on the network with index 1.
const NVM3KEY_MULTI_NETWORK_STACK_NONCE_COUNTER = (NVM3KEY_DOMAIN_ZIGBEE | 0xE220);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved
const NVM3KEY_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO = (NVM3KEY_DOMAIN_ZIGBEE | 0x0300);
// GP stack tokens.
const NVM3KEY_STACK_GP_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0xE258);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_STACK_GP_PROXY_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0380);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_STACK_GP_SINK_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0400);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved
const NVM3KEY_STACK_GP_INCOMING_FC = (NVM3KEY_DOMAIN_ZIGBEE | 0x0480);
// APP KEYS
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_STACK_BINDING_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0500);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_STACK_CHILD_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0580);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_STACK_KEY_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0600);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_STACK_CERTIFICATE_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0680);
const NVM3KEY_STACK_ZLL_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0xE501);
const NVM3KEY_STACK_ZLL_SECURITY = (NVM3KEY_DOMAIN_ZIGBEE | 0xE502);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved.
const NVM3KEY_STACK_ADDITIONAL_CHILD_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0x0700);
// This key is used for an indexed token and the subsequent 0x7F keys are also reserved
const NVM3KEY_STACK_GP_INCOMING_FC_IN_SINK = (NVM3KEY_DOMAIN_ZIGBEE | 0x0780);
// XXX: comment out in prod, along with debug token prints
// const DEBUG_TOKEN_STRINGS = {
//     [NVM3KEY_STACK_NVDATA_VERSION]: 'NVM3KEY_STACK_NVDATA_VERSION',
//     [NVM3KEY_STACK_BOOT_COUNTER]: 'NVM3KEY_STACK_BOOT_COUNTER',
//     [NVM3KEY_STACK_NONCE_COUNTER]: 'NVM3KEY_STACK_NONCE_COUNTER',
//     [NVM3KEY_STACK_ANALYSIS_REBOOT]: 'NVM3KEY_STACK_ANALYSIS_REBOOT',
//     [NVM3KEY_STACK_KEYS]: 'NVM3KEY_STACK_KEYS',
//     [NVM3KEY_STACK_NODE_DATA]: 'NVM3KEY_STACK_NODE_DATA',
//     [NVM3KEY_STACK_CLASSIC_DATA]: 'NVM3KEY_STACK_CLASSIC_DATA',
//     [NVM3KEY_STACK_ALTERNATE_KEY]: 'NVM3KEY_STACK_ALTERNATE_KEY',
//     [NVM3KEY_STACK_APS_FRAME_COUNTER]: 'NVM3KEY_STACK_APS_FRAME_COUNTER',
//     [NVM3KEY_STACK_TRUST_CENTER]: 'NVM3KEY_STACK_TRUST_CENTER',
//     [NVM3KEY_STACK_NETWORK_MANAGEMENT]: 'NVM3KEY_STACK_NETWORK_MANAGEMENT',
//     [NVM3KEY_STACK_PARENT_INFO]: 'NVM3KEY_STACK_PARENT_INFO',
//     [NVM3KEY_STACK_PARENT_ADDITIONAL_INFO]: 'NVM3KEY_STACK_PARENT_ADDITIONAL_INFO',
//     [NVM3KEY_STACK_MULTI_PHY_NWK_INFO]: 'NVM3KEY_STACK_MULTI_PHY_NWK_INFO',
//     [NVM3KEY_STACK_MIN_RECEIVED_RSSI]: 'NVM3KEY_STACK_MIN_RECEIVED_RSSI',
//     [NVM3KEY_STACK_RESTORED_EUI64]: 'NVM3KEY_STACK_RESTORED_EUI64',
//     [NVM3KEY_MULTI_NETWORK_STACK_KEYS]: 'NVM3KEY_MULTI_NETWORK_STACK_KEYS',
//     [NVM3KEY_MULTI_NETWORK_STACK_NODE_DATA]: 'NVM3KEY_MULTI_NETWORK_STACK_NODE_DATA',
//     [NVM3KEY_MULTI_NETWORK_STACK_ALTERNATE_KEY]: 'NVM3KEY_MULTI_NETWORK_STACK_ALTERNATE_KEY',
//     [NVM3KEY_MULTI_NETWORK_STACK_TRUST_CENTER]: 'NVM3KEY_MULTI_NETWORK_STACK_TRUST_CENTER',
//     [NVM3KEY_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT]: 'NVM3KEY_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT',
//     [NVM3KEY_MULTI_NETWORK_STACK_PARENT_INFO]: 'NVM3KEY_MULTI_NETWORK_STACK_PARENT_INFO',
//     [NVM3KEY_MULTI_NETWORK_STACK_NONCE_COUNTER]: 'NVM3KEY_MULTI_NETWORK_STACK_NONCE_COUNTER',
//     [NVM3KEY_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO]: 'NVM3KEY_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO',
//     [NVM3KEY_STACK_GP_DATA]: 'NVM3KEY_STACK_GP_DATA',
//     [NVM3KEY_STACK_GP_PROXY_TABLE]: 'NVM3KEY_STACK_GP_PROXY_TABLE',
//     [NVM3KEY_STACK_GP_SINK_TABLE]: 'NVM3KEY_STACK_GP_SINK_TABLE',
//     [NVM3KEY_STACK_GP_INCOMING_FC]: 'NVM3KEY_STACK_GP_INCOMING_FC',
//     [NVM3KEY_STACK_BINDING_TABLE]: 'NVM3KEY_STACK_BINDING_TABLE',
//     [NVM3KEY_STACK_CHILD_TABLE]: 'NVM3KEY_STACK_CHILD_TABLE',
//     [NVM3KEY_STACK_KEY_TABLE]: 'NVM3KEY_STACK_KEY_TABLE',
//     [NVM3KEY_STACK_CERTIFICATE_TABLE]: 'NVM3KEY_STACK_CERTIFICATE_TABLE',
//     [NVM3KEY_STACK_ZLL_DATA]: 'NVM3KEY_STACK_ZLL_DATA',
//     [NVM3KEY_STACK_ZLL_SECURITY]: 'NVM3KEY_STACK_ZLL_SECURITY',
//     [NVM3KEY_STACK_ADDITIONAL_CHILD_DATA]: 'NVM3KEY_STACK_ADDITIONAL_CHILD_DATA',
//     [NVM3KEY_STACK_GP_INCOMING_FC_IN_SINK]: 'NVM3KEY_STACK_GP_INCOMING_FC_IN_SINK',
// };
/**
 * The current version number of the stack tokens.
 * MSB is the version, LSB is a complement.
 *
 * See hal/micro/token.h for a more complete explanation.
 */
const CURRENT_STACK_TOKEN_VERSION = 0x03FC;
/** 8-byte IEEE + 16-byte Key + 1-byte info */
const KEY_TABLE_ENTRY_SIZE = 25;
const KEY_ENTRY_IEEE_OFFSET = 0;
/** first 4 bytes may point to PSA ID if data[KEY_ENTRY_INFO_OFFSET] & KEY_TABLE_ENTRY_HAS_PSA_ID */
const KEY_ENTRY_KEY_DATA_OFFSET = 8;
const KEY_ENTRY_INFO_OFFSET = 24;
/* eslint-enable @typescript-eslint/no-unused-vars */
/** uint16_t */
const CREATORS = [
    CREATOR_STACK_NVDATA_VERSION,
    CREATOR_STACK_BOOT_COUNTER,
    CREATOR_STACK_NONCE_COUNTER,
    CREATOR_STACK_ANALYSIS_REBOOT,
    CREATOR_STACK_KEYS,
    CREATOR_STACK_NODE_DATA,
    CREATOR_STACK_CLASSIC_DATA,
    CREATOR_STACK_ALTERNATE_KEY,
    CREATOR_STACK_APS_FRAME_COUNTER,
    CREATOR_STACK_TRUST_CENTER,
    CREATOR_STACK_NETWORK_MANAGEMENT,
    CREATOR_STACK_PARENT_INFO,
    CREATOR_STACK_PARENT_ADDITIONAL_INFO,
    CREATOR_STACK_MULTI_PHY_NWK_INFO,
    CREATOR_STACK_MIN_RECEIVED_RSSI,
    CREATOR_STACK_RESTORED_EUI64,
    CREATOR_MULTI_NETWORK_STACK_KEYS,
    CREATOR_MULTI_NETWORK_STACK_NODE_DATA,
    CREATOR_MULTI_NETWORK_STACK_ALTERNATE_KEY,
    CREATOR_MULTI_NETWORK_STACK_TRUST_CENTER,
    CREATOR_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT,
    CREATOR_MULTI_NETWORK_STACK_PARENT_INFO,
    CREATOR_MULTI_NETWORK_STACK_NONCE_COUNTER,
    CREATOR_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO,
    CREATOR_STACK_GP_DATA,
    CREATOR_STACK_GP_PROXY_TABLE,
    CREATOR_STACK_GP_SINK_TABLE,
    CREATOR_STACK_GP_INCOMING_FC,
    CREATOR_STACK_GP_INCOMING_FC_IN_SINK,
    CREATOR_STACK_BINDING_TABLE,
    CREATOR_STACK_CHILD_TABLE,
    CREATOR_STACK_KEY_TABLE,
    CREATOR_STACK_CERTIFICATE_TABLE,
    CREATOR_STACK_ZLL_DATA,
    CREATOR_STACK_ZLL_SECURITY,
    CREATOR_STACK_ADDITIONAL_CHILD_DATA,
];
/** uint32_t */
const NVM3KEYS = [
    NVM3KEY_STACK_NVDATA_VERSION,
    NVM3KEY_STACK_BOOT_COUNTER,
    NVM3KEY_STACK_NONCE_COUNTER,
    NVM3KEY_STACK_ANALYSIS_REBOOT,
    NVM3KEY_STACK_KEYS,
    NVM3KEY_STACK_NODE_DATA,
    NVM3KEY_STACK_CLASSIC_DATA,
    NVM3KEY_STACK_ALTERNATE_KEY,
    NVM3KEY_STACK_APS_FRAME_COUNTER,
    NVM3KEY_STACK_TRUST_CENTER,
    NVM3KEY_STACK_NETWORK_MANAGEMENT,
    NVM3KEY_STACK_PARENT_INFO,
    NVM3KEY_STACK_PARENT_ADDITIONAL_INFO,
    NVM3KEY_STACK_MULTI_PHY_NWK_INFO,
    NVM3KEY_STACK_MIN_RECEIVED_RSSI,
    NVM3KEY_STACK_RESTORED_EUI64,
    NVM3KEY_MULTI_NETWORK_STACK_KEYS,
    NVM3KEY_MULTI_NETWORK_STACK_NODE_DATA,
    NVM3KEY_MULTI_NETWORK_STACK_ALTERNATE_KEY,
    NVM3KEY_MULTI_NETWORK_STACK_TRUST_CENTER,
    NVM3KEY_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT,
    NVM3KEY_MULTI_NETWORK_STACK_PARENT_INFO,
    NVM3KEY_MULTI_NETWORK_STACK_NONCE_COUNTER,
    NVM3KEY_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO,
    NVM3KEY_STACK_GP_DATA,
    NVM3KEY_STACK_GP_PROXY_TABLE,
    NVM3KEY_STACK_GP_SINK_TABLE,
    NVM3KEY_STACK_GP_INCOMING_FC,
    NVM3KEY_STACK_BINDING_TABLE,
    NVM3KEY_STACK_CHILD_TABLE,
    NVM3KEY_STACK_KEY_TABLE,
    NVM3KEY_STACK_CERTIFICATE_TABLE,
    NVM3KEY_STACK_ZLL_DATA,
    NVM3KEY_STACK_ZLL_SECURITY,
    NVM3KEY_STACK_ADDITIONAL_CHILD_DATA,
    NVM3KEY_STACK_GP_INCOMING_FC_IN_SINK,
];
const BLANK_EUI64_BUF = Buffer.from(consts_1.BLANK_EUI64.substring(2) /*take out 0x*/, 'hex');
class EmberTokensManager {
    /**
     * Host-only API to check whether the NCP uses key storage.
     *
     * @returns false if keys are in classic key storage, and true if they are located in PSA key storage.
     */
    static async ncpUsesPSAKeyStorage(ezsp) {
        const [status, valueLength, value] = (await ezsp.ezspGetValue(enums_2.EzspValueId.KEY_STORAGE_VERSION, 1));
        if ((status !== enums_1.EzspStatus.SUCCESS) || (valueLength < 1)) {
            throw new Error(`[TOKENS] Error retrieving key storage version, status=${enums_1.EzspStatus[status]}.`);
        }
        return (value[0] === 1);
    }
    /**
     * Matcher for Zigbeed tokens.
     * @param nvm3Key
     * @returns
     */
    static getCreatorFromNvm3Key(nvm3Key) {
        for (let i = 0; i < NVM3KEYS.length; i++) {
            if (NVM3KEYS[i] === nvm3Key) {
                return CREATORS[i];
            }
        }
        return 0xFFFF;
    }
    /**
     * Saves tokens. Only for NVM3-based NCP.
     *
     * The binary file format to save the tokens are
     *
     * Number of Tokens (1 byte)
     * Token0 (4 bytes) Token0Size(1 byte) Token0ArraySize(1 byte) Token0Data(Token0Size * Token0ArraySize)
     * :
     * :
     * TokenM (4 bytes) TokenMSize(1 byte) TokenMArraySize(1 byte) TokenMData(TokenMSize * TokenMArraySize)
     *
     * @param localEui64 Used in place of blank `restoredEui64` keys
     *
     * @return Saved tokens buffer or null.
     */
    static async saveTokens(ezsp, localEui64) {
        console.log(`[TOKENS] Saving tokens...`);
        const tokenCount = (await ezsp.ezspGetTokenCount());
        if (tokenCount) {
            const chunks = [Buffer.from([tokenCount])]; // 1 byte
            // returns 1 if NCP has secure key storage (where these tokens do not store the key data).
            // Don't compile for scripted test or any non-host code due to linker issues.
            const hasSecureStorage = (await EmberTokensManager.ncpUsesPSAKeyStorage(ezsp));
            debug(`[TOKENS] Saving ${tokenCount} tokens, ${hasSecureStorage ? "with" : "without"} secure storage.`);
            for (let i = 0; i < tokenCount; i++) {
                const [tiStatus, tokenInfo] = (await ezsp.ezspGetTokenInfo(i));
                let writeOffset = 0;
                if (tiStatus === enums_1.EmberStatus.SUCCESS) {
                    const outputToken = Buffer.alloc(4 + 1 + 1 + (tokenInfo.size * tokenInfo.arraySize));
                    outputToken.writeUInt32LE(tokenInfo.nvm3Key, writeOffset); // 4 bytes
                    writeOffset += 4;
                    outputToken.writeUInt8(tokenInfo.size, writeOffset++); // 1 byte
                    outputToken.writeUInt8(tokenInfo.arraySize, writeOffset++); // 1 byte
                    for (let arrayIndex = 0; arrayIndex < tokenInfo.arraySize; arrayIndex++) {
                        const [tdStatus, tokenData] = (await ezsp.ezspGetTokenData(tokenInfo.nvm3Key, arrayIndex));
                        if (tdStatus === enums_1.EmberStatus.SUCCESS) {
                            if (hasSecureStorage) {
                                // Populate keys into tokenData because tokens do not contain them with secure key storage
                                await EmberTokensManager.saveKeysToData(ezsp, tokenData, tokenInfo.nvm3Key, arrayIndex);
                                // ensure the token data was retrieved properly, length should match the size announced by the token info
                                console.assert(tokenData.data.length === tokenInfo.size, `[TOKENS] Mismatch in token data size; got ${tokenData.data.length}, expected ${tokenInfo.size}.`);
                            }
                            // debug(`[TOKENS] TOKEN nvm3Key=${DEBUG_TOKEN_STRINGS[tokenInfo.nvm3Key]} size=${tokenInfo.size} `
                            //     + `arraySize=${tokenInfo.arraySize} token=${tokenData.data.toString('hex')}`);
                            // Check the Key to see if the token to save is restoredEui64, in that case
                            // check if it is blank, then save the node EUI64 in its place, else save the value
                            // received from the API. Once it saves, during restore process the set token will
                            // simply write the restoredEUI64 and the node will start to use that.
                            if (tokenInfo.nvm3Key === NVM3KEY_STACK_RESTORED_EUI64 && tokenData.size === consts_2.EUI64_SIZE
                                && (tokenData.data === BLANK_EUI64_BUF)) {
                                // Special case : Save the node EUI64 on the restoredEui64 token while saving.
                                tokenData.data.set(localEui64);
                                debug(`[TOKENS] Saved node EUI64 in place of blank RESTORED EUI64.`);
                            }
                            outputToken.set(tokenData.data, writeOffset);
                            writeOffset += tokenData.size;
                        }
                        else {
                            console.error(`[TOKENS] Failed to get token data at index ${arrayIndex} with status=${enums_1.EmberStatus[tdStatus]}.`);
                        }
                    }
                    chunks.push(outputToken);
                }
                else {
                    console.error(`[TOKENS] Failed to get token info at index ${i} with status=${enums_1.EmberStatus[tiStatus]}.`);
                }
            }
            return Buffer.concat(chunks);
        }
        else {
            // ezspGetTokenCount == 0 OR (ezspGetTokenInfo|ezspGetTokenData|ezspSetTokenData return LIBRARY_NOT_PRESENT)
            // ezspTokenFactoryReset will do nothing.
            console.error(`[TOKENS] Saving tokens not supported by NCP (not NVM3-based).`);
        }
        return null;
    }
    /**
     * Restores tokens. Only for NVM3-based NCP.
     * XXX: If a previous backup from an NVM3 NCP is attempted on a non-NVM3 NCP,
     *      it should just fail (LIBRARY_NOT_PRESENT all on token-related functions).
     *
     * @see EmberTokensManager.saveTokens() for format
     *
     * @return EmberStatus status code
     */
    static async restoreTokens(ezsp, inBuffer) {
        if (!inBuffer?.length) {
            throw new Error(`[TOKENS] Restore tokens buffer empty.`);
        }
        console.log(`[TOKENS] Restoring tokens...`);
        let readOffset = 0;
        const inTokenCount = inBuffer.readUInt8(readOffset++);
        const hasSecureStorage = (await EmberTokensManager.ncpUsesPSAKeyStorage(ezsp));
        debug(`[TOKENS] Restoring ${inTokenCount} tokens, ${hasSecureStorage ? "with" : "without"} secure storage.`);
        for (let i = 0; i < inTokenCount; i++) {
            const [tiStatus, tokenInfo] = (await ezsp.ezspGetTokenInfo(i));
            if (tiStatus === enums_1.EmberStatus.SUCCESS) {
                const nvm3Key = inBuffer.readUInt32LE(readOffset); // 4 bytes Token Key/Creator
                readOffset += 4;
                const size = inBuffer.readUInt8(readOffset++); // 1 byte token size
                const arraySize = inBuffer.readUInt8(readOffset++); // 1 byte array size.
                for (let arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) {
                    const tokenData = {
                        data: inBuffer.subarray(readOffset, readOffset + size),
                        size,
                    };
                    if (hasSecureStorage) {
                        // do not keep keys in classic key storage upon restoration
                        await EmberTokensManager.restoreKeysFromData(ezsp, tokenData, tokenInfo.nvm3Key, arrayIndex);
                    }
                    const status = (await ezsp.ezspSetTokenData(nvm3Key, arrayIndex, tokenData));
                    console.assert(status === enums_1.EmberStatus.SUCCESS, `[TOKENS] Failed to set token data for key "${nvm3Key}" with status=${enums_1.EmberStatus[status]}.`);
                    readOffset += tokenData.size;
                }
            }
            else {
                console.error(`[TOKENS] Failed to get token info at index ${i} with status=${enums_1.EmberStatus[tiStatus]}.`);
            }
        }
        return enums_1.EmberStatus.SUCCESS;
    }
    /**
     * Secure key storage needs to export the keys first so backup file has them.
     *
     * @param tokenData EmberTokenData* [IN/OUT]
     * @param nvm3Key uint32_t
     * @param index uint8_t
     * @returns
     */
    static async saveKeysToData(ezsp, tokenData, nvm3Key, index) {
        let status = enums_1.SLStatus.OK;
        const context = (0, initters_1.initSecurityManagerContext)();
        let plaintextKey;
        if (nvm3Key === NVM3KEY_STACK_KEYS) {
            // typedef struct {
            //     uint8_t networkKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run)
            //     uint8_t activeKeySeqNum;
            // } tokTypeStackKeys;
            context.coreKeyType = enums_1.SecManKeyType.NETWORK;
            context.keyIndex = 0;
            [plaintextKey, status] = (await ezsp.ezspExportKey(context));
            tokenData.data.set(plaintextKey.contents, 0); // at beginning
        }
        else if (nvm3Key === NVM3KEY_STACK_ALTERNATE_KEY) {
            // typedef struct {
            //     uint8_t networkKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run)
            //     uint8_t activeKeySeqNum;
            // } tokTypeStackKeys;
            context.coreKeyType = enums_1.SecManKeyType.NETWORK;
            context.keyIndex = 1;
            [plaintextKey, status] = (await ezsp.ezspExportKey(context));
            tokenData.data.set(plaintextKey.contents, 0); // at beginning
        }
        else if (nvm3Key === NVM3KEY_STACK_TRUST_CENTER) {
            // typedef struct {
            //     uint16_t mode;
            //     uint8_t eui64[8];
            //     uint8_t key[16];  // ignored if (mode & TRUST_CENTER_KEY_LIVES_IN_PSA)
            // } tokTypeStackTrustCenter;
            context.coreKeyType = enums_1.SecManKeyType.TC_LINK;
            [plaintextKey, status] = (await ezsp.ezspExportKey(context));
            tokenData.data.set(plaintextKey.contents, 2 + consts_2.EUI64_SIZE); // uint16_t+uint8_t[8]
        }
        else if (nvm3Key === NVM3KEY_STACK_KEY_TABLE) {
            // typedef uint8_t tokTypeStackKeyTable[25];
            context.coreKeyType = enums_1.SecManKeyType.APP_LINK;
            context.keyIndex = index;
            //this must be set to export a specific link key from table
            context.flags |= enums_1.SecManFlag.KEY_INDEX_IS_VALID;
            [plaintextKey, status] = (await ezsp.ezspExportKey(context));
            tokenData.data.set(plaintextKey.contents, KEY_ENTRY_KEY_DATA_OFFSET); // end part of uint8_t[25]
        }
        else if (nvm3Key === NVM3KEY_STACK_GP_PROXY_TABLE) {
            // typedef struct {
            //     uint8_t status;
            //     uint32_t options;
            //     //EmberGpAddress gpd;
            //     uint8_t gpAddress[8];
            //     uint8_t endpoint;
            //     //uint16_t assignedAlias;
            //     uint8_t securityOptions;
            //     uint8_t gpdKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run)
            //     //EmberGpSinkListEntry sinkList[2];
            //     uint8_t sinkType[2];
            //     uint8_t sinkEUI[2][8];
            //     //uint16_t sinkNodeId[2];
            // } tokTypeStackGpProxyTableEntry;
            context.coreKeyType = enums_1.SecManKeyType.GREEN_POWER_PROXY_TABLE_KEY;
            context.keyIndex = index;
            [plaintextKey, status] = (await ezsp.ezspExportKey(context));
            tokenData.data.set(plaintextKey.contents, 1 + 4 + 8 + 1 + 1); // uint8_t+uint32_t+uint8_t[8]+uint8_t+uint8_t
        }
        else if (nvm3Key === NVM3KEY_STACK_GP_SINK_TABLE) {
            // typedef struct {
            //     uint8_t status;
            //     uint16_t options;
            //     //EmberGpAddress gpd;
            //     uint8_t gpAddress[8];
            //     uint8_t endpoint;
            //     uint8_t securityOptions;
            //     uint8_t gpdKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run)
            //     uint8_t sinkType[2];
            //     uint16_t groupList[2][2];
            //     uint32_t securityFrameCounter; // This is no more used, Incoming FC for gpd in a separate Token to control its update.
            //     uint16_t assignedAlias;
            //     uint8_t deviceId;
            //     uint8_t groupcastRadius;
            // } tokTypeStackGpSinkTableEntry;
            context.coreKeyType = enums_1.SecManKeyType.GREEN_POWER_SINK_TABLE_KEY;
            context.keyIndex = index;
            [plaintextKey, status] = (await ezsp.ezspExportKey(context));
            tokenData.data.set(plaintextKey.contents, 1 + 2 + 8 + 1 + 1); // uint8_t+uint16_t+uint8_t[8]+uint8_t+uint8_t
        }
        else if (nvm3Key === NVM3KEY_STACK_ZLL_SECURITY) {
            // typedef struct {
            //     uint32_t bitmask;
            //     uint8_t keyIndex;
            //     uint8_t encryptionKey[EMBER_ENCRYPTION_KEY_SIZE];
            //     uint8_t preconfiguredKey[EMBER_ENCRYPTION_KEY_SIZE];
            // } EmberTokTypeStackZllSecurity;
            context.coreKeyType = enums_1.SecManKeyType.ZLL_ENCRYPTION_KEY;
            [plaintextKey, status] = (await ezsp.ezspExportKey(context));
            tokenData.data.set(plaintextKey.contents, 4 + 1); // uint32_t+uint8_t
            context.coreKeyType = enums_1.SecManKeyType.ZLL_PRECONFIGURED_KEY;
            [plaintextKey, status] = (await ezsp.ezspExportKey(context));
            tokenData.data.set(plaintextKey.contents, 4 + 1 + consts_2.EMBER_ENCRYPTION_KEY_SIZE); // uint32_t+uint8_t+uint8_t[EMBER_ENCRYPTION_KEY_SIZE]
        }
        else {
            //nothing needs to be done for non-key tokens
        }
        return status;
    }
    /**
     *
     * @param data_s EmberTokenData*
     * @param nvm3Key uint32_t
     * @param index uint8_t
     * @returns
     *
     * @from sli_zigbee_af_trust_center_backup_restore_keys_from_data
     */
    static async restoreKeysFromData(ezsp, tokenData, nvm3Key, index) {
        let status = enums_1.SLStatus.OK;
        const context = (0, initters_1.initSecurityManagerContext)();
        const plaintextKey = { contents: null };
        if (nvm3Key === NVM3KEY_STACK_KEYS) {
            // typedef struct {
            //     uint8_t networkKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run)
            //     uint8_t activeKeySeqNum;
            // } tokTypeStackKeys;
            context.coreKeyType = enums_1.SecManKeyType.NETWORK;
            context.keyIndex = 0;
            plaintextKey.contents = tokenData.data.subarray(0, consts_2.EMBER_ENCRYPTION_KEY_SIZE); // at beginning
            status = await ezsp.ezspImportKey(context, plaintextKey);
        }
        else if (nvm3Key === NVM3KEY_STACK_ALTERNATE_KEY) {
            // typedef struct {
            //     uint8_t networkKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run)
            //     uint8_t activeKeySeqNum;
            // } tokTypeStackKeys;
            context.coreKeyType = enums_1.SecManKeyType.NETWORK;
            context.keyIndex = 1;
            plaintextKey.contents = tokenData.data.subarray(0, consts_2.EMBER_ENCRYPTION_KEY_SIZE); // at beginning
            status = await ezsp.ezspImportKey(context, plaintextKey);
        }
        else if (nvm3Key === NVM3KEY_STACK_TRUST_CENTER) {
            // typedef struct {
            //     uint16_t mode;
            //     uint8_t eui64[8];
            //     uint8_t key[16];  // ignored if (mode & TRUST_CENTER_KEY_LIVES_IN_PSA)
            // } tokTypeStackTrustCenter;
            context.coreKeyType = enums_1.SecManKeyType.TC_LINK;
            const s = 2 + consts_2.EUI64_SIZE;
            plaintextKey.contents = tokenData.data.subarray(s, s + consts_2.EMBER_ENCRYPTION_KEY_SIZE); // uint16_t+uint8_t[8]
            status = await ezsp.ezspImportKey(context, plaintextKey);
        }
        else if (nvm3Key === NVM3KEY_STACK_KEY_TABLE) {
            // typedef uint8_t tokTypeStackKeyTable[25];
            context.coreKeyType = enums_1.SecManKeyType.APP_LINK;
            context.keyIndex = index;
            context.flags |= enums_1.SecManFlag.KEY_INDEX_IS_VALID;
            plaintextKey.contents = tokenData.data.subarray(KEY_ENTRY_KEY_DATA_OFFSET, KEY_ENTRY_KEY_DATA_OFFSET + consts_2.EMBER_ENCRYPTION_KEY_SIZE); // end part of uint8_t[25]
            status = await ezsp.ezspImportKey(context, plaintextKey);
        }
        else if (nvm3Key === NVM3KEY_STACK_GP_PROXY_TABLE) {
            // typedef struct {
            //     uint8_t status;
            //     uint32_t options;
            //     //EmberGpAddress gpd;
            //     uint8_t gpAddress[8];
            //     uint8_t endpoint;
            //     //uint16_t assignedAlias;
            //     uint8_t securityOptions;
            //     uint8_t gpdKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run)
            //     //EmberGpSinkListEntry sinkList[2];
            //     uint8_t sinkType[2];
            //     uint8_t sinkEUI[2][8];
            //     //uint16_t sinkNodeId[2];
            // } tokTypeStackGpProxyTableEntry;
            context.coreKeyType = enums_1.SecManKeyType.GREEN_POWER_PROXY_TABLE_KEY;
            context.keyIndex = index;
            const s = 1 + 4 + 8 + 1 + 1;
            plaintextKey.contents = tokenData.data.subarray(s, s + consts_2.EMBER_ENCRYPTION_KEY_SIZE); // uint8_t+uint32_t+uint8_t[8]+uint8_t+uint8_t
            status = await ezsp.ezspImportKey(context, plaintextKey);
        }
        else if (nvm3Key === NVM3KEY_STACK_GP_SINK_TABLE) {
            // typedef struct {
            //     uint8_t status;
            //     uint16_t options;
            //     //EmberGpAddress gpd;
            //     uint8_t gpAddress[8];
            //     uint8_t endpoint;
            //     uint8_t securityOptions;
            //     uint8_t gpdKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run)
            //     uint8_t sinkType[2];
            //     uint16_t groupList[2][2];
            //     uint32_t securityFrameCounter; // This is no more used, Incoming FC for gpd in a separate Token to control its update.
            //     uint16_t assignedAlias;
            //     uint8_t deviceId;
            //     uint8_t groupcastRadius;
            // } tokTypeStackGpSinkTableEntry;
            context.coreKeyType = enums_1.SecManKeyType.GREEN_POWER_SINK_TABLE_KEY;
            context.keyIndex = index;
            const s = 1 + 2 + 8 + 1 + 1;
            plaintextKey.contents = tokenData.data.subarray(s, s + consts_2.EMBER_ENCRYPTION_KEY_SIZE); // uint8_t+uint16_t+uint8_t[8]+uint8_t+uint8_t
            status = await ezsp.ezspImportKey(context, plaintextKey);
        }
        else if (nvm3Key === NVM3KEY_STACK_ZLL_SECURITY) {
            // typedef struct {
            //     uint32_t bitmask;
            //     uint8_t keyIndex;
            //     uint8_t encryptionKey[EMBER_ENCRYPTION_KEY_SIZE];
            //     uint8_t preconfiguredKey[EMBER_ENCRYPTION_KEY_SIZE];
            // } EmberTokTypeStackZllSecurity;
            context.coreKeyType = enums_1.SecManKeyType.ZLL_ENCRYPTION_KEY;
            let s = 4 + 1;
            plaintextKey.contents = tokenData.data.subarray(s, s + consts_2.EMBER_ENCRYPTION_KEY_SIZE); // uint32_t+uint8_t
            status = await ezsp.ezspImportKey(context, plaintextKey);
            context.coreKeyType = enums_1.SecManKeyType.ZLL_PRECONFIGURED_KEY;
            s += consts_2.EMBER_ENCRYPTION_KEY_SIZE; // after `encryptionKey`
            plaintextKey.contents = tokenData.data.subarray(s, s + consts_2.EMBER_ENCRYPTION_KEY_SIZE); // uint32_t+uint8_t+uint8_t[EMBER_ENCRYPTION_KEY_SIZE]
            status = await ezsp.ezspImportKey(context, plaintextKey);
        }
        else {
            // unknown key
        }
        return status;
    }
    /**
     * Updates zigbeed tokens from a backup of NCP tokens.
     *
     * @return EmberStatus status code
     */
    static async writeNcpTokensToZigbeedTokens(ezsp, inBuffer) {
        if (!inBuffer?.length) {
            throw new Error(`[TOKENS] Restore tokens buffer empty.`);
        }
        console.log(`[TOKENS] Restoring tokens to Zigbeed...`);
        let readOffset = 0;
        const inTokenCount = inBuffer.readUInt8(readOffset++);
        for (let i = 0; i < inTokenCount; i++) {
            const nvm3Key = inBuffer.readUInt32LE(readOffset); // 4 bytes Token Key/Creator
            readOffset += 4;
            const size = inBuffer.readUInt8(readOffset++); // 1 byte token size
            const arraySize = inBuffer.readUInt8(readOffset++); // 1 byte array size.
            for (let arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) {
                const tokenData = {
                    data: inBuffer.subarray(readOffset, readOffset + size),
                    size,
                };
                const creator = EmberTokensManager.getCreatorFromNvm3Key(nvm3Key); // uint16_t
                const status = (await ezsp.ezspSetTokenData(creator, arrayIndex, tokenData));
                console.assert(status === enums_1.EmberStatus.SUCCESS, `[TOKENS] Failed to set Zigbeed token data for key "${nvm3Key}" creator "${creator}" with status=${enums_1.EmberStatus[status]}.`);
                readOffset += tokenData.size;
            }
        }
        return enums_1.EmberStatus.SUCCESS;
    }
}
exports.EmberTokensManager = EmberTokensManager;
//# sourceMappingURL=tokensManager.js.map