1453 lines
34 KiB
TypeScript
1453 lines
34 KiB
TypeScript
/*
|
|
This file is part of TALER
|
|
(C) 2015 GNUnet e.V.
|
|
|
|
TALER is free software; you can redistribute it and/or modify it under the
|
|
terms of the GNU General Public License as published by the Free Software
|
|
Foundation; either version 3, or (at your option) any later version.
|
|
|
|
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
*/
|
|
|
|
|
|
/**
|
|
* Medium-level interface to emscripten-compiled modules used
|
|
* by the wallet. Handles memory management by allocating by allocating
|
|
* objects in arenas that then can be disposed of all at once.
|
|
*
|
|
* The high-level interface (using WebWorkers) is exposed in src/cryptoApi.ts.
|
|
*/
|
|
|
|
/**
|
|
* Imports.
|
|
*/
|
|
import {AmountJson} from "../types";
|
|
|
|
import {EmscFunGen, getLib} from "./emscLoader";
|
|
|
|
const emscLib = getLib();
|
|
|
|
|
|
/**
|
|
* Size of a native pointer. Must match the size
|
|
* use when compiling via emscripten.
|
|
*/
|
|
const PTR_SIZE = 4;
|
|
|
|
const GNUNET_OK = 1;
|
|
|
|
|
|
/**
|
|
* Get an emscripten-compiled function.
|
|
*/
|
|
const getEmsc: EmscFunGen = (name: string, ret: any, argTypes: any[]) => {
|
|
return (...args: any[]) => {
|
|
return emscLib.ccall(name, ret, argTypes, args);
|
|
};
|
|
};
|
|
|
|
|
|
/**
|
|
* Wrapped emscripten functions that do not allocate any memory.
|
|
*/
|
|
const emsc = {
|
|
amount_add: getEmsc("TALER_amount_add", "number", ["number", "number", "number"]),
|
|
amount_cmp: getEmsc("TALER_amount_cmp", "number", ["number", "number"]),
|
|
amount_get_zero: getEmsc("TALER_amount_get_zero", "number", ["string", "number"]),
|
|
amount_hton: getEmsc("TALER_amount_hton", "void", ["number", "number"]),
|
|
amount_normalize: getEmsc("TALER_amount_normalize", "void", ["number"]),
|
|
amount_ntoh: getEmsc("TALER_amount_ntoh", "void", ["number", "number"]),
|
|
amount_subtract: getEmsc("TALER_amount_subtract", "number", ["number", "number", "number"]),
|
|
ecdh_eddsa: getEmsc("GNUNET_CRYPTO_ecdh_eddsa", "number", ["number", "number", "number"]),
|
|
eddsa_sign: getEmsc("GNUNET_CRYPTO_eddsa_sign", "number", ["number", "number", "number"]),
|
|
eddsa_verify: getEmsc("GNUNET_CRYPTO_eddsa_verify", "number", ["number", "number", "number", "number"]),
|
|
free: (ptr: number) => emscLib._free(ptr),
|
|
get_currency: getEmsc("TALER_WR_get_currency", "string", ["number"]),
|
|
get_fraction: getEmsc("TALER_WR_get_fraction", "number", ["number"]),
|
|
get_value: getEmsc("TALER_WR_get_value", "number", ["number"]),
|
|
hash: getEmsc("GNUNET_CRYPTO_hash", "void", ["number", "number", "number"]),
|
|
hash_context_abort: getEmsc("GNUNET_CRYPTO_hash_context_abort", "void", ["number"]),
|
|
hash_context_finish: getEmsc("GNUNET_CRYPTO_hash_context_finish", "void", ["number", "number"]),
|
|
hash_context_read: getEmsc("GNUNET_CRYPTO_hash_context_read", "void", ["number", "number", "number"]),
|
|
hash_create_random: getEmsc("GNUNET_CRYPTO_hash_create_random", "void", ["number", "number"]),
|
|
memmove: getEmsc("memmove", "number", ["number", "number", "number"]),
|
|
random_block: getEmsc("GNUNET_CRYPTO_random_block", "void", ["number", "number", "number"]),
|
|
rsa_blinding_key_destroy: getEmsc("GNUNET_CRYPTO_rsa_blinding_key_free", "void", ["number"]),
|
|
rsa_public_key_free: getEmsc("GNUNET_CRYPTO_rsa_public_key_free", "void", ["number"]),
|
|
rsa_signature_free: getEmsc("GNUNET_CRYPTO_rsa_signature_free", "void", ["number"]),
|
|
setup_fresh_coin: getEmsc( "TALER_setup_fresh_coin", "void", ["number", "number", "number"]),
|
|
string_to_data: getEmsc("GNUNET_STRINGS_string_to_data", "number", ["number", "number", "number", "number"]),
|
|
};
|
|
|
|
|
|
/**
|
|
* Emscripten functions that allocate memory.
|
|
*/
|
|
const emscAlloc = {
|
|
data_to_string_alloc: getEmsc("GNUNET_STRINGS_data_to_string_alloc", "number", ["number", "number"]),
|
|
ecdhe_key_create: getEmsc("GNUNET_CRYPTO_ecdhe_key_create", "number", []),
|
|
ecdhe_public_key_from_private: getEmsc( "TALER_WRALL_ecdhe_public_key_from_private", "number", ["number"]),
|
|
ecdsa_key_create: getEmsc("GNUNET_CRYPTO_ecdsa_key_create", "number", []),
|
|
ecdsa_public_key_from_private: getEmsc( "TALER_WRALL_ecdsa_public_key_from_private", "number", ["number"]),
|
|
eddsa_key_create: getEmsc("GNUNET_CRYPTO_eddsa_key_create", "number", []),
|
|
eddsa_public_key_from_private: getEmsc( "TALER_WRALL_eddsa_public_key_from_private", "number", ["number"]),
|
|
get_amount: getEmsc("TALER_WRALL_get_amount", "number", ["number", "number", "number", "string"]),
|
|
hash_context_start: getEmsc("GNUNET_CRYPTO_hash_context_start", "number", []),
|
|
malloc: (size: number) => emscLib._malloc(size),
|
|
purpose_create: getEmsc("TALER_WRALL_purpose_create", "number", ["number", "number", "number"]),
|
|
rsa_blind: getEmsc("GNUNET_CRYPTO_rsa_blind", "number", ["number", "number", "number", "number", "number"]),
|
|
rsa_blinding_key_create: getEmsc("GNUNET_CRYPTO_rsa_blinding_key_create", "number", ["number"]),
|
|
rsa_blinding_key_decode: getEmsc("GNUNET_CRYPTO_rsa_blinding_key_decode", "number", ["number", "number"]),
|
|
rsa_blinding_key_encode: getEmsc("GNUNET_CRYPTO_rsa_blinding_key_encode", "number", ["number", "number"]),
|
|
rsa_public_key_decode: getEmsc("GNUNET_CRYPTO_rsa_public_key_decode", "number", ["number", "number"]),
|
|
rsa_public_key_encode: getEmsc("GNUNET_CRYPTO_rsa_public_key_encode", "number", ["number", "number"]),
|
|
rsa_signature_decode: getEmsc("GNUNET_CRYPTO_rsa_signature_decode", "number", ["number", "number"]),
|
|
rsa_signature_encode: getEmsc("GNUNET_CRYPTO_rsa_signature_encode", "number", ["number", "number"]),
|
|
rsa_unblind: getEmsc("GNUNET_CRYPTO_rsa_unblind", "number", ["number", "number", "number"]),
|
|
};
|
|
|
|
|
|
/**
|
|
* Constants for signatures purposes, define what the signatures vouches for.
|
|
*/
|
|
export enum SignaturePurpose {
|
|
RESERVE_WITHDRAW = 1200,
|
|
WALLET_COIN_DEPOSIT = 1201,
|
|
MASTER_DENOMINATION_KEY_VALIDITY = 1025,
|
|
WALLET_COIN_MELT = 1202,
|
|
TEST = 4242,
|
|
MERCHANT_PAYMENT_OK = 1104,
|
|
MASTER_WIRE_FEES = 1028,
|
|
WALLET_COIN_PAYBACK = 1203,
|
|
}
|
|
|
|
|
|
/**
|
|
* Desired quality levels for random numbers.
|
|
*/
|
|
export enum RandomQuality {
|
|
WEAK = 0,
|
|
STRONG = 1,
|
|
NONCE = 2,
|
|
}
|
|
|
|
|
|
/**
|
|
* Object that is allocated in some arena.
|
|
*/
|
|
interface ArenaObject {
|
|
destroy(): void;
|
|
}
|
|
|
|
|
|
/**
|
|
* Context for cummulative hashing.
|
|
*/
|
|
export class HashContext implements ArenaObject {
|
|
private hashContextPtr: number | undefined;
|
|
|
|
constructor() {
|
|
this.hashContextPtr = emscAlloc.hash_context_start();
|
|
}
|
|
|
|
/**
|
|
* Add data to be hashed.
|
|
*/
|
|
read(obj: PackedArenaObject): void {
|
|
if (!this.hashContextPtr) {
|
|
throw Error("assertion failed");
|
|
}
|
|
emsc.hash_context_read(this.hashContextPtr, obj.nativePtr, obj.size());
|
|
}
|
|
|
|
/**
|
|
* Finish the hash computation.
|
|
*/
|
|
finish(h: HashCode) {
|
|
if (!this.hashContextPtr) {
|
|
throw Error("assertion failed");
|
|
}
|
|
h.alloc();
|
|
emsc.hash_context_finish(this.hashContextPtr, h.nativePtr);
|
|
}
|
|
|
|
/**
|
|
* Abort hashing without computing the result.
|
|
*/
|
|
destroy(): void {
|
|
if (this.hashContextPtr) {
|
|
emsc.hash_context_abort(this.hashContextPtr);
|
|
}
|
|
this.hashContextPtr = undefined;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Arena object that points to an allocaed block of memory.
|
|
*/
|
|
abstract class MallocArenaObject implements ArenaObject {
|
|
protected _nativePtr: number | undefined = undefined;
|
|
|
|
/**
|
|
* Is this a weak reference to the underlying memory?
|
|
*/
|
|
isWeak = false;
|
|
|
|
destroy(): void {
|
|
if (this._nativePtr && !this.isWeak) {
|
|
emsc.free(this.nativePtr);
|
|
this._nativePtr = undefined;
|
|
}
|
|
}
|
|
|
|
constructor(arena?: Arena) {
|
|
if (!arena) {
|
|
if (arenaStack.length === 0) {
|
|
throw Error("No arena available");
|
|
}
|
|
arena = arenaStack[arenaStack.length - 1];
|
|
}
|
|
arena.put(this);
|
|
}
|
|
|
|
alloc(size: number) {
|
|
if (this._nativePtr !== undefined) {
|
|
throw Error("Double allocation");
|
|
}
|
|
this.nativePtr = emscAlloc.malloc(size);
|
|
}
|
|
|
|
set nativePtr(v: number) {
|
|
if (v === undefined) {
|
|
throw Error("Native pointer must be a number or null");
|
|
}
|
|
this._nativePtr = v;
|
|
}
|
|
|
|
get nativePtr() {
|
|
// We want to allow latent allocation
|
|
// of native wrappers, but we never want to
|
|
// pass 'undefined' to emscripten.
|
|
if (this._nativePtr === undefined) {
|
|
throw Error("Native pointer not initialized");
|
|
}
|
|
return this._nativePtr;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* An arena stores objects that will be deallocated
|
|
* at the same time.
|
|
*/
|
|
interface Arena {
|
|
put(obj: ArenaObject): void;
|
|
destroy(): void;
|
|
}
|
|
|
|
|
|
/**
|
|
* Arena that must be manually destroyed.
|
|
*/
|
|
class SimpleArena implements Arena {
|
|
protected heap: ArenaObject[];
|
|
|
|
constructor() {
|
|
this.heap = [];
|
|
}
|
|
|
|
put(obj: ArenaObject) {
|
|
this.heap.push(obj);
|
|
}
|
|
|
|
destroy() {
|
|
for (const obj of this.heap) {
|
|
obj.destroy();
|
|
}
|
|
this.heap = [];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Arena that destroys all its objects once control has returned to the message
|
|
* loop.
|
|
*/
|
|
class SyncArena extends SimpleArena {
|
|
private isScheduled: boolean;
|
|
|
|
constructor() {
|
|
super();
|
|
}
|
|
|
|
pub(obj: MallocArenaObject) {
|
|
super.put(obj);
|
|
if (!this.isScheduled) {
|
|
this.schedule();
|
|
}
|
|
this.heap.push(obj);
|
|
}
|
|
|
|
private schedule() {
|
|
this.isScheduled = true;
|
|
Promise.resolve().then(() => {
|
|
this.isScheduled = false;
|
|
this.destroy();
|
|
});
|
|
}
|
|
}
|
|
|
|
const arenaStack: Arena[] = [];
|
|
arenaStack.push(new SyncArena());
|
|
|
|
|
|
/**
|
|
* Representation of monetary value in a given currency.
|
|
*/
|
|
export class Amount extends MallocArenaObject {
|
|
constructor(args?: AmountJson, arena?: Arena) {
|
|
super(arena);
|
|
if (args) {
|
|
this.nativePtr = emscAlloc.get_amount(args.value,
|
|
0,
|
|
args.fraction,
|
|
args.currency);
|
|
} else {
|
|
this.nativePtr = emscAlloc.get_amount(0, 0, 0, "");
|
|
}
|
|
}
|
|
|
|
static getZero(currency: string, a?: Arena): Amount {
|
|
const am = new Amount(undefined, a);
|
|
const r = emsc.amount_get_zero(currency, am.nativePtr);
|
|
if (r !== GNUNET_OK) {
|
|
throw Error("invalid currency");
|
|
}
|
|
return am;
|
|
}
|
|
|
|
|
|
toNbo(a?: Arena): AmountNbo {
|
|
const x = new AmountNbo(a);
|
|
x.alloc();
|
|
emsc.amount_hton(x.nativePtr, this.nativePtr);
|
|
return x;
|
|
}
|
|
|
|
fromNbo(nbo: AmountNbo): void {
|
|
emsc.amount_ntoh(this.nativePtr, nbo.nativePtr);
|
|
}
|
|
|
|
get value() {
|
|
return emsc.get_value(this.nativePtr);
|
|
}
|
|
|
|
get fraction() {
|
|
return emsc.get_fraction(this.nativePtr);
|
|
}
|
|
|
|
get currency(): string {
|
|
return emsc.get_currency(this.nativePtr);
|
|
}
|
|
|
|
toJson(): AmountJson {
|
|
return {
|
|
currency: emsc.get_currency(this.nativePtr),
|
|
fraction: emsc.get_fraction(this.nativePtr),
|
|
value: emsc.get_value(this.nativePtr),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Add an amount to this amount.
|
|
*/
|
|
add(a: Amount) {
|
|
const res = emsc.amount_add(this.nativePtr, a.nativePtr, this.nativePtr);
|
|
if (res < 1) {
|
|
// Overflow
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Perform saturating subtraction on amounts.
|
|
*/
|
|
sub(a: Amount) {
|
|
// this = this - a
|
|
const res = emsc.amount_subtract(this.nativePtr, this.nativePtr, a.nativePtr);
|
|
if (res === 0) {
|
|
// Underflow
|
|
return false;
|
|
}
|
|
if (res > 0) {
|
|
return true;
|
|
}
|
|
throw Error("Incompatible currencies");
|
|
}
|
|
|
|
cmp(a: Amount) {
|
|
// If we don't check this, the c code aborts.
|
|
if (this.currency !== a.currency) {
|
|
throw Error(`incomparable currencies (${this.currency} and ${a.currency})`);
|
|
}
|
|
return emsc.amount_cmp(this.nativePtr, a.nativePtr);
|
|
}
|
|
|
|
normalize() {
|
|
emsc.amount_normalize(this.nativePtr);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Count the UTF-8 characters in a JavaScript string.
|
|
*/
|
|
function countUtf8Bytes(str: string): number {
|
|
let s = str.length;
|
|
// JavaScript strings are UTF-16 arrays
|
|
for (let i = str.length - 1; i >= 0; i--) {
|
|
const code = str.charCodeAt(i);
|
|
if (code > 0x7f && code <= 0x7ff) {
|
|
// We need an extra byte in utf-8 here
|
|
s++;
|
|
} else if (code > 0x7ff && code <= 0xffff) {
|
|
// We need two extra bytes in utf-8 here
|
|
s += 2;
|
|
}
|
|
// Skip over the other surrogate
|
|
if (code >= 0xDC00 && code <= 0xDFFF) {
|
|
i--;
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
|
|
/**
|
|
* Managed reference to a contiguous block of memory in the Emscripten heap.
|
|
* Can be converted from / to a serialized representation.
|
|
* Should contain only data, not pointers.
|
|
*/
|
|
abstract class PackedArenaObject extends MallocArenaObject {
|
|
abstract size(): number;
|
|
|
|
constructor(a?: Arena) {
|
|
super(a);
|
|
}
|
|
|
|
randomize(qual: RandomQuality = RandomQuality.STRONG): void {
|
|
emsc.random_block(qual, this.nativePtr, this.size());
|
|
}
|
|
|
|
toCrock(): string {
|
|
const d = emscAlloc.data_to_string_alloc(this.nativePtr, this.size());
|
|
const s = emscLib.Pointer_stringify(d);
|
|
emsc.free(d);
|
|
return s;
|
|
}
|
|
|
|
toJson(): any {
|
|
// Per default, the json encoding of
|
|
// packed arena objects is just the crockford encoding.
|
|
// Subclasses typically want to override this.
|
|
return this.toCrock();
|
|
}
|
|
|
|
loadCrock(s: string) {
|
|
this.alloc();
|
|
// We need to get the javascript string
|
|
// to the emscripten heap first.
|
|
const buf = ByteArray.fromStringWithNull(s);
|
|
const res = emsc.string_to_data(buf.nativePtr,
|
|
s.length,
|
|
this.nativePtr,
|
|
this.size());
|
|
buf.destroy();
|
|
if (res < 1) {
|
|
throw {error: "wrong encoding"};
|
|
}
|
|
}
|
|
|
|
alloc() {
|
|
// FIXME: should the client be allowed to call alloc multiple times?
|
|
if (!this._nativePtr) {
|
|
this.nativePtr = emscAlloc.malloc(this.size());
|
|
}
|
|
}
|
|
|
|
hash(): HashCode {
|
|
const x = new HashCode();
|
|
x.alloc();
|
|
emsc.hash(this.nativePtr, this.size(), x.nativePtr);
|
|
return x;
|
|
}
|
|
|
|
hexdump() {
|
|
const bytes: string[] = [];
|
|
for (let i = 0; i < this.size(); i++) {
|
|
let b = emscLib.getValue(this.nativePtr + i, "i8");
|
|
b = (b + 256) % 256;
|
|
bytes.push("0".concat(b.toString(16)).slice(-2));
|
|
}
|
|
const lines: string[] = [];
|
|
for (let i = 0; i < bytes.length; i += 8) {
|
|
lines.push(bytes.slice(i, i + 8).join(","));
|
|
}
|
|
return lines.join("\n");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Amount, encoded for network transmission.
|
|
*/
|
|
export class AmountNbo extends PackedArenaObject {
|
|
size() {
|
|
return 24;
|
|
}
|
|
|
|
toJson(): any {
|
|
const a = new SimpleArena();
|
|
const am = new Amount(undefined, a);
|
|
am.fromNbo(this);
|
|
const json = am.toJson();
|
|
a.destroy();
|
|
return json;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a packed arena object from the base32 crockford encoding.
|
|
*/
|
|
function fromCrock<T extends PackedArenaObject>(s: string, ctor: Ctor<T>): T {
|
|
const x: T = new ctor();
|
|
x.alloc();
|
|
x.loadCrock(s);
|
|
return x;
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a packed arena object from the base32 crockford encoding for objects
|
|
* that have a special decoding function.
|
|
*/
|
|
function fromCrockDecoded<T extends MallocArenaObject>(s: string,
|
|
ctor: Ctor<T>,
|
|
decodeFn: (p: number, s: number) => number): T {
|
|
const obj = new ctor();
|
|
const buf = ByteArray.fromCrock(s);
|
|
obj.nativePtr = decodeFn(buf.nativePtr, buf.size());
|
|
buf.destroy();
|
|
return obj;
|
|
}
|
|
|
|
|
|
/**
|
|
* Encode an object using a special encoding function.
|
|
*/
|
|
function encode<T extends MallocArenaObject>(obj: T, encodeFn: any, arena?: Arena): ByteArray {
|
|
const ptr = emscAlloc.malloc(PTR_SIZE);
|
|
const len = encodeFn(obj.nativePtr, ptr);
|
|
const res = new ByteArray(len, undefined, arena);
|
|
res.nativePtr = emscLib.getValue(ptr, "*");
|
|
emsc.free(ptr);
|
|
return res;
|
|
}
|
|
|
|
|
|
/**
|
|
* Private EdDSA key.
|
|
*/
|
|
export class EddsaPrivateKey extends PackedArenaObject {
|
|
static create(a?: Arena): EddsaPrivateKey {
|
|
const obj = new EddsaPrivateKey(a);
|
|
obj.nativePtr = emscAlloc.eddsa_key_create();
|
|
return obj;
|
|
}
|
|
|
|
size() {
|
|
return 32;
|
|
}
|
|
|
|
getPublicKey(a?: Arena): EddsaPublicKey {
|
|
const obj = new EddsaPublicKey(a);
|
|
obj.nativePtr = emscAlloc.eddsa_public_key_from_private(this.nativePtr);
|
|
return obj;
|
|
}
|
|
|
|
static fromCrock(s: string): EddsaPrivateKey {
|
|
return fromCrock(s, this);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to an EdDSA private key.
|
|
*/
|
|
export class EcdsaPrivateKey extends PackedArenaObject {
|
|
static create(a?: Arena): EcdsaPrivateKey {
|
|
const obj = new EcdsaPrivateKey(a);
|
|
obj.nativePtr = emscAlloc.ecdsa_key_create();
|
|
return obj;
|
|
}
|
|
|
|
size() {
|
|
return 32;
|
|
}
|
|
|
|
getPublicKey(a?: Arena): EcdsaPublicKey {
|
|
const obj = new EcdsaPublicKey(a);
|
|
obj.nativePtr = emscAlloc.ecdsa_public_key_from_private(this.nativePtr);
|
|
return obj;
|
|
}
|
|
|
|
static fromCrock(s: string): EcdsaPrivateKey {
|
|
return fromCrock(s, this);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to an ECDHE private key.
|
|
*/
|
|
export class EcdhePrivateKey extends PackedArenaObject {
|
|
static create(a?: Arena): EcdhePrivateKey {
|
|
const obj = new EcdhePrivateKey(a);
|
|
obj.nativePtr = emscAlloc.ecdhe_key_create();
|
|
return obj;
|
|
}
|
|
|
|
size() {
|
|
return 32;
|
|
}
|
|
|
|
getPublicKey(a?: Arena): EcdhePublicKey {
|
|
const obj = new EcdhePublicKey(a);
|
|
obj.nativePtr = emscAlloc.ecdhe_public_key_from_private(this.nativePtr);
|
|
return obj;
|
|
}
|
|
|
|
static fromCrock(s: string): EcdhePrivateKey {
|
|
return fromCrock(s, this);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Constructor for a given type.
|
|
*/
|
|
interface Ctor<T> {
|
|
new(): T;
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to an EdDSA public key.
|
|
*/
|
|
export class EddsaPublicKey extends PackedArenaObject {
|
|
size() {
|
|
return 32;
|
|
}
|
|
|
|
static fromCrock(s: string): EddsaPublicKey {
|
|
return fromCrock(s, this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Low-level handle to an ECDSA public key.
|
|
*/
|
|
export class EcdsaPublicKey extends PackedArenaObject {
|
|
size() {
|
|
return 32;
|
|
}
|
|
|
|
static fromCrock(s: string): EcdsaPublicKey {
|
|
return fromCrock(s, this);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to an ECDHE public key.
|
|
*/
|
|
export class EcdhePublicKey extends PackedArenaObject {
|
|
size() {
|
|
return 32;
|
|
}
|
|
|
|
static fromCrock(s: string): EcdhePublicKey {
|
|
return fromCrock(s, this);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to a blinding key secret.
|
|
*/
|
|
export class RsaBlindingKeySecret extends PackedArenaObject {
|
|
size() {
|
|
return 32;
|
|
}
|
|
|
|
/**
|
|
* Create a random blinding key secret.
|
|
*/
|
|
static create(a?: Arena): RsaBlindingKeySecret {
|
|
const o = new RsaBlindingKeySecret(a);
|
|
o.alloc();
|
|
o.randomize();
|
|
return o;
|
|
}
|
|
|
|
static fromCrock(s: string): RsaBlindingKeySecret {
|
|
return fromCrock(s, this);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to a hash code.
|
|
*/
|
|
export class HashCode extends PackedArenaObject {
|
|
size() {
|
|
return 64;
|
|
}
|
|
|
|
static fromCrock(s: string): HashCode {
|
|
return fromCrock(s, this);
|
|
}
|
|
|
|
random(qual: RandomQuality = RandomQuality.STRONG) {
|
|
this.alloc();
|
|
emsc.hash_create_random(qual, this.nativePtr);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to a byte array.
|
|
*/
|
|
export class ByteArray extends PackedArenaObject {
|
|
private allocatedSize: number;
|
|
|
|
size() {
|
|
return this.allocatedSize;
|
|
}
|
|
|
|
constructor(desiredSize: number, init?: number, a?: Arena) {
|
|
super(a);
|
|
if (init === undefined) {
|
|
this.nativePtr = emscAlloc.malloc(desiredSize);
|
|
} else {
|
|
this.nativePtr = init;
|
|
}
|
|
this.allocatedSize = desiredSize;
|
|
}
|
|
|
|
static fromStringWithoutNull(s: string, a?: Arena): ByteArray {
|
|
// UTF-8 bytes, including 0-terminator
|
|
const terminatedByteLength = countUtf8Bytes(s) + 1;
|
|
const hstr = emscAlloc.malloc(terminatedByteLength);
|
|
emscLib.stringToUTF8(s, hstr, terminatedByteLength);
|
|
return new ByteArray(terminatedByteLength - 1, hstr, a);
|
|
}
|
|
|
|
static fromStringWithNull(s: string, a?: Arena): ByteArray {
|
|
// UTF-8 bytes, including 0-terminator
|
|
const terminatedByteLength = countUtf8Bytes(s) + 1;
|
|
const hstr = emscAlloc.malloc(terminatedByteLength);
|
|
emscLib.stringToUTF8(s, hstr, terminatedByteLength);
|
|
return new ByteArray(terminatedByteLength, hstr, a);
|
|
}
|
|
|
|
static fromCrock(s: string, a?: Arena): ByteArray {
|
|
// this one is a bit more complicated than the other fromCrock functions,
|
|
// since we don't have a fixed size
|
|
const byteLength = countUtf8Bytes(s);
|
|
const hstr = emscAlloc.malloc(byteLength + 1);
|
|
emscLib.stringToUTF8(s, hstr, byteLength + 1);
|
|
const decodedLen = Math.floor((byteLength * 5) / 8);
|
|
const ba = new ByteArray(decodedLen, undefined, a);
|
|
const res = emsc.string_to_data(hstr, byteLength, ba.nativePtr, decodedLen);
|
|
emsc.free(hstr);
|
|
if (res !== GNUNET_OK) {
|
|
throw Error("decoding failed");
|
|
}
|
|
return ba;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Data to sign, together with a header that includes a purpose id
|
|
* and size.
|
|
*/
|
|
export class EccSignaturePurpose extends PackedArenaObject {
|
|
size() {
|
|
return this.payloadSize + 8;
|
|
}
|
|
|
|
private payloadSize: number;
|
|
|
|
constructor(purpose: SignaturePurpose,
|
|
payload: PackedArenaObject,
|
|
a?: Arena) {
|
|
super(a);
|
|
this.nativePtr = emscAlloc.purpose_create(purpose,
|
|
payload.nativePtr,
|
|
payload.size());
|
|
this.payloadSize = payload.size();
|
|
}
|
|
}
|
|
|
|
|
|
abstract class SignatureStruct {
|
|
abstract fieldTypes(): any[];
|
|
|
|
abstract purpose(): SignaturePurpose;
|
|
|
|
private members: any = {};
|
|
|
|
constructor(x: { [name: string]: any }) {
|
|
for (const k in x) {
|
|
this.set(k, x[k]);
|
|
}
|
|
}
|
|
|
|
toPurpose(a?: Arena): EccSignaturePurpose {
|
|
let totalSize = 0;
|
|
for (const f of this.fieldTypes()) {
|
|
const name = f[0];
|
|
const member = this.members[name];
|
|
if (!member) {
|
|
throw Error(`Member ${name} not set`);
|
|
}
|
|
totalSize += member.size();
|
|
}
|
|
|
|
const buf = emscAlloc.malloc(totalSize);
|
|
let ptr = buf;
|
|
for (const f of this.fieldTypes()) {
|
|
const name = f[0];
|
|
const member = this.members[name];
|
|
const size = member.size();
|
|
emsc.memmove(ptr, member.nativePtr, size);
|
|
ptr += size;
|
|
}
|
|
const ba = new ByteArray(totalSize, buf, a);
|
|
return new EccSignaturePurpose(this.purpose(), ba);
|
|
}
|
|
|
|
|
|
toJson() {
|
|
const res: any = {};
|
|
for (const f of this.fieldTypes()) {
|
|
const name = f[0];
|
|
const member = this.members[name];
|
|
if (!member) {
|
|
throw Error(`Member ${name} not set`);
|
|
}
|
|
res[name] = member.toJson();
|
|
}
|
|
res.purpose = this.purpose();
|
|
return res;
|
|
}
|
|
|
|
protected set(name: string, value: PackedArenaObject) {
|
|
const typemap: any = {};
|
|
for (const f of this.fieldTypes()) {
|
|
typemap[f[0]] = f[1];
|
|
}
|
|
if (!(name in typemap)) {
|
|
throw Error(`Key ${name} not found`);
|
|
}
|
|
if (!(value instanceof typemap[name])) {
|
|
throw Error("Wrong type for ${name}");
|
|
}
|
|
this.members[name] = value;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Arguments to constructor of [[WithdrawRequestPS]].
|
|
*/
|
|
export interface WithdrawRequestPS_Args {
|
|
/**
|
|
* Reserve public key.
|
|
*/
|
|
reserve_pub: EddsaPublicKey;
|
|
/**
|
|
* Amount with fee.
|
|
*/
|
|
amount_with_fee: AmountNbo;
|
|
/**
|
|
* Withdraw fee.
|
|
*/
|
|
withdraw_fee: AmountNbo;
|
|
/**
|
|
* Hash of denomination public key.
|
|
*/
|
|
h_denomination_pub: HashCode;
|
|
/**
|
|
* Hash of coin envelope.
|
|
*/
|
|
h_coin_envelope: HashCode;
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to a WithdrawRequest signature structure.
|
|
*/
|
|
export class WithdrawRequestPS extends SignatureStruct {
|
|
constructor(w: WithdrawRequestPS_Args) {
|
|
super(w);
|
|
}
|
|
|
|
purpose() {
|
|
return SignaturePurpose.RESERVE_WITHDRAW;
|
|
}
|
|
|
|
fieldTypes() {
|
|
return [
|
|
["reserve_pub", EddsaPublicKey],
|
|
["amount_with_fee", AmountNbo],
|
|
["withdraw_fee", AmountNbo],
|
|
["h_denomination_pub", HashCode],
|
|
["h_coin_envelope", HashCode],
|
|
];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Arguments for constructor or [[PaybackRequestPS]].
|
|
*/
|
|
export interface PaybackRequestPS_args {
|
|
coin_pub: EddsaPublicKey;
|
|
h_denom_pub: HashCode;
|
|
coin_blind: RsaBlindingKeySecret;
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to a PaybackRequest signature structure.
|
|
*/
|
|
export class PaybackRequestPS extends SignatureStruct {
|
|
constructor(w: PaybackRequestPS_args) {
|
|
super(w);
|
|
}
|
|
|
|
purpose() {
|
|
return SignaturePurpose.WALLET_COIN_PAYBACK;
|
|
}
|
|
|
|
fieldTypes() {
|
|
return [
|
|
["coin_pub", EddsaPublicKey],
|
|
["h_denom_pub", HashCode],
|
|
["coin_blind", RsaBlindingKeySecret],
|
|
];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Arguments for constructor of [[RefreshMeltCoinAffirmationPS]].
|
|
*/
|
|
interface RefreshMeltCoinAffirmationPS_Args {
|
|
session_hash: HashCode;
|
|
amount_with_fee: AmountNbo;
|
|
melt_fee: AmountNbo;
|
|
coin_pub: EddsaPublicKey;
|
|
}
|
|
|
|
/**
|
|
* Low-level handle to a RefreshMeltCoinAffirmationPS signature structure.
|
|
*/
|
|
export class RefreshMeltCoinAffirmationPS extends SignatureStruct {
|
|
constructor(w: RefreshMeltCoinAffirmationPS_Args) {
|
|
super(w);
|
|
}
|
|
|
|
purpose() {
|
|
return SignaturePurpose.WALLET_COIN_MELT;
|
|
}
|
|
|
|
fieldTypes() {
|
|
return [
|
|
["session_hash", HashCode],
|
|
["amount_with_fee", AmountNbo],
|
|
["melt_fee", AmountNbo],
|
|
["coin_pub", EddsaPublicKey],
|
|
];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Arguments for constructor of [[MasterWireFeePS]].
|
|
*/
|
|
interface MasterWireFeePS_Args {
|
|
/**
|
|
* Hash of wire method.
|
|
*/
|
|
h_wire_method: HashCode;
|
|
/**
|
|
* Start date.
|
|
*/
|
|
start_date: AbsoluteTimeNbo;
|
|
/**
|
|
* End date.
|
|
*/
|
|
end_date: AbsoluteTimeNbo;
|
|
/**
|
|
* Wire fee.
|
|
*/
|
|
wire_fee: AmountNbo;
|
|
/**
|
|
* Closing fee.
|
|
*/
|
|
closing_fee: AmountNbo;
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to a structure being signed over.
|
|
*/
|
|
export class MasterWireFeePS extends SignatureStruct {
|
|
constructor(w: MasterWireFeePS_Args) {
|
|
super(w);
|
|
}
|
|
|
|
purpose() {
|
|
return SignaturePurpose.MASTER_WIRE_FEES;
|
|
}
|
|
|
|
fieldTypes() {
|
|
return [
|
|
["h_wire_method", HashCode],
|
|
["start_date", AbsoluteTimeNbo],
|
|
["end_date", AbsoluteTimeNbo],
|
|
["wire_fee", AmountNbo],
|
|
["closing_fee", AmountNbo],
|
|
];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to an absolute time in network byte order (NBO).
|
|
*/
|
|
export class AbsoluteTimeNbo extends PackedArenaObject {
|
|
static fromTalerString(s: string): AbsoluteTimeNbo {
|
|
const x = new AbsoluteTimeNbo();
|
|
x.alloc();
|
|
const r = /Date\(([0-9]+)\)/;
|
|
const m = r.exec(s);
|
|
if (!m || m.length !== 2) {
|
|
throw Error();
|
|
}
|
|
const n = parseInt(m[1], 10) * 1000000;
|
|
// XXX: This only works up to 54 bit numbers.
|
|
set64(x.nativePtr, n);
|
|
return x;
|
|
}
|
|
|
|
static fromStampSeconds(stamp: number): AbsoluteTimeNbo {
|
|
const x = new AbsoluteTimeNbo();
|
|
x.alloc();
|
|
// XXX: This only works up to 54 bit numbers.
|
|
set64(x.nativePtr, stamp * 1000000);
|
|
return x;
|
|
}
|
|
|
|
|
|
size() {
|
|
return 8;
|
|
}
|
|
}
|
|
|
|
|
|
// XXX: This only works up to 54 bit numbers.
|
|
function set64(p: number, n: number) {
|
|
for (let i = 0; i < 8; ++i) {
|
|
emscLib.setValue(p + (7 - i), n & 0xFF, "i8");
|
|
n = Math.floor(n / 256);
|
|
}
|
|
}
|
|
|
|
// XXX: This only works up to 54 bit numbers.
|
|
function set32(p: number, n: number) {
|
|
for (let i = 0; i < 4; ++i) {
|
|
emscLib.setValue(p + (3 - i), n & 0xFF, "i8");
|
|
n = Math.floor(n / 256);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to an unsigned 64-bit value.
|
|
*/
|
|
export class UInt64 extends PackedArenaObject {
|
|
static fromNumber(n: number): UInt64 {
|
|
const x = new UInt64();
|
|
x.alloc();
|
|
set64(x.nativePtr, n);
|
|
return x;
|
|
}
|
|
|
|
size() {
|
|
return 8;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to an unsigned 32-bit value.
|
|
*/
|
|
export class UInt32 extends PackedArenaObject {
|
|
static fromNumber(n: number): UInt32 {
|
|
const x = new UInt32();
|
|
x.alloc();
|
|
set32(x.nativePtr, n);
|
|
return x;
|
|
}
|
|
|
|
size() {
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Argument to the constructor of [[DepositRequestPS]].
|
|
*/
|
|
export interface DepositRequestPS_Args {
|
|
/**
|
|
* Contract hash.
|
|
*/
|
|
h_contract: HashCode;
|
|
/**
|
|
* Wire info hash.
|
|
*/
|
|
h_wire: HashCode;
|
|
/**
|
|
* Timestamp.
|
|
*/
|
|
timestamp: AbsoluteTimeNbo;
|
|
/**
|
|
* Refund deadline.
|
|
*/
|
|
refund_deadline: AbsoluteTimeNbo;
|
|
/**
|
|
* Amount with fee.
|
|
*/
|
|
amount_with_fee: AmountNbo;
|
|
/**
|
|
* Deposit fee.
|
|
*/
|
|
deposit_fee: AmountNbo;
|
|
/**
|
|
* Merchant public key.
|
|
*/
|
|
merchant: EddsaPublicKey;
|
|
/**
|
|
* Public key of the coin being deposited.
|
|
*/
|
|
coin_pub: EddsaPublicKey;
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to a struct being signed over.
|
|
*/
|
|
export class DepositRequestPS extends SignatureStruct {
|
|
constructor(w: DepositRequestPS_Args) {
|
|
super(w);
|
|
}
|
|
|
|
purpose() {
|
|
return SignaturePurpose.WALLET_COIN_DEPOSIT;
|
|
}
|
|
|
|
fieldTypes() {
|
|
return [
|
|
["h_contract", HashCode],
|
|
["h_wire", HashCode],
|
|
["timestamp", AbsoluteTimeNbo],
|
|
["refund_deadline", AbsoluteTimeNbo],
|
|
["amount_with_fee", AmountNbo],
|
|
["deposit_fee", AmountNbo],
|
|
["merchant", EddsaPublicKey],
|
|
["coin_pub", EddsaPublicKey],
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Arguments for constuctor of [[DenominationKeyValidityPS]].
|
|
*/
|
|
export interface DenominationKeyValidityPS_args {
|
|
master: EddsaPublicKey;
|
|
start: AbsoluteTimeNbo;
|
|
expire_withdraw: AbsoluteTimeNbo;
|
|
expire_spend: AbsoluteTimeNbo;
|
|
expire_legal: AbsoluteTimeNbo;
|
|
value: AmountNbo;
|
|
fee_withdraw: AmountNbo;
|
|
fee_deposit: AmountNbo;
|
|
fee_refresh: AmountNbo;
|
|
fee_refund: AmountNbo;
|
|
denom_hash: HashCode;
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to a structure being signed over.
|
|
*/
|
|
export class DenominationKeyValidityPS extends SignatureStruct {
|
|
constructor(w: DenominationKeyValidityPS_args) {
|
|
super(w);
|
|
}
|
|
|
|
purpose() {
|
|
return SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY;
|
|
}
|
|
|
|
fieldTypes() {
|
|
return [
|
|
["master", EddsaPublicKey],
|
|
["start", AbsoluteTimeNbo],
|
|
["expire_withdraw", AbsoluteTimeNbo],
|
|
["expire_spend", AbsoluteTimeNbo],
|
|
["expire_legal", AbsoluteTimeNbo],
|
|
["value", AmountNbo],
|
|
["fee_withdraw", AmountNbo],
|
|
["fee_deposit", AmountNbo],
|
|
["fee_refresh", AmountNbo],
|
|
["fee_refund", AmountNbo],
|
|
["denom_hash", HashCode],
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Arguments to constructor of [[PaymentSignaturePS]].
|
|
*/
|
|
export interface PaymentSignaturePS_args {
|
|
/**
|
|
* Contract hash.
|
|
*/
|
|
contract_hash: HashCode;
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to a structure being signed over.
|
|
*/
|
|
export class PaymentSignaturePS extends SignatureStruct {
|
|
constructor(w: PaymentSignaturePS_args) {
|
|
super(w);
|
|
}
|
|
|
|
purpose() {
|
|
return SignaturePurpose.MERCHANT_PAYMENT_OK;
|
|
}
|
|
|
|
fieldTypes() {
|
|
return [
|
|
["contract_hash", HashCode],
|
|
];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to an RsaPublicKey.
|
|
*/
|
|
export class RsaPublicKey extends MallocArenaObject {
|
|
static fromCrock(s: string): RsaPublicKey {
|
|
return fromCrockDecoded(s, this, emscAlloc.rsa_public_key_decode);
|
|
}
|
|
|
|
toCrock() {
|
|
return this.encode().toCrock();
|
|
}
|
|
|
|
destroy() {
|
|
emsc.rsa_public_key_free(this.nativePtr);
|
|
this.nativePtr = 0;
|
|
}
|
|
|
|
encode(arena?: Arena): ByteArray {
|
|
return encode(this, emscAlloc.rsa_public_key_encode);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to an EddsaSignature.
|
|
*/
|
|
export class EddsaSignature extends PackedArenaObject {
|
|
size() {
|
|
return 64;
|
|
}
|
|
static fromCrock(s: string): EddsaSignature {
|
|
return fromCrock(s, this);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Low-level handle to an RsaSignature.
|
|
*/
|
|
export class RsaSignature extends MallocArenaObject {
|
|
static fromCrock(s: string, a?: Arena) {
|
|
return fromCrockDecoded(s, this, emscAlloc.rsa_signature_decode);
|
|
}
|
|
|
|
encode(arena?: Arena): ByteArray {
|
|
return encode(this, emscAlloc.rsa_signature_encode);
|
|
}
|
|
|
|
destroy() {
|
|
emsc.rsa_signature_free(this.nativePtr);
|
|
this.nativePtr = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Blind a value so it can be blindly signed.
|
|
*/
|
|
export function rsaBlind(hashCode: HashCode,
|
|
blindingKey: RsaBlindingKeySecret,
|
|
pkey: RsaPublicKey,
|
|
arena?: Arena): ByteArray|null {
|
|
const buf_ptr_out = emscAlloc.malloc(PTR_SIZE);
|
|
const buf_size_out = emscAlloc.malloc(PTR_SIZE);
|
|
const res = emscAlloc.rsa_blind(hashCode.nativePtr,
|
|
blindingKey.nativePtr,
|
|
pkey.nativePtr,
|
|
buf_ptr_out,
|
|
buf_size_out);
|
|
const buf_ptr = emscLib.getValue(buf_ptr_out, "*");
|
|
const buf_size = emscLib.getValue(buf_size_out, "*");
|
|
emsc.free(buf_ptr_out);
|
|
emsc.free(buf_size_out);
|
|
if (res !== GNUNET_OK) {
|
|
// malicious key
|
|
return null;
|
|
}
|
|
return new ByteArray(buf_size, buf_ptr, arena);
|
|
}
|
|
|
|
|
|
/**
|
|
* Sign data using EdDSA.
|
|
*/
|
|
export function eddsaSign(purpose: EccSignaturePurpose,
|
|
priv: EddsaPrivateKey,
|
|
a?: Arena): EddsaSignature {
|
|
const sig = new EddsaSignature(a);
|
|
sig.alloc();
|
|
const res = emsc.eddsa_sign(priv.nativePtr, purpose.nativePtr, sig.nativePtr);
|
|
if (res < 1) {
|
|
throw Error("EdDSA signing failed");
|
|
}
|
|
return sig;
|
|
}
|
|
|
|
|
|
/**
|
|
* Verify EdDSA-signed data.
|
|
*/
|
|
export function eddsaVerify(purposeNum: number,
|
|
verify: EccSignaturePurpose,
|
|
sig: EddsaSignature,
|
|
pub: EddsaPublicKey,
|
|
a?: Arena): boolean {
|
|
const r = emsc.eddsa_verify(purposeNum,
|
|
verify.nativePtr,
|
|
sig.nativePtr,
|
|
pub.nativePtr);
|
|
return r === GNUNET_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* Unblind a blindly signed value.
|
|
*/
|
|
export function rsaUnblind(sig: RsaSignature,
|
|
bk: RsaBlindingKeySecret,
|
|
pk: RsaPublicKey,
|
|
a?: Arena): RsaSignature {
|
|
const x = new RsaSignature(a);
|
|
x.nativePtr = emscAlloc.rsa_unblind(sig.nativePtr,
|
|
bk.nativePtr,
|
|
pk.nativePtr);
|
|
return x;
|
|
}
|
|
|
|
|
|
type TransferSecretP = HashCode;
|
|
|
|
/**
|
|
* A fresh coin generated from a sed.
|
|
*/
|
|
export interface FreshCoin {
|
|
/**
|
|
* The coin's private key.
|
|
*/
|
|
priv: EddsaPrivateKey;
|
|
/**
|
|
* The blinding key to use for withdrawal.
|
|
*/
|
|
blindingKey: RsaBlindingKeySecret;
|
|
}
|
|
|
|
/**
|
|
* Diffie-Hellman operation between an ECDHE private key
|
|
* and an EdDSA public key.
|
|
*/
|
|
export function ecdhEddsa(priv: EcdhePrivateKey,
|
|
pub: EddsaPublicKey): HashCode {
|
|
const h = new HashCode();
|
|
h.alloc();
|
|
const res = emsc.ecdh_eddsa(priv.nativePtr, pub.nativePtr, h.nativePtr);
|
|
if (res !== GNUNET_OK) {
|
|
throw Error("ecdh_eddsa failed");
|
|
}
|
|
return h;
|
|
}
|
|
|
|
|
|
/**
|
|
* Derive a fresh coin from the given seed. Used during refreshing.
|
|
*/
|
|
export function setupFreshCoin(secretSeed: TransferSecretP,
|
|
coinIndex: number): FreshCoin {
|
|
const priv = new EddsaPrivateKey();
|
|
priv.isWeak = true;
|
|
const blindingKey = new RsaBlindingKeySecret();
|
|
blindingKey.isWeak = true;
|
|
const buf = new ByteArray(priv.size() + blindingKey.size());
|
|
|
|
emsc.setup_fresh_coin(secretSeed.nativePtr, coinIndex, buf.nativePtr);
|
|
|
|
priv.nativePtr = buf.nativePtr;
|
|
blindingKey.nativePtr = buf.nativePtr + priv.size();
|
|
|
|
return {priv, blindingKey};
|
|
}
|