import * as utils from '../../utils.js';
import { DerivationNonce, } from './interfaces.js';
import { SupportedKeyDerivationTypes } from '../module.js';
import { Bn, Ecdsa, KeyPair, Point, PrivKey, PubKey, Sig } from '@ts-bitcoin/core';
import { jose, JWKOrHexKeyToCryptoKeyAndJWK, crypto } from '../../utils.js';
const keyBitsLen = 32; // FIPS 186 B.4.1.
const defaultNonceIterations = 10000;
export class Secp256k1KeyDerivationStrategy {
    async fromRaw(rawKey) {
        const bitcoinKey = PrivKey.fromBuffer(Buffer.from(rawKey));
        return new Secp256k1Key(bitcoinKey, rawKey);
    }
    async create(masterKey, salt) {
        const rawMasterKey = await crypto.subtle.exportKey('raw', masterKey);
        const pathBytes = new TextEncoder().encode(salt);
        // Because a PBKDF2 output doesnt necessarily lie on the correct curve, we need to keep increasing iterations until we find a key that lies on the curve
        const derivedBytes = await utils.pbkdf2(new Uint8Array(rawMasterKey), pathBytes, keyBitsLen * 8, defaultNonceIterations);
        console.log('Derived bytes before curve adjustment', {
            derivedBytes,
            derivedBytesLen: derivedBytes.length,
        });
        const derivedKey = bytesToPrivateKey(derivedBytes);
        const nonce = new DerivationNonce(salt, 10000);
        return new Secp256k1Key(derivedKey, derivedBytes, nonce);
    }
    async hydrate(masterKey, nonce) {
        const rawMasterKey = await crypto.subtle.exportKey('raw', masterKey);
        const saltBytes = new TextEncoder().encode(nonce.salt);
        const derivedBytes = await utils.pbkdf2(new Uint8Array(rawMasterKey), saltBytes, keyBitsLen * 8, nonce.iterations);
        const derivedKey = bytesToPrivateKey(derivedBytes);
        return new Secp256k1Key(derivedKey, derivedBytes, nonce);
    }
}
export class Secp256k1PublicKey {
    pubKey;
    constructor(pubKey) {
        this.pubKey = pubKey;
    }
    getPublicKeyHex() {
        return Promise.resolve(this.pubKey.toHex());
    }
    getPublicKeyBytes() {
        return Promise.resolve(new Uint8Array(this.pubKey.toBuffer()));
    }
    async verify(signature, message) {
        const messageHash = await utils.sha256(message);
        return Ecdsa.verify(Buffer.from(messageHash), Sig.fromBuffer(Buffer.from(signature)), this.pubKey);
    }
}
export class Secp256k1Key {
    key;
    secret;
    derivation_path;
    constructor(key, secret, derivation_path) {
        this.key = key;
        this.secret = secret;
        this.derivation_path = derivation_path;
    }
    async getPublicKeyBytes() {
        const pubKey = await this.getPublicKey();
        return await pubKey.getPublicKeyBytes();
    }
    async getPrivateKeyBytes() {
        return new Uint8Array(this.key.toBuffer());
    }
    async toJWK() {
        const hexPrivateKey = utils.hex.bufToHex(this.key.toBuffer());
        const { jwk } = await JWKOrHexKeyToCryptoKeyAndJWK(hexPrivateKey, 'ECDSA', 'secp256k1');
        const thumbprint = await jose.calculateJwkThumbprint(jwk, 'sha256');
        return {
            ...jwk,
            kid: thumbprint,
        };
    }
    async toCryptoKey() {
        const hexPrivateKey = utils.hex.bufToHex(this.key.toBuffer());
        const { cryptoKey } = await JWKOrHexKeyToCryptoKeyAndJWK(hexPrivateKey, 'ECDSA', 'secp256k1');
        return cryptoKey;
    }
    async exportKeyInformation() {
        return {
            version: 1,
            curve: SupportedKeyDerivationTypes.SECP256K1,
            key: this.derivation_path
                ? {
                    type: 'derived',
                    from: 'master',
                    nonce: this.derivation_path.toString(),
                }
                : {
                    type: 'raw',
                    rawKeyHex: utils.hex.bufToHex(this.secret),
                },
            publicKey: await this.getPublicKey().then((p) => p.getPublicKeyHex()),
            fingerprint: utils.hex.bufToHex(await this.getFingerprint()),
        };
    }
    toBitcoinKey() {
        return this.key;
    }
    async sign(message) {
        const messageHash = await utils.sha256(message);
        const signature = Ecdsa.sign(Buffer.from(messageHash), KeyPair.fromPrivKey(this.key));
        return signature.toBuffer();
    }
    async verify(signature, message) {
        const pubKey = await this.getPublicKey();
        return pubKey.verify(signature, message);
    }
    getPath() {
        return this.derivation_path;
    }
    async getFingerprint() {
        return utils.sha256(await this.getPublicKeyBytes());
    }
    getPublicKey() {
        return Promise.resolve(new Secp256k1PublicKey(PubKey.fromPrivKey(this.key)));
    }
}
/// Borrowed from ts-bitcoin
const bytesToPrivateKey = (bytes) => {
    let bn;
    let condition;
    do {
        bn = new Bn().fromBuffer(Buffer.from(bytes));
        condition = bn.lt(Point.getN());
    } while (!condition);
    return PrivKey.fromBn(bn);
};
//# sourceMappingURL=secp256k1.js.map