Skip to content

Instantly share code, notes, and snippets.

@paulmillr
paulmillr / inp-nacl.mjs
Created April 23, 2024 16:08
noble vs tweetnacl
export { box_keyPair, sign_detached_verify, sign, box, box_open } from 'tweetnacl-ts';
@paulmillr
paulmillr / md5.test.ts
Created January 2, 2024 06:25
noble-hashes md5
MD5: {
fn: md5,
obj: md5.create,
node: (buf) => Uint8Array.from(crypto.createHash('md5').update(buf).digest()),
node_obj: () => crypto.createHash('md5'),
nist: [
'90015098 3cd24fb0d 6963f7d2 8e17f72',
'd41d8cd9 8f00b204e 9800998e cf8427e',
'8215ef07 96a20bcaa ae116d38 76c664a',
'03dd8807 a93175fb0 62dfb55d c7d359c',
@paulmillr
paulmillr / metamask-sign.md
Last active September 10, 2023 13:36
Tracing cryptography in Metamask dependencies

Tracing cryptography in Metamask dependencies

Metamask uses noble for low-level cryptography operations, such as signature creation. The audit path as per Sep 2023, where every item is name of NPM package:

flowchart TD;
    MM[metamask-extension] -->|imports KeyringController| MMKC["@metamask/keyring-controller"];
    MMKC -->|imports HDKeyring| MMHD["@metamask/eth-hd-keyring"];
 MMKC -->|imports SimpleKeyring| MMSK["@metamask/eth-simple-keyring"];
@paulmillr
paulmillr / noble-vs-elliptic.js
Last active July 26, 2023 08:29
Timing differences between different keys in noble and elliptic
import { secp256k1 } from '@noble/curves/secp256k1';
import elliptic from 'elliptic';
import { mark } from 'micro-bmark';
const EC = elliptic.ec;
(async () => {
var ec = new EC('secp256k1');
const a = '0000000000000000000000000000000000000000000000000000000000000003';
const b = '3000000000000000000000000000000000000000000000000000000000000000';
@paulmillr
paulmillr / hybrid-pkg.md
Last active February 10, 2024 17:45
Why writing hybrid Common.js + ESM NPM packages is hard
  • BigInt literals (15n) are not supported in some environments
    • Must use BigInt('15') instead
    • It was not supported in React Native 0.70. Need to test in new versions
  • Must use hybrid ESM-Common.js package
    • ESM (ECMAScript modules) are not supported in Electron, Jest
      • Electron needs pre-compilation step aka bundler
      • Jest has experimental esm flag, also can be replaced with micro-should
    • Common.js modules (legacy node.js modules) are not supported in browsers, Deno
      • Browsers can be worked around with UMD wrapper
  • Doesn’t play well with ESM in-browser imports
@paulmillr
paulmillr / jacobi.js
Created March 3, 2023 00:52
jacobi symbol for bigints
function jacobi(a, n) {
if (n <= 0n) throw new Error('n must be positive integer');
if (n % 2n === 0n) throw new Error('n must be odd');
a %= n;
let result = 1;
while (a !== 0n) {
while (a % 2n === 0n) {
a /= 2n;
let n_mod_8 = n % 8n;
if (n_mod_8 === 3n || n_mod_8 === 5n) {
multiplyAndAddUnsafe(Q: ProjectivePoint, a: bigint, b: bigint): ProjectivePoint | undefined {
// R=R*2+P //bits from a,b = 1,0
// R=R*2 //bits from a,b = 0,0
// R=R*2+Q //bits from a,b = 0,1
// R=R*2+P+Q //bits from a,b = 1,1
const G: ProjectivePoint = this;
const spl = (a: bigint) => a.toString(2).padStart(max, '0').split('').map(i => Number(i));
const max = Math.max(ut.bitLen(a), ut.bitLen(b));
const aBits = spl(a);
const bBits = spl(b);
@paulmillr
paulmillr / guide.md
Last active November 4, 2023 15:22
Sign git commits with ssh key

Sign git commits with ssh key

  • git version must be >= 2.34. Earlier versions don't support gpg.format ssh (signing commits with ssh keys).

    If you're printing signature keys in git log by using %GK %GF %GP %GT in git.format.pretty, earlier versions will crash when doing git log with following error:

    BUG: gpg-interface.c:284: bad signature

  • OpenSSH version must be >= 8.8. Earlier versions don't support valid-after,valid-before options.

Fun parts of developing noble-hashes and scure-base

  • Base58 is quadratic (O(n^2)). Basically you can’t encode 1MB of data with it. This has been found with our DoS tests, which we employ for scure-base and noble-hashes. See README for more details
  • Hashes are additionally tested against huge multi-gig inputs, scrypt/pbkdf2 are tested against all possible combination of options. They take 2 hours to run on a decent machine
  • Hashes are actually faster than many wasm alternatives. A single sha256 hashing of 32 bytes of data takes 888 nanoseconds on mac with M1
  • The last fact is extremely remarkable, because we do not employ loop unrolls in the code. A loop unroll is when you’re writing code which could have been executed in loop like for (let step = 0; step &lt; 64), but instead, you’re writing every iteration step-by-step. Which incr
@paulmillr
paulmillr / unrolled-noble-sha3.ts
Last active September 2, 2022 11:32
Replace sha3.js in node_modules/@noble/hashes with unrolled-sha3.js
const rotlHs = (h: string, l: string, s: number) =>
s > 32 ? `(${l} << ${s - 32}) | (${h} >>> ${64 - s})` : `(${h} << ${s}) | (${l} >>> ${32 - s})`;
const rotlLs = (h: string, l: string, s: number) =>
s > 32 ? `(${h} << ${s - 32}) | (${l} >>> ${64 - s})` : `(${l} << ${s}) | (${h} >>> ${32 - s})`;
export const keccakP = (() => {
let out = 'let h, l, s = state;\n';
const vars = [];
for (let i = 0; i < 200 / 4; i++) vars.push(`s${i} = s[${i}]`);
out += `let ${vars.join(', ')};\n`;