I'm doing a deep dive into Bitcoin and how it works. I'm currently learning about elliptic curves, signatures and verifications.
I'm reading the steps to for the signature generation algorithm.
It says:
For Alice to sign a message m
, she follows these steps:
- Calculate
e=HASH(m)
. (Here HASH is a cryptographic hash function, such as SHA-2, with the output converted to an integer.) - Let
z
be theL_n
leftmost bits ofe
, whereL_n
is the bit length of the group ordern
. (Note thatz
can be greater thann
but not longer.)
I don't understand both sentences from 2.
Let's say I have a hash
like this:
import { keccakFromString } from 'ethereumjs-util';
// This is Wikipedia's `n`
const BTC_PRIME_MODULO =
2n ** 256n -
2n ** 32n -
2n ** 9n -
2n ** 8n -
2n ** 7n -
2n ** 6n -
2n ** 4n -
1n;
const message = 'Learning blockchain development is fun.';
const hash = keccakFromString(message).toString('hex'); // `e` from Wikipedia
const hexToBigInt = (hex: string) => BigInt('0x' + hex);
const bigIntToBinary = (int: bigint) => int.toString(2);
type getFirstNChars = (numberOfChars: number) => (string: string) => string;
const getFirstNChars: getFirstNChars = n => string => string.slice(0, n);
const getFirst256Chars = getFirstNChars(256);
const binaryToBigInt = (binary: string) => BigInt('0b' + binary);
const get256LeftmostBits = pipe(
hexToBigInt,
bigIntToBinary,
getFirst256Chars,
binaryToBigInt,
);
const z = get256LeftmostBits(hash) // ... Is this correct?
How do I now get z
?
Is the group order of Bitcoin FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
and therefore the bit length 256?
And does "z
can be greater than n
but not longer" mean that z
could be BTC_PRIME_MODULO + 1n
, so it is greater, but has as many digits? How can z
be greater if its the n left most bits?
More context:
I'm trying to implement sign
based on that Wikipedia article, and here is what I have so far:
const BITCOIN_GENERATOR_POINT: EllipticCurvePoint = {
x: 55_066_263_022_277_343_669_578_718_895_168_534_326_250_603_453_777_594_175_500_187_360_389_116_729_240n,
y: 32_670_510_020_758_816_978_083_085_130_507_043_184_471_273_380_659_243_275_938_904_335_757_337_482_424n,
};
const generateRandomBigInt = () =>
BigInt(`0x${randomBytes(32).toString('hex')}`);
const getK = pipe(
generateRandomBigInt,
maybeReducedModulo(BTC_PRIME_MODULO - 1n),
);
type sign = (privateKey: string, message: string) => string;
const sign: sign = (privateKey, message) => {
const privateKeyBigInt = BigInt(privateKey);
const hash = keccakFromString(message).toString('hex');
let r = 0n;
let s = 0n;
const z = get256LeftmostBits(hash); // I'm unsure whether this is correct
while (r === 0n || s === 0n) {
const k = getK();
const p1 = multiplyWithBasePoint(k);
r = maybeReduceByBTCPrimeModulo(p1.x);
s = maybeReduceByBTCPrimeModulo(
invertByBTCPrimeModulo(k) * (z + r * privateKeyBigInt),
);
}
return ''; // ... need to properly concat r and s ...
};
where
maybeReducedModulo
givena
andb
calculatesa % b
but addsb
ifa
is negative.multiplyWithBasePoint
multiplies with the Bitcoin Generator PointG
.- and
invertByBTCPrimeModulo
calculates the modular multiplicative inverse with theBTC_PRIME_MODULO
.