document emscriptif.ts and fix mixin oddities

This commit is contained in:
Florian Dold 2016-11-17 22:45:37 +01:00
parent 54e3bd2773
commit 87a8cd0a13

View File

@ -18,9 +18,11 @@ import {AmountJson} from "./types";
import Module, {EmscFunGen} from "src/emscripten/taler-emscripten-lib";
/**
* High-level interface to emscripten-compiled modules used
* Medium-level interface to emscripten-compiled modules used
* by the wallet.
*
* The high-level interface (using WebWorkers) is exposed in src/cryptoApi.ts.
*
* @author Florian Dold
*/
@ -35,6 +37,9 @@ const GNUNET_NO = 0;
const GNUNET_SYSERR = -1;
/**
* Get an emscripten-compiled function.
*/
const getEmsc: EmscFunGen = (name: string, ret: any, argTypes: any[]) => {
return (...args: any[]) => {
return Module.ccall(name, ret, argTypes, args);
@ -126,6 +131,10 @@ const emsc = {
["number", "number", "number"]),
};
/**
* Emscripten functions that allocate memory.
*/
const emscAlloc = {
get_amount: getEmsc("TALER_WRALL_get_amount",
"number",
@ -188,6 +197,9 @@ const emscAlloc = {
};
/**
* Constants for signatures purposes, define what the signatures vouches for.
*/
export enum SignaturePurpose {
RESERVE_WITHDRAW = 1200,
WALLET_COIN_DEPOSIT = 1201,
@ -196,17 +208,28 @@ export enum SignaturePurpose {
TEST = 4242,
}
/**
* 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;
@ -214,6 +237,9 @@ export class HashContext implements ArenaObject {
this.hashContextPtr = emscAlloc.hash_context_start();
}
/**
* Add data to be hashed.
*/
read(obj: PackedArenaObject): void {
if (!this.hashContextPtr) {
throw Error("assertion failed");
@ -221,6 +247,9 @@ export class HashContext implements ArenaObject {
emsc.hash_context_read(this.hashContextPtr, obj.nativePtr, obj.size());
}
/**
* Finish the hash computation.
*/
finish(h: HashCode) {
if (!this.hashContextPtr) {
throw Error("assertion failed");
@ -229,6 +258,9 @@ export class HashContext implements ArenaObject {
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);
@ -238,6 +270,9 @@ export class HashContext implements ArenaObject {
}
/**
* Arena object that points to an allocaed block of memory.
*/
abstract class MallocArenaObject implements ArenaObject {
protected _nativePtr: number | undefined = undefined;
@ -245,7 +280,6 @@ abstract class MallocArenaObject implements ArenaObject {
* Is this a weak reference to the underlying memory?
*/
isWeak = false;
arena: Arena;
destroy(): void {
if (this._nativePtr && !this.isWeak) {
@ -262,7 +296,6 @@ abstract class MallocArenaObject implements ArenaObject {
arena = arenaStack[arenaStack.length - 1];
}
arena.put(this);
this.arena = arena;
}
alloc(size: number) {
@ -291,6 +324,10 @@ abstract class MallocArenaObject implements ArenaObject {
}
/**
* An arena stores objects that will be deallocated
* at the same time.
*/
interface Arena {
put(obj: ArenaObject): void;
destroy(): void;
@ -352,6 +389,9 @@ let 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);
@ -547,6 +587,9 @@ abstract class PackedArenaObject extends MallocArenaObject {
}
/**
* Amount, encoded for network transmission.
*/
export class AmountNbo extends PackedArenaObject {
size() {
return 24;
@ -563,6 +606,46 @@ export class AmountNbo extends PackedArenaObject {
}
/**
* Create a packed arena object from the base32 crockford encoding.
*/
function fromCrock<T extends PackedArenaObject>(s: string, ctor: Ctor<T>): T {
let 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 {
let obj = new ctor();
let 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 {
let ptr = emscAlloc.malloc(PTR_SIZE);
let len = encodeFn(obj.nativePtr, ptr);
let res = new ByteArray(len, undefined, arena);
res.nativePtr = Module.getValue(ptr, '*');
emsc.free(ptr);
return res;
}
/**
* Private EdDSA key.
*/
export class EddsaPrivateKey extends PackedArenaObject {
static create(a?: Arena): EddsaPrivateKey {
let obj = new EddsaPrivateKey(a);
@ -580,9 +663,10 @@ export class EddsaPrivateKey extends PackedArenaObject {
return obj;
}
static fromCrock: (s: string) => EddsaPrivateKey;
static fromCrock(s: string): EddsaPrivateKey {
return fromCrock(s, this);
}
}
mixinStatic(EddsaPrivateKey, fromCrock);
export class EcdsaPrivateKey extends PackedArenaObject {
@ -602,9 +686,10 @@ export class EcdsaPrivateKey extends PackedArenaObject {
return obj;
}
static fromCrock: (s: string) => EcdsaPrivateKey;
static fromCrock(s: string): EcdsaPrivateKey {
return fromCrock(s, this);
}
}
mixinStatic(EcdsaPrivateKey, fromCrock);
export class EcdhePrivateKey extends PackedArenaObject {
@ -624,38 +709,17 @@ export class EcdhePrivateKey extends PackedArenaObject {
return obj;
}
static fromCrock: (s: string) => EcdhePrivateKey;
}
mixinStatic(EcdhePrivateKey, fromCrock);
function fromCrock(s: string) {
let x = new this();
x.alloc();
x.loadCrock(s);
return x;
static fromCrock(s: string): EcdhePrivateKey {
return fromCrock(s, this);
}
}
function mixin(obj: any, method: any, name?: string) {
if (!name) {
name = method.name;
}
if (!name) {
throw Error("Mixin needs a name.");
}
obj.prototype[method.name] = method;
}
function mixinStatic(obj: any, method: any, name?: string) {
if (!name) {
name = method.name;
}
if (!name) {
throw Error("Mixin needs a name.");
}
obj[method.name] = method;
/**
* Constructor for a given type.
*/
interface Ctor<T> {
new(): T
}
@ -664,18 +728,20 @@ export class EddsaPublicKey extends PackedArenaObject {
return 32;
}
static fromCrock: (s: string) => EddsaPublicKey;
static fromCrock(s: string): EddsaPublicKey {
return fromCrock(s, this);
}
}
mixinStatic(EddsaPublicKey, fromCrock);
export class EcdsaPublicKey extends PackedArenaObject {
size() {
return 32;
}
static fromCrock: (s: string) => EcdsaPublicKey;
static fromCrock(s: string): EcdsaPublicKey {
return fromCrock(s, this);
}
}
mixinStatic(EcdsaPublicKey, fromCrock);
export class EcdhePublicKey extends PackedArenaObject {
@ -683,36 +749,9 @@ export class EcdhePublicKey extends PackedArenaObject {
return 32;
}
static fromCrock: (s: string) => EcdhePublicKey;
}
mixinStatic(EcdhePublicKey, fromCrock);
function makeFromCrock(decodeFn: (p: number, s: number) => number) {
function fromCrock(s: string, a?: Arena) {
let obj = new this(a);
let buf = ByteArray.fromCrock(s);
obj.nativePtr = decodeFn(buf.nativePtr, buf.size());
buf.destroy();
return obj;
static fromCrock(s: string): EcdhePublicKey {
return fromCrock(s, this);
}
return fromCrock;
}
function makeToCrock(encodeFn: (po: number,
ps: number) => number): () => string {
function toCrock() {
let ptr = emscAlloc.malloc(PTR_SIZE);
let size = emscAlloc.rsa_blinding_key_encode(this.nativePtr, ptr);
let res = new ByteArray(size, Module.getValue(ptr, '*'));
let s = res.toCrock();
emsc.free(ptr);
res.destroy();
return s;
}
return toCrock;
}
export class RsaBlindingKeySecret extends PackedArenaObject {
@ -730,9 +769,10 @@ export class RsaBlindingKeySecret extends PackedArenaObject {
return o;
}
static fromCrock: (s: string) => RsaBlindingKeySecret;
static fromCrock(s: string): RsaBlindingKeySecret {
return fromCrock(s, this);
}
}
mixinStatic(RsaBlindingKeySecret, fromCrock);
export class HashCode extends PackedArenaObject {
@ -740,14 +780,15 @@ export class HashCode extends PackedArenaObject {
return 64;
}
static fromCrock: (s: string) => HashCode;
static fromCrock(s: string): HashCode {
return fromCrock(s, this);
}
random(qual: RandomQuality = RandomQuality.STRONG) {
this.alloc();
emsc.hash_create_random(qual, this.nativePtr);
}
}
mixinStatic(HashCode, fromCrock);
export class ByteArray extends PackedArenaObject {
@ -784,6 +825,8 @@ export class ByteArray extends PackedArenaObject {
}
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
let byteLength = countUtf8Bytes(s);
let hstr = emscAlloc.malloc(byteLength + 1);
Module.stringToUTF8(s, hstr, byteLength + 1);
@ -799,6 +842,10 @@ export class ByteArray extends PackedArenaObject {
}
/**
* Data to sign, together with a header that includes a purpose id
* and size.
*/
export class EccSignaturePurpose extends PackedArenaObject {
size() {
return this.payloadSize + 8;
@ -1090,27 +1137,11 @@ export class DenominationKeyValidityPS extends SignatureStruct {
}
interface Encodeable {
encode(arena?: Arena): ByteArray;
}
function makeEncode(encodeFn: any) {
function encode(arena?: Arena) {
let ptr = emscAlloc.malloc(PTR_SIZE);
let len = encodeFn(this.nativePtr, ptr);
let res = new ByteArray(len, undefined, arena);
res.nativePtr = Module.getValue(ptr, '*');
emsc.free(ptr);
return res;
export class RsaPublicKey extends MallocArenaObject {
static fromCrock(s: string): RsaPublicKey {
return fromCrockDecoded(s, this, emscAlloc.rsa_public_key_decode);
}
return encode;
}
export class RsaPublicKey extends MallocArenaObject implements Encodeable {
static fromCrock: (s: string, a?: Arena) => RsaPublicKey;
toCrock() {
return this.encode().toCrock();
}
@ -1120,10 +1151,10 @@ export class RsaPublicKey extends MallocArenaObject implements Encodeable {
this.nativePtr = 0;
}
encode: (arena?: Arena) => ByteArray;
encode(arena?: Arena): ByteArray {
return encode(this, emscAlloc.rsa_public_key_encode);
}
}
mixinStatic(RsaPublicKey, makeFromCrock(emscAlloc.rsa_public_key_decode));
mixin(RsaPublicKey, makeEncode(emscAlloc.rsa_public_key_encode));
export class EddsaSignature extends PackedArenaObject {
@ -1133,20 +1164,25 @@ export class EddsaSignature extends PackedArenaObject {
}
export class RsaSignature extends MallocArenaObject implements Encodeable {
static fromCrock: (s: string, a?: Arena) => RsaSignature;
export class RsaSignature extends MallocArenaObject {
static fromCrock(s: string, a?: Arena) {
return fromCrockDecoded(s, this, emscAlloc.rsa_signature_decode);
}
encode: (arena?: Arena) => ByteArray;
encode(arena?: Arena): ByteArray {
return encode(this, emscAlloc.rsa_signature_encode);
}
destroy() {
emsc.rsa_signature_free(this.nativePtr);
this.nativePtr = 0;
}
}
mixinStatic(RsaSignature, makeFromCrock(emscAlloc.rsa_signature_decode));
mixin(RsaSignature, makeEncode(emscAlloc.rsa_signature_encode));
/**
* Blind a value so it can be blindly signed.
*/
export function rsaBlind(hashCode: HashCode,
blindingKey: RsaBlindingKeySecret,
pkey: RsaPublicKey,
@ -1170,6 +1206,9 @@ export function rsaBlind(hashCode: HashCode,
}
/**
* Sign data using EdDSA.
*/
export function eddsaSign(purpose: EccSignaturePurpose,
priv: EddsaPrivateKey,
a?: Arena): EddsaSignature {
@ -1183,6 +1222,9 @@ export function eddsaSign(purpose: EccSignaturePurpose,
}
/**
* Verify EdDSA-signed data.
*/
export function eddsaVerify(purposeNum: number,
verify: EccSignaturePurpose,
sig: EddsaSignature,
@ -1196,6 +1238,9 @@ export function eddsaVerify(purposeNum: number,
}
/**
* Unblind a blindly signed value.
*/
export function rsaUnblind(sig: RsaSignature,
bk: RsaBlindingKeySecret,
pk: RsaPublicKey,
@ -1210,12 +1255,15 @@ export function rsaUnblind(sig: RsaSignature,
type TransferSecretP = HashCode;
export interface FreshCoin {
priv: EddsaPrivateKey;
blindingKey: RsaBlindingKeySecret;
}
/**
* Diffie-Hellman operation between an ECDHE private key
* and an EdDSA public key.
*/
export function ecdhEddsa(priv: EcdhePrivateKey,
pub: EddsaPublicKey): HashCode {
let h = new HashCode();
@ -1227,6 +1275,10 @@ export function ecdhEddsa(priv: EcdhePrivateKey,
return h;
}
/**
* Derive a fresh coin from the given seed. Used during refreshing.
*/
export function setupFreshCoin(secretSeed: TransferSecretP,
coinIndex: number): FreshCoin {
let priv = new EddsaPrivateKey();