aboutsummaryrefslogtreecommitdiff
path: root/src/crypto/talerCrypto.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2019-11-28 00:46:34 +0100
committerFlorian Dold <florian.dold@gmail.com>2019-11-28 00:46:34 +0100
commit706c07fa1d069290992bd31d53b0c89324992f9c (patch)
tree8114c23cda7547f8475c7d8d579f51f968a97f9d /src/crypto/talerCrypto.ts
parentc3ca556affe2f514aeb7fd052fe6d626d9319e99 (diff)
downloadwallet-core-706c07fa1d069290992bd31d53b0c89324992f9c.tar.xz
implement JS-only Taler, remove emscripten
Diffstat (limited to 'src/crypto/talerCrypto.ts')
-rw-r--r--src/crypto/talerCrypto.ts144
1 files changed, 120 insertions, 24 deletions
diff --git a/src/crypto/talerCrypto.ts b/src/crypto/talerCrypto.ts
index 0a36f0fe4..b754b0c57 100644
--- a/src/crypto/talerCrypto.ts
+++ b/src/crypto/talerCrypto.ts
@@ -18,9 +18,9 @@
* Native implementation of GNU Taler crypto.
*/
-import nacl = require("./nacl-fast");
+import nacl = require("./primitives/nacl-fast");
import bigint from "big-integer";
-import { kdf } from "./kdf";
+import { kdf } from "./primitives/kdf";
export function getRandomBytes(n: number): Uint8Array {
return nacl.randomBytes(n);
@@ -123,7 +123,7 @@ export function decodeCrock(encoded: string): Uint8Array {
}
export function eddsaGetPublic(eddsaPriv: Uint8Array): Uint8Array {
- const pair = nacl.sign_keyPair_fromSeed(eddsaPriv);
+ const pair = nacl.sign_keyPair_fromSeed(eddsaPriv);
return pair.publicKey;
}
@@ -131,7 +131,10 @@ export function ecdheGetPublic(ecdhePriv: Uint8Array): Uint8Array {
return nacl.scalarMult_base(ecdhePriv);
}
-export function keyExchangeEddsaEcdhe(eddsaPriv: Uint8Array, ecdhePub: Uint8Array): Uint8Array {
+export function keyExchangeEddsaEcdhe(
+ eddsaPriv: Uint8Array,
+ ecdhePub: Uint8Array,
+): Uint8Array {
const ph = nacl.hash(eddsaPriv);
const a = new Uint8Array(32);
for (let i = 0; i < 32; i++) {
@@ -141,7 +144,10 @@ export function keyExchangeEddsaEcdhe(eddsaPriv: Uint8Array, ecdhePub: Uint8Arra
return nacl.hash(x);
}
-export function keyExchangeEcdheEddsa(ecdhePriv: Uint8Array, eddsaPub: Uint8Array): Uint8Array {
+export function keyExchangeEcdheEddsa(
+ ecdhePriv: Uint8Array,
+ eddsaPub: Uint8Array,
+): Uint8Array {
const curve25519Pub = nacl.sign_ed25519_pk_to_curve25519(eddsaPub);
const x = nacl.scalarMult(ecdhePriv, curve25519Pub);
return nacl.hash(x);
@@ -172,8 +178,8 @@ function kdfMod(
while (true) {
const ctx = new Uint8Array(info.byteLength + 2);
ctx.set(info, 0);
- ctx[ctx.length - 2] = (counter >>> 8) & 0xFF;
- ctx[ctx.length - 1] = counter & 0xFF;
+ ctx[ctx.length - 2] = (counter >>> 8) & 0xff;
+ ctx[ctx.length - 1] = counter & 0xff;
const buf = kdf(buflen, ikm, salt, ctx);
const arr = Array.from(buf);
arr[0] = arr[0] & mask;
@@ -185,7 +191,7 @@ function kdfMod(
}
}
-function stringToBuf(s: string) {
+export function stringToBytes(s: string) {
const te = new TextEncoder();
return te.encode(s);
}
@@ -194,9 +200,12 @@ function loadBigInt(arr: Uint8Array) {
return bigint.fromArray(Array.from(arr), 256, false);
}
-function rsaBlindingKeyDerive(rsaPub: RsaPub, bks: Uint8Array): bigint.BigInteger {
- const salt = stringToBuf("Blinding KDF extrator HMAC key");
- const info = stringToBuf("Blinding KDF");
+function rsaBlindingKeyDerive(
+ rsaPub: RsaPub,
+ bks: Uint8Array,
+): bigint.BigInteger {
+ const salt = stringToBytes("Blinding KDF extrator HMAC key");
+ const info = stringToBytes("Blinding KDF");
return kdfMod(rsaPub.N, bks, salt, info);
}
@@ -206,7 +215,7 @@ function rsaBlindingKeyDerive(rsaPub: RsaPub, bks: Uint8Array): bigint.BigIntege
* Assuming n is an RSA modulous and r is generated using a call to
* GNUNET_CRYPTO_kdf_mod_mpi, if gcd(r,n) != 1 then n must be a
* malicious RSA key designed to deanomize the user.
- *
+ *
* @param r KDF result
* @param n RSA modulus of the public key
*/
@@ -218,7 +227,7 @@ function rsaGcdValidate(r: bigint.BigInteger, n: bigint.BigInteger) {
}
function rsaFullDomainHash(hm: Uint8Array, rsaPub: RsaPub): bigint.BigInteger {
- const info = stringToBuf("RSA-FDA FTpsW!");
+ const info = stringToBytes("RSA-FDA FTpsW!");
const salt = rsaPubEncode(rsaPub);
const r = kdfMod(rsaPub.N, hm, salt, info);
rsaGcdValidate(r, rsaPub.N);
@@ -228,12 +237,15 @@ function rsaFullDomainHash(hm: Uint8Array, rsaPub: RsaPub): bigint.BigInteger {
function rsaPubDecode(rsaPub: Uint8Array): RsaPub {
const modulusLength = (rsaPub[0] << 8) | rsaPub[1];
const exponentLength = (rsaPub[2] << 8) | rsaPub[3];
- const modulus = rsaPub.slice(4, 4 + modulusLength)
- const exponent = rsaPub.slice(4 + modulusLength, 4 + modulusLength + exponentLength);
+ const modulus = rsaPub.slice(4, 4 + modulusLength);
+ const exponent = rsaPub.slice(
+ 4 + modulusLength,
+ 4 + modulusLength + exponentLength,
+ );
const res = {
N: loadBigInt(modulus),
e: loadBigInt(exponent),
- }
+ };
return res;
}
@@ -241,16 +253,20 @@ function rsaPubEncode(rsaPub: RsaPub): Uint8Array {
const mb = rsaPub.N.toArray(256).value;
const eb = rsaPub.e.toArray(256).value;
const out = new Uint8Array(4 + mb.length + eb.length);
- out[0] = (mb.length >>> 8) & 0xFF;
- out[1] = mb.length & 0xFF;
- out[2] = (eb.length >>> 8) & 0xFF;
- out[3] = eb.length & 0xFF;
+ out[0] = (mb.length >>> 8) & 0xff;
+ out[1] = mb.length & 0xff;
+ out[2] = (eb.length >>> 8) & 0xff;
+ out[3] = eb.length & 0xff;
out.set(mb, 4);
out.set(eb, 4 + mb.length);
return out;
}
-export function rsaBlind(hm: Uint8Array, bks: Uint8Array, rsaPubEnc: Uint8Array): Uint8Array {
+export function rsaBlind(
+ hm: Uint8Array,
+ bks: Uint8Array,
+ rsaPubEnc: Uint8Array,
+): Uint8Array {
const rsaPub = rsaPubDecode(rsaPubEnc);
const data = rsaFullDomainHash(hm, rsaPub);
const r = rsaBlindingKeyDerive(rsaPub, bks);
@@ -259,7 +275,11 @@ export function rsaBlind(hm: Uint8Array, bks: Uint8Array, rsaPubEnc: Uint8Array)
return new Uint8Array(bm.toArray(256).value);
}
-export function rsaUnblind(sig: Uint8Array, rsaPubEnc: Uint8Array, bks: Uint8Array): Uint8Array {
+export function rsaUnblind(
+ sig: Uint8Array,
+ rsaPubEnc: Uint8Array,
+ bks: Uint8Array,
+): Uint8Array {
const rsaPub = rsaPubDecode(rsaPubEnc);
const blinded_s = loadBigInt(sig);
const r = rsaBlindingKeyDerive(rsaPub, bks);
@@ -268,10 +288,86 @@ export function rsaUnblind(sig: Uint8Array, rsaPubEnc: Uint8Array, bks: Uint8Arr
return new Uint8Array(s.toArray(256).value);
}
-export function rsaVerify(hm: Uint8Array, rsaSig: Uint8Array, rsaPubEnc: Uint8Array): boolean {
+export function rsaVerify(
+ hm: Uint8Array,
+ rsaSig: Uint8Array,
+ rsaPubEnc: Uint8Array,
+): boolean {
const rsaPub = rsaPubDecode(rsaPubEnc);
const d = rsaFullDomainHash(hm, rsaPub);
const sig = loadBigInt(rsaSig);
const sig_e = sig.modPow(rsaPub.e, rsaPub.N);
return sig_e.equals(d);
-} \ No newline at end of file
+}
+
+export interface EddsaKeyPair {
+ eddsaPub: Uint8Array;
+ eddsaPriv: Uint8Array;
+}
+
+export interface EcdheKeyPair {
+ ecdhePub: Uint8Array;
+ ecdhePriv: Uint8Array;
+}
+
+export function createEddsaKeyPair(): EddsaKeyPair {
+ const eddsaPriv = nacl.randomBytes(32);
+ const eddsaPub = eddsaGetPublic(eddsaPriv);
+ return { eddsaPriv, eddsaPub };
+}
+
+export function createEcdheKeyPair(): EcdheKeyPair {
+ const ecdhePriv = nacl.randomBytes(32);
+ const ecdhePub = ecdheGetPublic(ecdhePriv);
+ return { ecdhePriv, ecdhePub };
+}
+
+export function createBlindingKeySecret(): Uint8Array {
+ return nacl.randomBytes(32);
+}
+
+export function hash(d: Uint8Array): Uint8Array {
+ return nacl.hash(d);
+}
+
+export function eddsaSign(msg: Uint8Array, eddsaPriv: Uint8Array): Uint8Array {
+ const pair = nacl.sign_keyPair_fromSeed(eddsaPriv);
+ return nacl.sign_detached(msg, pair.secretKey);
+}
+
+export function eddsaVerify(
+ msg: Uint8Array,
+ sig: Uint8Array,
+ eddsaPub: Uint8Array,
+): boolean {
+ return nacl.sign_detached_verify(msg, sig, eddsaPub);
+}
+
+export function createHashContext(): nacl.HashState {
+ return new nacl.HashState();
+}
+
+export interface FreshCoin {
+ coinPub: Uint8Array;
+ coinPriv: Uint8Array;
+ bks: Uint8Array;
+}
+
+export function setupRefreshPlanchet(
+ secretSeed: Uint8Array,
+ coinNumber: number,
+): FreshCoin {
+ const info = stringToBytes("taler-coin-derivation");
+ const saltArrBuf = new ArrayBuffer(4);
+ const salt = new Uint8Array(saltArrBuf);
+ const saltDataView = new DataView(saltArrBuf);
+ saltDataView.setUint32(0, coinNumber);
+ const out = kdf(64, secretSeed, salt, info);
+ const coinPriv = out.slice(0, 32);
+ const bks = out.slice(32, 64);
+ return {
+ bks,
+ coinPriv,
+ coinPub: eddsaGetPublic(coinPriv),
+ };
+}