"use strict";
const assert = require("assert");
const brorand = require("brorand");
const hashjs = require("hash.js");
const elliptic = require("elliptic");
const addressCodec = require("ripple-address-codec");
const secp256k1_1 = require("./secp256k1");
const utils = require("./utils");
const Ed25519 = elliptic.eddsa('ed25519');
const Secp256k1 = elliptic.ec('secp256k1');
const { hexToBytes } = utils;
const { bytesToHex } = utils;
function generateSeed(options = {}) {
    assert.ok(!options.entropy || options.entropy.length >= 16, 'entropy too short');
    const entropy = options.entropy ? options.entropy.slice(0, 16) : brorand(16);
    const type = options.algorithm === 'ed25519' ? 'ed25519' : 'secp256k1';
    return addressCodec.encodeSeed(Buffer.from(entropy), type);
}
function hash(message) {
    return hashjs.sha512().update(message).digest().slice(0, 32);
}
const secp256k1 = {
    deriveKeypair(entropy, options) {
        const prefix = '00';
        const privateKey = prefix + (0, secp256k1_1.derivePrivateKey)(entropy, options).toString(16, 64).toUpperCase();
        const publicKey = bytesToHex(Secp256k1.keyFromPrivate(privateKey.slice(2))
            .getPublic()
            .encodeCompressed());
        return { privateKey, publicKey };
    },
    sign(message, privateKey) {
        return bytesToHex(Secp256k1.sign(hash(message), hexToBytes(privateKey), {
            canonical: true,
        }).toDER());
    },
    verify(message, signature, publicKey) {
        return Secp256k1.verify(hash(message), signature, hexToBytes(publicKey));
    },
};
const ed25519 = {
    deriveKeypair(entropy) {
        const prefix = 'ED';
        const rawPrivateKey = hash(entropy);
        const privateKey = prefix + bytesToHex(rawPrivateKey);
        const publicKey = prefix + bytesToHex(Ed25519.keyFromSecret(rawPrivateKey).pubBytes());
        return { privateKey, publicKey };
    },
    sign(message, privateKey) {
        // caution: Ed25519.sign interprets all strings as hex, stripping
        // any non-hex characters without warning
        assert.ok(Array.isArray(message), 'message must be array of octets');
        return bytesToHex(Ed25519.sign(message, hexToBytes(privateKey).slice(1)).toBytes());
    },
    verify(message, signature, publicKey) {
        return Ed25519.verify(message, hexToBytes(signature), hexToBytes(publicKey).slice(1));
    },
};
function select(algorithm) {
    const methods = { 'ecdsa-secp256k1': secp256k1, ed25519 };
    return methods[algorithm];
}
function deriveKeypair(seed, options) {
    const decoded = addressCodec.decodeSeed(seed);
    const algorithm = decoded.type === 'ed25519' ? 'ed25519' : 'ecdsa-secp256k1';
    const method = select(algorithm);
    const keypair = method.deriveKeypair(decoded.bytes, options);
    const messageToVerify = hash('This test message should verify.');
    const signature = method.sign(messageToVerify, keypair.privateKey);
    /* istanbul ignore if */
    if (method.verify(messageToVerify, signature, keypair.publicKey) !== true) {
        throw new Error('derived keypair did not generate verifiable signature');
    }
    return keypair;
}
function getAlgorithmFromKey(key) {
    const bytes = hexToBytes(key);
    return bytes.length === 33 && bytes[0] === 0xed
        ? 'ed25519'
        : 'ecdsa-secp256k1';
}
function sign(messageHex, privateKey) {
    const algorithm = getAlgorithmFromKey(privateKey);
    return select(algorithm).sign(hexToBytes(messageHex), privateKey);
}
function verify(messageHex, signature, publicKey) {
    const algorithm = getAlgorithmFromKey(publicKey);
    return select(algorithm).verify(hexToBytes(messageHex), signature, publicKey);
}
function deriveAddressFromBytes(publicKeyBytes) {
    return addressCodec.encodeAccountID(utils.computePublicKeyHash(publicKeyBytes));
}
function deriveAddress(publicKey) {
    return deriveAddressFromBytes(Buffer.from(hexToBytes(publicKey)));
}
function deriveNodeAddress(publicKey) {
    const generatorBytes = addressCodec.decodeNodePublic(publicKey);
    const accountPublicBytes = (0, secp256k1_1.accountPublicFromPublicGenerator)(generatorBytes);
    return deriveAddressFromBytes(accountPublicBytes);
}
const { decodeSeed } = addressCodec;
module.exports = {
    generateSeed,
    deriveKeypair,
    sign,
    verify,
    deriveAddress,
    deriveNodeAddress,
    decodeSeed,
};
//# sourceMappingURL=index.js.map