meta-log-db
Version:
Native database package for Meta-Log (ProLog, DataLog, R5RS)
152 lines • 6.46 kB
JavaScript
;
/**
* BIP32 HD Key Derivation Implementation
*
* Implements Hierarchical Deterministic (HD) wallet key derivation using Web Crypto API
* Based on BIP32 specification: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.deriveKey = deriveKey;
exports.derivePublicKey = derivePublicKey;
exports.deriveFromExtendedKey = deriveFromExtendedKey;
/**
* Parse derivation path (e.g., "m/44'/60'/0'/0/0")
*/
function parsePath(path) {
const parts = path.split('/');
if (parts[0] !== 'm') {
throw new Error('Path must start with "m"');
}
return parts.slice(1).map(part => {
const hardened = part.endsWith("'") || part.endsWith('h');
const index = parseInt(part.replace(/['h]/g, ''), 10);
if (isNaN(index)) {
throw new Error(`Invalid path segment: ${part}`);
}
// Hardened keys use index >= 2^31
return hardened ? index + 0x80000000 : index;
});
}
/**
* HMAC-SHA512 using Web Crypto API
*/
async function hmacSha512(key, data) {
// Ensure we have proper ArrayBuffer views
const keyBuffer = key.buffer instanceof ArrayBuffer ? key : new Uint8Array(key).buffer;
const dataBuffer = data.buffer instanceof ArrayBuffer ? data : new Uint8Array(data).buffer;
const cryptoKey = await crypto.subtle.importKey('raw', keyBuffer, { name: 'HMAC', hash: 'SHA-512' }, false, ['sign']);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, dataBuffer);
return new Uint8Array(signature);
}
/**
* Derive child key from parent key using BIP32
*/
async function deriveChildKey(parentKey, parentChainCode, index) {
const isHardened = index >= 0x80000000;
const indexBuffer = new ArrayBuffer(4);
const indexView = new DataView(indexBuffer);
indexView.setUint32(0, index, false);
const data = new Uint8Array(37);
if (isHardened) {
// Hardened: 0x00 || parentKey || index
data[0] = 0x00;
data.set(parentKey, 1);
data.set(new Uint8Array(indexBuffer), 33);
}
else {
// Non-hardened: parentPublicKey || index
// For non-hardened, we need the public key
// This is a simplified version - full implementation would derive public key
throw new Error('Non-hardened derivation requires public key derivation (not implemented)');
}
const hmac = await hmacSha512(parentChainCode, data);
const childKey = hmac.slice(0, 32);
const chainCode = hmac.slice(32, 64);
// Add parent key to child key (modulo curve order)
// Simplified: XOR for demonstration (full implementation would use secp256k1 arithmetic)
const derivedKey = new Uint8Array(32);
for (let i = 0; i < 32; i++) {
derivedKey[i] = (parentKey[i] + childKey[i]) % 256;
}
return { key: derivedKey, chainCode };
}
/**
* Derive key from seed using BIP32 derivation path
*/
async function deriveKey(seed, path) {
// Master key derivation: HMAC-SHA512(Key = "Bitcoin seed", Data = seed)
const keyMaterial = new TextEncoder().encode('Bitcoin seed');
const masterHmac = await hmacSha512(keyMaterial, seed);
const masterKey = masterHmac.slice(0, 32);
const masterChainCode = masterHmac.slice(32, 64);
// Parse derivation path
const indices = parsePath(path);
// Derive through each level
let currentKey = masterKey;
let currentChainCode = masterChainCode;
for (const index of indices) {
const result = await deriveChildKey(currentKey, currentChainCode, index);
currentKey = new Uint8Array(result.key);
currentChainCode = new Uint8Array(result.chainCode);
}
// Import as AES-GCM key for encryption
// Ensure we have a proper ArrayBuffer
const keyBuffer = currentKey.buffer instanceof ArrayBuffer
? currentKey.buffer
: new Uint8Array(currentKey).buffer;
return await crypto.subtle.importKey('raw', keyBuffer, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
}
/**
* Derive public key from private key (simplified)
*
* Note: Full implementation would use secp256k1 elliptic curve operations
* This is a placeholder that derives a key suitable for encryption
*/
async function derivePublicKey(privateKey) {
// Export private key material
const keyData = await crypto.subtle.exportKey('raw', privateKey);
const keyBytes = new Uint8Array(keyData);
// Simplified public key derivation (XOR with constant)
// Full implementation would use secp256k1 point multiplication
const publicKeyBytes = new Uint8Array(32);
for (let i = 0; i < 32; i++) {
publicKeyBytes[i] = keyBytes[i] ^ 0xFF;
}
return await crypto.subtle.importKey('raw', publicKeyBytes, { name: 'AES-GCM', length: 256 }, false, ['encrypt']);
}
/**
* Derive key from extended key format
*
* Extended key format: base58 encoded (version || depth || fingerprint || index || chaincode || key)
* This is a simplified version - full implementation would decode base58
*/
async function deriveFromExtendedKey(extendedKey, path) {
// For now, decode as hex (full implementation would use base58)
// Browser-compatible hex decoding
const hexMatch = extendedKey.match(/.{1,2}/g);
if (!hexMatch) {
throw new Error('Invalid hex string');
}
const keyBytes = new Uint8Array(hexMatch.map(byte => parseInt(byte, 16)));
if (keyBytes.length < 78) {
throw new Error('Invalid extended key length');
}
const chainCode = keyBytes.slice(13, 45);
const key = keyBytes.slice(45, 77);
// Parse derivation path (remove 'm' prefix if present)
const indices = parsePath(path.startsWith('m/') ? path : `m/${path}`);
// Derive through each level
let currentKey = new Uint8Array(key);
let currentChainCode = new Uint8Array(chainCode);
for (const index of indices) {
const result = await deriveChildKey(currentKey, currentChainCode, index);
currentKey = new Uint8Array(result.key);
currentChainCode = new Uint8Array(result.chainCode);
}
// Ensure we have a proper ArrayBuffer
const keyBuffer = currentKey.buffer instanceof ArrayBuffer
? currentKey.buffer
: new Uint8Array(currentKey).buffer;
return await crypto.subtle.importKey('raw', keyBuffer, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
}
//# sourceMappingURL=bip32.js.map