@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
text/typescript
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);
}