ecash-lib
Version:
Library for eCash transaction building
102 lines • 3.93 kB
JavaScript
;
// Copyright (c) 2025 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyMsg = exports.signMsg = exports.magicHash = void 0;
const hash_1 = require("./hash");
const ecc_1 = require("./ecc");
const writerbytes_1 = require("./io/writerbytes");
const varsize_1 = require("./io/varsize");
const hex_1 = require("./io/hex");
const address_1 = require("./address/address");
/**
* messages.ts
*
* Sign and verify messages
*/
const ECASH_MSG_SIGNING_PREFIX = '\x16eCash Signed Message:\n';
/**
* Messages are prepared in a standard way before signing and verifying
*
* - The raw message (e.g., "Hello, world!") is encoded as a UTF-8 byte array
* - The prefixed message is constructed as:
*
* [prefix][message_length][message]
*
* where message_length is a variable-length integer (varint) encoding the
* byte length of the message
*
* We keep the "magicHash" name used in bitcoinjs-message as we do the same thing here
* with eCash tools
*
* ref https://github.com/bitcoinjs/bitcoinjs-message/blob/master/index.js#L57
*/
const magicHash = (message, messagePrefix = ECASH_MSG_SIGNING_PREFIX) => {
const encoder = new TextEncoder();
// Convert prefix to Uint8Array
const prefixBytes = encoder.encode(messagePrefix);
// Convert message to Uint8Array
const messageBytes = encoder.encode(message);
// Calculate the maximum possible size of the varint for message length
const maxVarintSize = messageBytes.length <= 0xfc
? 1
: messageBytes.length <= 0xffff
? 3
: messageBytes.length <= 0xffffffff
? 5
: 9;
// Create a WriterBytes instance with enough capacity
const writer = new writerbytes_1.WriterBytes(prefixBytes.length + maxVarintSize + messageBytes.length);
// Write the prefix
writer.putBytes(prefixBytes);
// Write the message length as a varint
(0, varsize_1.writeVarSize)(messageBytes.length, writer);
// Write the message
writer.putBytes(messageBytes);
// Return double SHA-256 hash
return (0, hash_1.sha256d)(writer.data);
};
exports.magicHash = magicHash;
/**
* Sign a message
*
* While there is not an official BIP or spec here, there is
* a de facto standard
*
* See implementation in bitcoinjs-lib and electrum
*/
const signMsg = (msg, sk, prefix = ECASH_MSG_SIGNING_PREFIX) => {
const preparedMsg = (0, exports.magicHash)(msg, prefix);
const sig = new ecc_1.Ecc().signRecoverable(sk, preparedMsg);
// Convert Uint8Array to binary string and encode with btoa
const binaryString = String.fromCharCode(...sig);
return btoa(binaryString);
};
exports.signMsg = signMsg;
/**
* Verify that a given message and signature
* came from a given address
*/
const verifyMsg = (msg, signature, address, prefix = ECASH_MSG_SIGNING_PREFIX) => {
try {
const preparedMsg = (0, exports.magicHash)(msg, prefix);
// Decode base64 signature to binary string and convert to Uint8Array
const binaryString = atob(signature);
const sig = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
sig[i] = binaryString.charCodeAt(i);
}
const recoveredPk = new ecc_1.Ecc().recoverSig(sig, preparedMsg);
// Get recovered hash as a hex string and compare to tested hash
const recoveredHash = (0, hex_1.toHex)((0, hash_1.shaRmd160)(recoveredPk));
const testedHash = address_1.Address.fromCashAddress(address).hash;
return recoveredHash === testedHash;
}
catch (err) {
console.error(`Error verifying signature`, err);
return false;
}
};
exports.verifyMsg = verifyMsg;
//# sourceMappingURL=messages.js.map