UNPKG

@xmr-core/xmr-crypto-utils

Version:

Core crypto operations for Monero and implementations and interfaces of various hardware devices for creating Monero transactions securely

186 lines (173 loc) 5 kB
import { CNCrypto, nacl } from "@xmr-core/xmr-vendor"; import { bintohex, hextobin, valid_hex } from "@xmr-core/xmr-str-utils"; import { STRUCT_SIZES, KEY_SIZE } from "./constants"; import { hash_to_ec_2 } from "./hash_ops"; //curve and scalar functions; split out to make their host functions cleaner and more readable //inverts X coordinate -- this seems correct ^_^ -luigi1111 function ge_neg(point: string) { if (point.length !== 64) { throw Error("expected 64 char hex string"); } return ( point.slice(0, 62) + ((parseInt(point.slice(62, 63), 16) + 8) % 16).toString(16) + point.slice(63, 64) ); } export function ge_add(p1: string, p2: string) { if (p1.length !== 64 || p2.length !== 64) { throw Error("Invalid input length!"); } return bintohex(nacl.ge_add(hextobin(p1), hextobin(p2))); } //order matters export function ge_sub(point1: string, point2: string) { const point2n = ge_neg(point2); return ge_add(point1, point2n); } //adds two scalars together export function sc_add(scalar1: string, scalar2: string) { if (scalar1.length !== 64 || scalar2.length !== 64) { throw Error("Invalid input length!"); } const scalar1_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR); const scalar2_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR); CNCrypto.HEAPU8.set(hextobin(scalar1), scalar1_m); CNCrypto.HEAPU8.set(hextobin(scalar2), scalar2_m); const derived_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR); CNCrypto.ccall( "sc_add", "void", ["number", "number", "number"], [derived_m, scalar1_m, scalar2_m], ); const res = CNCrypto.HEAPU8.subarray( derived_m, derived_m + STRUCT_SIZES.EC_SCALAR, ); CNCrypto._free(scalar1_m); CNCrypto._free(scalar2_m); CNCrypto._free(derived_m); return bintohex(res); } //subtracts one scalar from another export function sc_sub(scalar1: string, scalar2: string) { if (scalar1.length !== 64 || scalar2.length !== 64) { throw Error("Invalid input length!"); } const scalar1_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR); const scalar2_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR); CNCrypto.HEAPU8.set(hextobin(scalar1), scalar1_m); CNCrypto.HEAPU8.set(hextobin(scalar2), scalar2_m); const derived_m = CNCrypto._malloc(STRUCT_SIZES.EC_SCALAR); CNCrypto.ccall( "sc_sub", "void", ["number", "number", "number"], [derived_m, scalar1_m, scalar2_m], ); const res = CNCrypto.HEAPU8.subarray( derived_m, derived_m + STRUCT_SIZES.EC_SCALAR, ); CNCrypto._free(scalar1_m); CNCrypto._free(scalar2_m); CNCrypto._free(derived_m); return bintohex(res); } //res = c - (ab) mod l; argument names copied from the signature implementation export function sc_mulsub(sigc: string, sec: string, k: string) { if ( k.length !== KEY_SIZE * 2 || sigc.length !== KEY_SIZE * 2 || sec.length !== KEY_SIZE * 2 || !valid_hex(k) || !valid_hex(sigc) || !valid_hex(sec) ) { throw Error("bad scalar"); } const sec_m = CNCrypto._malloc(KEY_SIZE); CNCrypto.HEAPU8.set(hextobin(sec), sec_m); const sigc_m = CNCrypto._malloc(KEY_SIZE); CNCrypto.HEAPU8.set(hextobin(sigc), sigc_m); const k_m = CNCrypto._malloc(KEY_SIZE); CNCrypto.HEAPU8.set(hextobin(k), k_m); const res_m = CNCrypto._malloc(KEY_SIZE); CNCrypto.ccall( "sc_mulsub", "void", ["number", "number", "number", "number"], [res_m, sigc_m, sec_m, k_m], ); const res = CNCrypto.HEAPU8.subarray(res_m, res_m + KEY_SIZE); CNCrypto._free(k_m); CNCrypto._free(sec_m); CNCrypto._free(sigc_m); CNCrypto._free(res_m); return bintohex(res); } export function ge_double_scalarmult_base_vartime( c: string, P: string, r: string, ) { if (c.length !== 64 || P.length !== 64 || r.length !== 64) { throw Error("Invalid input length!"); } return bintohex( nacl.ge_double_scalarmult_base_vartime( hextobin(c), hextobin(P), hextobin(r), ), ); } export function ge_double_scalarmult_postcomp_vartime( r: string, P: string, c: string, I: string, ) { if ( c.length !== 64 || P.length !== 64 || r.length !== 64 || I.length !== 64 ) { throw Error("Invalid input length!"); } const Pb = hash_to_ec_2(P); return bintohex( nacl.ge_double_scalarmult_postcomp_vartime( hextobin(r), hextobin(Pb), hextobin(c), hextobin(I), ), ); } export function ge_scalarmult_base(sec: string) { if (sec.length !== 64) { throw Error("Invalid sec length"); } return bintohex(nacl.ge_scalarmult_base(hextobin(sec))); } export function ge_scalarmult(pub: string, sec: string) { if (pub.length !== 64 || sec.length !== 64) { throw Error("Invalid input length"); } return bintohex(nacl.ge_scalarmult(hextobin(pub), hextobin(sec))); } export function sc_reduce32(hex: string) { const input = hextobin(hex); if (input.length !== 32) { throw Error("Invalid input length"); } const mem = CNCrypto._malloc(32); CNCrypto.HEAPU8.set(input, mem); CNCrypto.ccall("sc_reduce32", "void", ["number"], [mem]); const output = CNCrypto.HEAPU8.subarray(mem, mem + 32); CNCrypto._free(mem); return bintohex(output); }