UNPKG

@iacobus/hd

Version:

Hierarchical Deterministic Symmetric Keys.

87 lines (86 loc) 3.52 kB
"use strict"; /** * @fileoverview Provides functionality for deriving hierarchical deterministic symmetric keys. * @module * @author Jacob V. B. Haap <iacobus.xyz> * @license MIT */ Object.defineProperty(exports, "__esModule", { value: true }); exports.deriveMaster = deriveMaster; exports.deriveChild = deriveChild; exports.deriveNode = deriveNode; exports.lineage = lineage; const hkdf_1 = require("@noble/hashes/hkdf"); const utils_js_1 = require("./utils.js"); /** * deriveMaster derives a new master key from a given hash and secret. * @example * const master = deriveMaster(h, secret); */ function deriveMaster(h, secret) { const salt = (0, utils_js_1.calcSalt)(h, secret); // Derive salt from the secret const ikm = (0, hkdf_1.hkdf)(h, secret, salt, "MASTER", 64); // Derive ikm from bytes const master = ikm.slice(0, 32); // First 32 bytes as the key const code = ikm.slice(32, 64); // Last 32 bytes as the chain code const fp = (0, utils_js_1.fingerprint)(h, secret, master); // Derive a fingerprint for the master key return { key: master, code: code, depth: 0, fingerprint: fp }; } /** * deriveChild derives a new child key from a given hash, master key, and index. * @example * const child = deriveChild(h, master, 42); */ function deriveChild(h, master, index) { index = index >>> 0; // Emulate 32 bit integer const info1 = (0, utils_js_1.encodeInt)(index); // Context info from encoded index const salt = (0, utils_js_1.calcSalt)(h, master.code, info1); // Derive salt from the master code const info2 = "CHILD" + index.toString(); // Construct info for HKDF form CHILD + index string const ikm = (0, hkdf_1.hkdf)(h, master.code, salt, info2, 64); // Derive ikm from master chain code const child = ikm.slice(0, 32); // First 32 bytes as the key const code = ikm.slice(32, 64); // Last 32 bytes as the chain code const fp = (0, utils_js_1.fingerprint)(h, master.key, child); // Derive a fingerprint for the child key return { key: child, code: code, depth: master.depth + 1, fingerprint: fp }; } /** * deriveNode derives a new key at a node in a hierarchy descending from a master key, from * a given hash, master key, and derivation path. * @example * const node = deriveNode(h, master, path); */ function deriveNode(h, master, path) { let key = deriveChild(h, master, path[0]); // Initialize key with first index from the path for (let i = 1; i < path.length; i++) { const index = path[i]; // Get the current index key = deriveChild(h, key, index); // Derive a child of key for the current index } return key; } /** * lineage checks if a key is the direct child of a master key, from a given hash, * child key, and master key. * @example * const related = lineage(h, child, master); */ function lineage(h, child, master) { const fp1 = child.fingerprint; // Extract the child fingerprint as fp1 const fp2 = (0, utils_js_1.fingerprint)(h, master.key, child.key); // Derive fp2 from the master and child keys if (fp1.length !== 16 || fp2.length !== 16) { throw new RangeError(`fingerprints for lineage verification must be 16 bytes each`); } // Complete a constant-time comparison between the 16 bytes of each fingerprint let result = 0; for (let i = 0; i < 16; i++) { result |= fp1[i] ^ fp2[i]; } return result === 0; // Return a boolean result of the byte comparison }