move some more crypto to taler-util package
This commit is contained in:
parent
3a69f27412
commit
269022a526
@ -5,7 +5,9 @@ import {
|
|||||||
encodeCrock,
|
encodeCrock,
|
||||||
getRandomBytes,
|
getRandomBytes,
|
||||||
kdf,
|
kdf,
|
||||||
|
kdfKw,
|
||||||
secretbox,
|
secretbox,
|
||||||
|
crypto_sign_keyPair_fromSeed,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { argon2id } from "hash-wasm";
|
import { argon2id } from "hash-wasm";
|
||||||
@ -25,6 +27,8 @@ export type EncryptedKeyShare = Flavor<string, "EncryptedKeyShare">;
|
|||||||
export type EncryptedTruth = Flavor<string, "EncryptedTruth">;
|
export type EncryptedTruth = Flavor<string, "EncryptedTruth">;
|
||||||
export type EncryptedCoreSecret = Flavor<string, "EncryptedCoreSecret">;
|
export type EncryptedCoreSecret = Flavor<string, "EncryptedCoreSecret">;
|
||||||
export type EncryptedMasterKey = Flavor<string, "EncryptedMasterKey">;
|
export type EncryptedMasterKey = Flavor<string, "EncryptedMasterKey">;
|
||||||
|
export type EddsaPublicKey = Flavor<string, "EddsaPublicKey">;
|
||||||
|
export type EddsaPrivateKey = Flavor<string, "EddsaPrivateKey">;
|
||||||
/**
|
/**
|
||||||
* Truth key, found in the recovery document.
|
* Truth key, found in the recovery document.
|
||||||
*/
|
*/
|
||||||
@ -53,6 +57,43 @@ export async function userIdentifierDerive(
|
|||||||
return encodeCrock(result);
|
return encodeCrock(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AccountKeyPair {
|
||||||
|
priv: EddsaPrivateKey;
|
||||||
|
pub: EddsaPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function accountKeypairDerive(userId: UserIdentifier): AccountKeyPair {
|
||||||
|
// FIXME: the KDF invocation looks fishy, but that's what the C code presently does.
|
||||||
|
const d = kdfKw({
|
||||||
|
outputLength: 32,
|
||||||
|
ikm: stringToBytes("ver"),
|
||||||
|
salt: decodeCrock(userId),
|
||||||
|
});
|
||||||
|
// FIXME: This bit twiddling seems wrong/unnecessary.
|
||||||
|
d[0] &= 248;
|
||||||
|
d[31] &= 127;
|
||||||
|
d[31] |= 64;
|
||||||
|
const pair = crypto_sign_keyPair_fromSeed(d);
|
||||||
|
return {
|
||||||
|
priv: encodeCrock(pair.secretKey),
|
||||||
|
pub: encodeCrock(pair.publicKey),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function encryptRecoveryDocument(
|
||||||
|
userId: UserIdentifier,
|
||||||
|
recoveryDoc: any,
|
||||||
|
): Promise<OpaqueData> {
|
||||||
|
const plaintext = stringToBytes(JSON.stringify(recoveryDoc));
|
||||||
|
const nonce = encodeCrock(getRandomBytes(nonceSize));
|
||||||
|
return anastasisEncrypt(
|
||||||
|
nonce,
|
||||||
|
asOpaque(userId),
|
||||||
|
encodeCrock(plaintext),
|
||||||
|
"erd",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function taConcat(chunks: Uint8Array[]): Uint8Array {
|
function taConcat(chunks: Uint8Array[]): Uint8Array {
|
||||||
let payloadLen = 0;
|
let payloadLen = 0;
|
||||||
for (const c of chunks) {
|
for (const c of chunks) {
|
||||||
|
@ -36,8 +36,10 @@ import {
|
|||||||
} from "./reducer-types.js";
|
} from "./reducer-types.js";
|
||||||
import fetchPonyfill from "fetch-ponyfill";
|
import fetchPonyfill from "fetch-ponyfill";
|
||||||
import {
|
import {
|
||||||
|
accountKeypairDerive,
|
||||||
coreSecretEncrypt,
|
coreSecretEncrypt,
|
||||||
encryptKeyshare,
|
encryptKeyshare,
|
||||||
|
encryptRecoveryDocument,
|
||||||
encryptTruth,
|
encryptTruth,
|
||||||
PolicyKey,
|
PolicyKey,
|
||||||
policyKeyDerive,
|
policyKeyDerive,
|
||||||
@ -492,14 +494,25 @@ async function uploadSecret(
|
|||||||
policies: policies.map((x, i) => {
|
policies: policies.map((x, i) => {
|
||||||
return {
|
return {
|
||||||
master_key: csr.encMasterKeys[i],
|
master_key: csr.encMasterKeys[i],
|
||||||
|
// FIXME: ...
|
||||||
uuid: [],
|
uuid: [],
|
||||||
salt:
|
salt: undefined as any,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const prov of state.policy_providers!) {
|
for (const prov of state.policy_providers!) {
|
||||||
|
const uid = uidMap[prov.provider_url]
|
||||||
|
const acctKeypair = accountKeypairDerive(uid);
|
||||||
|
const encRecoveryDoc = await encryptRecoveryDocument(uid, rd);
|
||||||
// FIXME: Upload recovery document.
|
// FIXME: Upload recovery document.
|
||||||
|
const resp = await fetch(
|
||||||
|
new URL(`policy/${acctKeypair.pub}`, prov.provider_url).href,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
body: decodeCrock(encRecoveryDoc),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -22,4 +22,9 @@ export * from "./url.js";
|
|||||||
export { fnutil } from "./fnutils.js";
|
export { fnutil } from "./fnutils.js";
|
||||||
export * from "./kdf.js";
|
export * from "./kdf.js";
|
||||||
export * from "./talerCrypto.js";
|
export * from "./talerCrypto.js";
|
||||||
export { randomBytes, secretbox, secretbox_open } from "./nacl-fast.js";
|
export {
|
||||||
|
randomBytes,
|
||||||
|
secretbox,
|
||||||
|
secretbox_open,
|
||||||
|
crypto_sign_keyPair_fromSeed,
|
||||||
|
} from "./nacl-fast.js";
|
||||||
|
@ -59,15 +59,30 @@ export function hmacSha256(key: Uint8Array, message: Uint8Array): Uint8Array {
|
|||||||
return hmac(sha256, 64, key, message);
|
return hmac(sha256, 64, key, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HMAC-SHA512-SHA256 (see RFC 5869).
|
||||||
|
*/
|
||||||
|
export function kdfKw(args: {
|
||||||
|
outputLength: number;
|
||||||
|
ikm: Uint8Array;
|
||||||
|
salt?: Uint8Array;
|
||||||
|
info?: Uint8Array;
|
||||||
|
}) {
|
||||||
|
return kdf(args.outputLength, args.ikm, args.salt, args.info);
|
||||||
|
}
|
||||||
|
|
||||||
export function kdf(
|
export function kdf(
|
||||||
outputLength: number,
|
outputLength: number,
|
||||||
ikm: Uint8Array,
|
ikm: Uint8Array,
|
||||||
salt: Uint8Array,
|
salt?: Uint8Array,
|
||||||
info: Uint8Array,
|
info?: Uint8Array,
|
||||||
): Uint8Array {
|
): Uint8Array {
|
||||||
|
salt = salt ?? new Uint8Array(64);
|
||||||
// extract
|
// extract
|
||||||
const prk = hmacSha512(salt, ikm);
|
const prk = hmacSha512(salt, ikm);
|
||||||
|
|
||||||
|
info = info ?? new Uint8Array(0);
|
||||||
|
|
||||||
// expand
|
// expand
|
||||||
const N = Math.ceil(outputLength / 32);
|
const N = Math.ceil(outputLength / 32);
|
||||||
const output = new Uint8Array(N * 32);
|
const output = new Uint8Array(N * 32);
|
||||||
|
@ -2894,7 +2894,6 @@ export function x25519_edwards_keyPair_fromSecretKey(
|
|||||||
throw new Error("bad secret key size");
|
throw new Error("bad secret key size");
|
||||||
}
|
}
|
||||||
d.set(secretKey, 0);
|
d.set(secretKey, 0);
|
||||||
//crypto_hash(d, secretKey, 32);
|
|
||||||
|
|
||||||
d[0] &= 248;
|
d[0] &= 248;
|
||||||
d[31] &= 127;
|
d[31] &= 127;
|
||||||
@ -2906,7 +2905,7 @@ export function x25519_edwards_keyPair_fromSecretKey(
|
|||||||
return pk;
|
return pk;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sign_keyPair_fromSecretKey(
|
export function crypto_sign_keyPair_fromSecretKey(
|
||||||
secretKey: Uint8Array,
|
secretKey: Uint8Array,
|
||||||
): {
|
): {
|
||||||
publicKey: Uint8Array;
|
publicKey: Uint8Array;
|
||||||
@ -2920,7 +2919,7 @@ export function sign_keyPair_fromSecretKey(
|
|||||||
return { publicKey: pk, secretKey: new Uint8Array(secretKey) };
|
return { publicKey: pk, secretKey: new Uint8Array(secretKey) };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sign_keyPair_fromSeed(
|
export function crypto_sign_keyPair_fromSeed(
|
||||||
seed: Uint8Array,
|
seed: Uint8Array,
|
||||||
): {
|
): {
|
||||||
publicKey: Uint8Array;
|
publicKey: Uint8Array;
|
||||||
|
@ -69,7 +69,7 @@ test("taler-exchange-tvg eddsa key", (t) => {
|
|||||||
const priv = "9TM70AKDTS57AWY9JK2J4TMBTMW6K62WHHGZWYDG0VM5ABPZKD40";
|
const priv = "9TM70AKDTS57AWY9JK2J4TMBTMW6K62WHHGZWYDG0VM5ABPZKD40";
|
||||||
const pub = "8GSJZ649T2PXMKZC01Y4ANNBE7MF14QVK9SQEC4E46ZHKCVG8AS0";
|
const pub = "8GSJZ649T2PXMKZC01Y4ANNBE7MF14QVK9SQEC4E46ZHKCVG8AS0";
|
||||||
|
|
||||||
const pair = nacl.sign_keyPair_fromSeed(decodeCrock(priv));
|
const pair = nacl.crypto_sign_keyPair_fromSeed(decodeCrock(priv));
|
||||||
t.deepEqual(encodeCrock(pair.publicKey), pub);
|
t.deepEqual(encodeCrock(pair.publicKey), pub);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ export function decodeCrock(encoded: string): Uint8Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function eddsaGetPublic(eddsaPriv: Uint8Array): Uint8Array {
|
export function eddsaGetPublic(eddsaPriv: Uint8Array): Uint8Array {
|
||||||
const pair = nacl.sign_keyPair_fromSeed(eddsaPriv);
|
const pair = nacl.crypto_sign_keyPair_fromSeed(eddsaPriv);
|
||||||
return pair.publicKey;
|
return pair.publicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,7 +353,7 @@ export function hash(d: Uint8Array): Uint8Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function eddsaSign(msg: Uint8Array, eddsaPriv: Uint8Array): Uint8Array {
|
export function eddsaSign(msg: Uint8Array, eddsaPriv: Uint8Array): Uint8Array {
|
||||||
const pair = nacl.sign_keyPair_fromSeed(eddsaPriv);
|
const pair = nacl.crypto_sign_keyPair_fromSeed(eddsaPriv);
|
||||||
return nacl.sign_detached(msg, pair.secretKey);
|
return nacl.sign_detached(msg, pair.secretKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,3 +447,56 @@ export function setupRefreshTransferPub(
|
|||||||
ecdhePub: ecdheGetPublic(out),
|
ecdhePub: ecdheGetPublic(out),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum TalerSignaturePurpose {
|
||||||
|
MERCHANT_TRACK_TRANSACTION = 1103,
|
||||||
|
WALLET_RESERVE_WITHDRAW = 1200,
|
||||||
|
WALLET_COIN_DEPOSIT = 1201,
|
||||||
|
MASTER_DENOMINATION_KEY_VALIDITY = 1025,
|
||||||
|
MASTER_WIRE_FEES = 1028,
|
||||||
|
MASTER_WIRE_DETAILS = 1030,
|
||||||
|
WALLET_COIN_MELT = 1202,
|
||||||
|
TEST = 4242,
|
||||||
|
MERCHANT_PAYMENT_OK = 1104,
|
||||||
|
MERCHANT_CONTRACT = 1101,
|
||||||
|
WALLET_COIN_RECOUP = 1203,
|
||||||
|
WALLET_COIN_LINK = 1204,
|
||||||
|
EXCHANGE_CONFIRM_RECOUP = 1039,
|
||||||
|
EXCHANGE_CONFIRM_RECOUP_REFRESH = 1041,
|
||||||
|
ANASTASIS_POLICY_UPLOAD = 1400,
|
||||||
|
ANASTASIS_POLICY_DOWNLOAD = 1401,
|
||||||
|
SYNC_BACKUP_UPLOAD = 1450,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SignaturePurposeBuilder {
|
||||||
|
private chunks: Uint8Array[] = [];
|
||||||
|
|
||||||
|
constructor(private purposeNum: number) {}
|
||||||
|
|
||||||
|
put(bytes: Uint8Array): SignaturePurposeBuilder {
|
||||||
|
this.chunks.push(Uint8Array.from(bytes));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
build(): Uint8Array {
|
||||||
|
let payloadLen = 0;
|
||||||
|
for (const c of this.chunks) {
|
||||||
|
payloadLen += c.byteLength;
|
||||||
|
}
|
||||||
|
const buf = new ArrayBuffer(4 + 4 + payloadLen);
|
||||||
|
const u8buf = new Uint8Array(buf);
|
||||||
|
let p = 8;
|
||||||
|
for (const c of this.chunks) {
|
||||||
|
u8buf.set(c, p);
|
||||||
|
p += c.byteLength;
|
||||||
|
}
|
||||||
|
const dvbuf = new DataView(buf);
|
||||||
|
dvbuf.setUint32(0, payloadLen + 4 + 4);
|
||||||
|
dvbuf.setUint32(4, this.purposeNum);
|
||||||
|
return u8buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildSigPS(purposeNum: number): SignaturePurposeBuilder {
|
||||||
|
return new SignaturePurposeBuilder(purposeNum);
|
||||||
|
}
|
||||||
|
@ -34,7 +34,14 @@ import {
|
|||||||
CoinSourceType,
|
CoinSourceType,
|
||||||
} from "../../db.js";
|
} from "../../db.js";
|
||||||
|
|
||||||
import { CoinDepositPermission, RecoupRequest, RefreshPlanchetInfo } from "@gnu-taler/taler-util";
|
import {
|
||||||
|
buildSigPS,
|
||||||
|
CoinDepositPermission,
|
||||||
|
RecoupRequest,
|
||||||
|
RefreshPlanchetInfo,
|
||||||
|
SignaturePurposeBuilder,
|
||||||
|
TalerSignaturePurpose,
|
||||||
|
} from "@gnu-taler/taler-util";
|
||||||
// FIXME: These types should be internal to the wallet!
|
// FIXME: These types should be internal to the wallet!
|
||||||
import {
|
import {
|
||||||
BenchmarkResult,
|
BenchmarkResult,
|
||||||
@ -80,24 +87,6 @@ import bigint from "big-integer";
|
|||||||
|
|
||||||
const logger = new Logger("cryptoImplementation.ts");
|
const logger = new Logger("cryptoImplementation.ts");
|
||||||
|
|
||||||
enum SignaturePurpose {
|
|
||||||
MERCHANT_TRACK_TRANSACTION = 1103,
|
|
||||||
WALLET_RESERVE_WITHDRAW = 1200,
|
|
||||||
WALLET_COIN_DEPOSIT = 1201,
|
|
||||||
MASTER_DENOMINATION_KEY_VALIDITY = 1025,
|
|
||||||
MASTER_WIRE_FEES = 1028,
|
|
||||||
MASTER_WIRE_DETAILS = 1030,
|
|
||||||
WALLET_COIN_MELT = 1202,
|
|
||||||
TEST = 4242,
|
|
||||||
MERCHANT_PAYMENT_OK = 1104,
|
|
||||||
MERCHANT_CONTRACT = 1101,
|
|
||||||
WALLET_COIN_RECOUP = 1203,
|
|
||||||
WALLET_COIN_LINK = 1204,
|
|
||||||
EXCHANGE_CONFIRM_RECOUP = 1039,
|
|
||||||
EXCHANGE_CONFIRM_RECOUP_REFRESH = 1041,
|
|
||||||
SYNC_BACKUP_UPLOAD = 1450,
|
|
||||||
}
|
|
||||||
|
|
||||||
function amountToBuffer(amount: AmountJson): Uint8Array {
|
function amountToBuffer(amount: AmountJson): Uint8Array {
|
||||||
const buffer = new ArrayBuffer(8 + 4 + 12);
|
const buffer = new ArrayBuffer(8 + 4 + 12);
|
||||||
const dvbuf = new DataView(buffer);
|
const dvbuf = new DataView(buffer);
|
||||||
@ -139,38 +128,6 @@ function timestampRoundedToBuffer(ts: Timestamp): Uint8Array {
|
|||||||
return new Uint8Array(b);
|
return new Uint8Array(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SignaturePurposeBuilder {
|
|
||||||
private chunks: Uint8Array[] = [];
|
|
||||||
|
|
||||||
constructor(private purposeNum: number) { }
|
|
||||||
|
|
||||||
put(bytes: Uint8Array): SignaturePurposeBuilder {
|
|
||||||
this.chunks.push(Uint8Array.from(bytes));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
build(): Uint8Array {
|
|
||||||
let payloadLen = 0;
|
|
||||||
for (const c of this.chunks) {
|
|
||||||
payloadLen += c.byteLength;
|
|
||||||
}
|
|
||||||
const buf = new ArrayBuffer(4 + 4 + payloadLen);
|
|
||||||
const u8buf = new Uint8Array(buf);
|
|
||||||
let p = 8;
|
|
||||||
for (const c of this.chunks) {
|
|
||||||
u8buf.set(c, p);
|
|
||||||
p += c.byteLength;
|
|
||||||
}
|
|
||||||
const dvbuf = new DataView(buf);
|
|
||||||
dvbuf.setUint32(0, payloadLen + 4 + 4);
|
|
||||||
dvbuf.setUint32(4, this.purposeNum);
|
|
||||||
return u8buf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildSigPS(purposeNum: number): SignaturePurposeBuilder {
|
|
||||||
return new SignaturePurposeBuilder(purposeNum);
|
|
||||||
}
|
|
||||||
export class CryptoImplementation {
|
export class CryptoImplementation {
|
||||||
static enableTracing = false;
|
static enableTracing = false;
|
||||||
|
|
||||||
@ -192,7 +149,9 @@ export class CryptoImplementation {
|
|||||||
const denomPubHash = hash(denomPub);
|
const denomPubHash = hash(denomPub);
|
||||||
const evHash = hash(ev);
|
const evHash = hash(ev);
|
||||||
|
|
||||||
const withdrawRequest = buildSigPS(SignaturePurpose.WALLET_RESERVE_WITHDRAW)
|
const withdrawRequest = buildSigPS(
|
||||||
|
TalerSignaturePurpose.WALLET_RESERVE_WITHDRAW,
|
||||||
|
)
|
||||||
.put(reservePub)
|
.put(reservePub)
|
||||||
.put(amountToBuffer(amountWithFee))
|
.put(amountToBuffer(amountWithFee))
|
||||||
.put(denomPubHash)
|
.put(denomPubHash)
|
||||||
@ -236,7 +195,7 @@ export class CryptoImplementation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
signTrackTransaction(req: SignTrackTransactionRequest): string {
|
signTrackTransaction(req: SignTrackTransactionRequest): string {
|
||||||
const p = buildSigPS(SignaturePurpose.MERCHANT_TRACK_TRANSACTION)
|
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_TRACK_TRANSACTION)
|
||||||
.put(decodeCrock(req.contractTermsHash))
|
.put(decodeCrock(req.contractTermsHash))
|
||||||
.put(decodeCrock(req.wireHash))
|
.put(decodeCrock(req.wireHash))
|
||||||
.put(decodeCrock(req.merchantPub))
|
.put(decodeCrock(req.merchantPub))
|
||||||
@ -249,7 +208,7 @@ export class CryptoImplementation {
|
|||||||
* Create and sign a message to recoup a coin.
|
* Create and sign a message to recoup a coin.
|
||||||
*/
|
*/
|
||||||
createRecoupRequest(coin: CoinRecord): RecoupRequest {
|
createRecoupRequest(coin: CoinRecord): RecoupRequest {
|
||||||
const p = buildSigPS(SignaturePurpose.WALLET_COIN_RECOUP)
|
const p = buildSigPS(TalerSignaturePurpose.WALLET_COIN_RECOUP)
|
||||||
.put(decodeCrock(coin.coinPub))
|
.put(decodeCrock(coin.coinPub))
|
||||||
.put(decodeCrock(coin.denomPubHash))
|
.put(decodeCrock(coin.denomPubHash))
|
||||||
.put(decodeCrock(coin.blindingKey))
|
.put(decodeCrock(coin.blindingKey))
|
||||||
@ -276,7 +235,7 @@ export class CryptoImplementation {
|
|||||||
contractHash: string,
|
contractHash: string,
|
||||||
merchantPub: string,
|
merchantPub: string,
|
||||||
): boolean {
|
): boolean {
|
||||||
const p = buildSigPS(SignaturePurpose.MERCHANT_PAYMENT_OK)
|
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_PAYMENT_OK)
|
||||||
.put(decodeCrock(contractHash))
|
.put(decodeCrock(contractHash))
|
||||||
.build();
|
.build();
|
||||||
const sigBytes = decodeCrock(sig);
|
const sigBytes = decodeCrock(sig);
|
||||||
@ -288,7 +247,7 @@ export class CryptoImplementation {
|
|||||||
* Check if a wire fee is correctly signed.
|
* Check if a wire fee is correctly signed.
|
||||||
*/
|
*/
|
||||||
isValidWireFee(type: string, wf: WireFee, masterPub: string): boolean {
|
isValidWireFee(type: string, wf: WireFee, masterPub: string): boolean {
|
||||||
const p = buildSigPS(SignaturePurpose.MASTER_WIRE_FEES)
|
const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_FEES)
|
||||||
.put(hash(stringToBytes(type + "\0")))
|
.put(hash(stringToBytes(type + "\0")))
|
||||||
.put(timestampRoundedToBuffer(wf.startStamp))
|
.put(timestampRoundedToBuffer(wf.startStamp))
|
||||||
.put(timestampRoundedToBuffer(wf.endStamp))
|
.put(timestampRoundedToBuffer(wf.endStamp))
|
||||||
@ -304,7 +263,7 @@ export class CryptoImplementation {
|
|||||||
* Check if the signature of a denomination is valid.
|
* Check if the signature of a denomination is valid.
|
||||||
*/
|
*/
|
||||||
isValidDenom(denom: DenominationRecord, masterPub: string): boolean {
|
isValidDenom(denom: DenominationRecord, masterPub: string): boolean {
|
||||||
const p = buildSigPS(SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY)
|
const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY)
|
||||||
.put(decodeCrock(masterPub))
|
.put(decodeCrock(masterPub))
|
||||||
.put(timestampRoundedToBuffer(denom.stampStart))
|
.put(timestampRoundedToBuffer(denom.stampStart))
|
||||||
.put(timestampRoundedToBuffer(denom.stampExpireWithdraw))
|
.put(timestampRoundedToBuffer(denom.stampExpireWithdraw))
|
||||||
@ -334,7 +293,9 @@ export class CryptoImplementation {
|
|||||||
stringToBytes(paytoUri + "\0"),
|
stringToBytes(paytoUri + "\0"),
|
||||||
new Uint8Array(0),
|
new Uint8Array(0),
|
||||||
);
|
);
|
||||||
const p = buildSigPS(SignaturePurpose.MASTER_WIRE_DETAILS).put(h).build();
|
const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS)
|
||||||
|
.put(h)
|
||||||
|
.build();
|
||||||
return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
|
return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,7 +305,7 @@ export class CryptoImplementation {
|
|||||||
merchantPub: string,
|
merchantPub: string,
|
||||||
): boolean {
|
): boolean {
|
||||||
const cthDec = decodeCrock(contractTermsHash);
|
const cthDec = decodeCrock(contractTermsHash);
|
||||||
const p = buildSigPS(SignaturePurpose.MERCHANT_CONTRACT)
|
const p = buildSigPS(TalerSignaturePurpose.MERCHANT_CONTRACT)
|
||||||
.put(cthDec)
|
.put(cthDec)
|
||||||
.build();
|
.build();
|
||||||
return eddsaVerify(p, decodeCrock(sig), decodeCrock(merchantPub));
|
return eddsaVerify(p, decodeCrock(sig), decodeCrock(merchantPub));
|
||||||
@ -364,8 +325,8 @@ export class CryptoImplementation {
|
|||||||
eddsaGetPublic(key: string): { priv: string; pub: string } {
|
eddsaGetPublic(key: string): { priv: string; pub: string } {
|
||||||
return {
|
return {
|
||||||
priv: key,
|
priv: key,
|
||||||
pub: encodeCrock(eddsaGetPublic(decodeCrock(key)))
|
pub: encodeCrock(eddsaGetPublic(decodeCrock(key))),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -392,7 +353,7 @@ export class CryptoImplementation {
|
|||||||
* and deposit permissions for each given coin.
|
* and deposit permissions for each given coin.
|
||||||
*/
|
*/
|
||||||
signDepositPermission(depositInfo: DepositInfo): CoinDepositPermission {
|
signDepositPermission(depositInfo: DepositInfo): CoinDepositPermission {
|
||||||
const d = buildSigPS(SignaturePurpose.WALLET_COIN_DEPOSIT)
|
const d = buildSigPS(TalerSignaturePurpose.WALLET_COIN_DEPOSIT)
|
||||||
.put(decodeCrock(depositInfo.contractTermsHash))
|
.put(decodeCrock(depositInfo.contractTermsHash))
|
||||||
.put(decodeCrock(depositInfo.wireInfoHash))
|
.put(decodeCrock(depositInfo.wireInfoHash))
|
||||||
.put(decodeCrock(depositInfo.denomPubHash))
|
.put(decodeCrock(depositInfo.denomPubHash))
|
||||||
@ -503,7 +464,7 @@ export class CryptoImplementation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sessionHash = sessionHc.finish();
|
const sessionHash = sessionHc.finish();
|
||||||
const confirmData = buildSigPS(SignaturePurpose.WALLET_COIN_MELT)
|
const confirmData = buildSigPS(TalerSignaturePurpose.WALLET_COIN_MELT)
|
||||||
.put(sessionHash)
|
.put(sessionHash)
|
||||||
.put(decodeCrock(meltCoinDenomPubHash))
|
.put(decodeCrock(meltCoinDenomPubHash))
|
||||||
.put(amountToBuffer(valueWithFee))
|
.put(amountToBuffer(valueWithFee))
|
||||||
@ -549,7 +510,7 @@ export class CryptoImplementation {
|
|||||||
coinEv: string,
|
coinEv: string,
|
||||||
): string {
|
): string {
|
||||||
const coinEvHash = hash(decodeCrock(coinEv));
|
const coinEvHash = hash(decodeCrock(coinEv));
|
||||||
const coinLink = buildSigPS(SignaturePurpose.WALLET_COIN_LINK)
|
const coinLink = buildSigPS(TalerSignaturePurpose.WALLET_COIN_LINK)
|
||||||
.put(decodeCrock(newDenomHash))
|
.put(decodeCrock(newDenomHash))
|
||||||
.put(decodeCrock(transferPub))
|
.put(decodeCrock(transferPub))
|
||||||
.put(coinEvHash)
|
.put(coinEvHash)
|
||||||
@ -622,9 +583,7 @@ export class CryptoImplementation {
|
|||||||
} else {
|
} else {
|
||||||
hOld = new Uint8Array(64);
|
hOld = new Uint8Array(64);
|
||||||
}
|
}
|
||||||
const sigBlob = new SignaturePurposeBuilder(
|
const sigBlob = buildSigPS(TalerSignaturePurpose.SYNC_BACKUP_UPLOAD)
|
||||||
SignaturePurpose.SYNC_BACKUP_UPLOAD,
|
|
||||||
)
|
|
||||||
.put(hOld)
|
.put(hOld)
|
||||||
.put(hNew)
|
.put(hNew)
|
||||||
.build();
|
.build();
|
||||||
|
@ -763,6 +763,8 @@ async function processDownloadProposalImpl(
|
|||||||
proposalResp.contract_terms,
|
proposalResp.contract_terms,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
logger.info(`Contract terms hash: ${contractTermsHash}`);
|
||||||
|
|
||||||
let parsedContractTerms: ContractTerms;
|
let parsedContractTerms: ContractTerms;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { canonicalJson } from "@gnu-taler/taler-util";
|
import { canonicalJson, Logger } from "@gnu-taler/taler-util";
|
||||||
import { kdf } from "@gnu-taler/taler-util";
|
import { kdf } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
decodeCrock,
|
decodeCrock,
|
||||||
@ -24,6 +24,8 @@ import {
|
|||||||
stringToBytes,
|
stringToBytes,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
|
|
||||||
|
const logger = new Logger("contractTerms.ts");
|
||||||
|
|
||||||
export namespace ContractTermsUtil {
|
export namespace ContractTermsUtil {
|
||||||
export type PathPredicate = (path: string[]) => boolean;
|
export type PathPredicate = (path: string[]) => boolean;
|
||||||
|
|
||||||
@ -222,6 +224,8 @@ export namespace ContractTermsUtil {
|
|||||||
export function hashContractTerms(contractTerms: unknown): string {
|
export function hashContractTerms(contractTerms: unknown): string {
|
||||||
const cleaned = scrub(contractTerms);
|
const cleaned = scrub(contractTerms);
|
||||||
const canon = canonicalJson(cleaned) + "\0";
|
const canon = canonicalJson(cleaned) + "\0";
|
||||||
return encodeCrock(hash(stringToBytes(canon)));
|
const bytes = stringToBytes(canon);
|
||||||
|
logger.info(`contract terms before hashing: ${encodeCrock(bytes)}`);
|
||||||
|
return encodeCrock(hash(bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,15 @@ importers:
|
|||||||
specifiers:
|
specifiers:
|
||||||
'@gnu-taler/taler-util': workspace:^0.8.3
|
'@gnu-taler/taler-util': workspace:^0.8.3
|
||||||
ava: ^3.15.0
|
ava: ^3.15.0
|
||||||
|
fetch-ponyfill: ^7.1.0
|
||||||
hash-wasm: ^4.9.0
|
hash-wasm: ^4.9.0
|
||||||
|
node-fetch: ^3.0.0
|
||||||
typescript: ^4.4.3
|
typescript: ^4.4.3
|
||||||
dependencies:
|
dependencies:
|
||||||
'@gnu-taler/taler-util': link:../taler-util
|
'@gnu-taler/taler-util': link:../taler-util
|
||||||
|
fetch-ponyfill: 7.1.0
|
||||||
hash-wasm: 4.9.0
|
hash-wasm: 4.9.0
|
||||||
|
node-fetch: 3.0.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
ava: 3.15.0
|
ava: 3.15.0
|
||||||
typescript: 4.4.3
|
typescript: 4.4.3
|
||||||
@ -32,6 +36,7 @@ importers:
|
|||||||
'@types/jest': ^26.0.8
|
'@types/jest': ^26.0.8
|
||||||
'@typescript-eslint/eslint-plugin': ^2.25.0
|
'@typescript-eslint/eslint-plugin': ^2.25.0
|
||||||
'@typescript-eslint/parser': ^2.25.0
|
'@typescript-eslint/parser': ^2.25.0
|
||||||
|
anastasis-core: workspace:^0.0.1
|
||||||
enzyme: ^3.11.0
|
enzyme: ^3.11.0
|
||||||
enzyme-adapter-preact-pure: ^3.1.0
|
enzyme-adapter-preact-pure: ^3.1.0
|
||||||
eslint: ^6.8.0
|
eslint: ^6.8.0
|
||||||
@ -46,6 +51,7 @@ importers:
|
|||||||
typescript: ^3.7.5
|
typescript: ^3.7.5
|
||||||
dependencies:
|
dependencies:
|
||||||
'@gnu-taler/taler-util': link:../taler-util
|
'@gnu-taler/taler-util': link:../taler-util
|
||||||
|
anastasis-core: link:../anastasis-core
|
||||||
preact: 10.5.14
|
preact: 10.5.14
|
||||||
preact-render-to-string: 5.1.19_preact@10.5.14
|
preact-render-to-string: 5.1.19_preact@10.5.14
|
||||||
preact-router: 3.2.1_preact@10.5.14
|
preact-router: 3.2.1_preact@10.5.14
|
||||||
@ -9701,6 +9707,11 @@ packages:
|
|||||||
assert-plus: 1.0.0
|
assert-plus: 1.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/data-uri-to-buffer/3.0.1:
|
||||||
|
resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/data-urls/1.1.0:
|
/data-urls/1.1.0:
|
||||||
resolution: {integrity: sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==}
|
resolution: {integrity: sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -11267,6 +11278,19 @@ packages:
|
|||||||
bser: 2.1.1
|
bser: 2.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/fetch-blob/3.1.2:
|
||||||
|
resolution: {integrity: sha512-hunJbvy/6OLjCD0uuhLdp0mMPzP/yd2ssd1t2FCJsaA7wkWhpbp9xfuNVpv7Ll4jFhzp6T4LAupSiV9uOeg0VQ==}
|
||||||
|
engines: {node: ^12.20 || >= 14.13}
|
||||||
|
dependencies:
|
||||||
|
web-streams-polyfill: 3.1.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/fetch-ponyfill/7.1.0:
|
||||||
|
resolution: {integrity: sha512-FhbbL55dj/qdVO3YNK7ZEkshvj3eQ7EuIGV2I6ic/2YiocvyWv+7jg2s4AyS0wdRU75s3tA8ZxI/xPigb0v5Aw==}
|
||||||
|
dependencies:
|
||||||
|
node-fetch: 2.6.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fflate/0.6.0:
|
/fflate/0.6.0:
|
||||||
resolution: {integrity: sha512-u4AdW/Xx7iinDhYQuS0B0vvbUX7JWXO07jEvYUlbNZvtoiDLkDvHR17LSwxhbawjZVDXczzLHAQUDSllISm4/A==}
|
resolution: {integrity: sha512-u4AdW/Xx7iinDhYQuS0B0vvbUX7JWXO07jEvYUlbNZvtoiDLkDvHR17LSwxhbawjZVDXczzLHAQUDSllISm4/A==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -11658,6 +11682,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@ -15032,7 +15057,14 @@ packages:
|
|||||||
/node-fetch/2.6.1:
|
/node-fetch/2.6.1:
|
||||||
resolution: {integrity: sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==}
|
resolution: {integrity: sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==}
|
||||||
engines: {node: 4.x || >=6.0.0}
|
engines: {node: 4.x || >=6.0.0}
|
||||||
dev: true
|
|
||||||
|
/node-fetch/3.0.0:
|
||||||
|
resolution: {integrity: sha512-bKMI+C7/T/SPU1lKnbQbwxptpCrG9ashG+VkytmXCPZyuM9jB6VU+hY0oi4lC8LxTtAeWdckNCTa3nrGsAdA3Q==}
|
||||||
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
dependencies:
|
||||||
|
data-uri-to-buffer: 3.0.1
|
||||||
|
fetch-blob: 3.1.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/node-forge/0.10.0:
|
/node-forge/0.10.0:
|
||||||
resolution: {integrity: sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==}
|
resolution: {integrity: sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==}
|
||||||
@ -20331,6 +20363,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==}
|
resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/web-streams-polyfill/3.1.1:
|
||||||
|
resolution: {integrity: sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/webidl-conversions/4.0.2:
|
/webidl-conversions/4.0.2:
|
||||||
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
|
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
Loading…
Reference in New Issue
Block a user