diff options
Diffstat (limited to 'src/crypto/talerCrypto.ts')
-rw-r--r-- | src/crypto/talerCrypto.ts | 144 |
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), + }; +} |