aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/crypto
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-07-12 17:41:14 +0200
committerFlorian Dold <florian@dold.me>2022-07-12 17:41:14 +0200
commitf11483b511ff1f839b9913c4832eee9109f67aeb (patch)
tree6f4e1c5891a24bbb7500cea3964d3826d2ef87e1 /packages/taler-wallet-core/src/crypto
parentb214934b75418d0d01c9556577d9594f1db5a319 (diff)
downloadwallet-core-f11483b511ff1f839b9913c4832eee9109f67aeb.tar.xz
wallet-core: implement accepting p2p push payments
Diffstat (limited to 'packages/taler-wallet-core/src/crypto')
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoImplementation.ts132
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoTypes.ts83
2 files changed, 210 insertions, 5 deletions
diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
index 1d3641836..c177a51dd 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
@@ -33,10 +33,12 @@ import {
BlindedDenominationSignature,
bufferForUint32,
buildSigPS,
+ bytesToString,
CoinDepositPermission,
CoinEnvelope,
createHashContext,
decodeCrock,
+ decryptContractForMerge,
DenomKeyType,
DepositInfo,
ecdheGetPublic,
@@ -45,6 +47,7 @@ import {
eddsaSign,
eddsaVerify,
encodeCrock,
+ encryptContractForMerge,
ExchangeProtocolVersion,
getRandomBytes,
hash,
@@ -81,10 +84,17 @@ import { DenominationRecord, WireFee } from "../db.js";
import {
CreateRecoupRefreshReqRequest,
CreateRecoupReqRequest,
+ DecryptContractRequest,
+ DecryptContractResponse,
DerivedRefreshSession,
DerivedTipPlanchet,
DeriveRefreshSessionRequest,
DeriveTipRequest,
+ EncryptContractRequest,
+ EncryptContractResponse,
+ EncryptedContract,
+ SignPurseMergeRequest,
+ SignPurseMergeResponse,
SignTrackTransactionRequest,
} from "./cryptoTypes.js";
@@ -185,6 +195,16 @@ export interface TalerCryptoInterface {
signPurseDeposits(
req: SignPurseDepositsRequest,
): Promise<SignPurseDepositsResponse>;
+
+ encryptContractForMerge(
+ req: EncryptContractRequest,
+ ): Promise<EncryptContractResponse>;
+
+ decryptContractForMerge(
+ req: DecryptContractRequest,
+ ): Promise<DecryptContractResponse>;
+
+ signPurseMerge(req: SignPurseMergeRequest): Promise<SignPurseMergeResponse>;
}
/**
@@ -326,6 +346,21 @@ export const nullCrypto: TalerCryptoInterface = {
): Promise<SignPurseDepositsResponse> {
throw new Error("Function not implemented.");
},
+ encryptContractForMerge: function (
+ req: EncryptContractRequest,
+ ): Promise<EncryptContractResponse> {
+ throw new Error("Function not implemented.");
+ },
+ decryptContractForMerge: function (
+ req: DecryptContractRequest,
+ ): Promise<DecryptContractResponse> {
+ throw new Error("Function not implemented.");
+ },
+ signPurseMerge: function (
+ req: SignPurseMergeRequest,
+ ): Promise<SignPurseMergeResponse> {
+ throw new Error("Function not implemented.");
+ },
};
export type WithArg<X> = X extends (req: infer T) => infer R
@@ -502,6 +537,9 @@ export interface TransferPubResponse {
transferPriv: string;
}
+/**
+ * JS-native implementation of the Taler crypto worker operations.
+ */
export const nativeCryptoR: TalerCryptoInterfaceR = {
async eddsaSign(
tci: TalerCryptoInterfaceR,
@@ -960,9 +998,11 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
maybeAgeCommitmentHash = ach;
hAgeCommitment = decodeCrock(ach);
if (depositInfo.requiredMinimumAge != null) {
- minimumAgeSig = AgeRestriction.commitmentAttest(
- depositInfo.ageCommitmentProof,
- depositInfo.requiredMinimumAge,
+ minimumAgeSig = encodeCrock(
+ AgeRestriction.commitmentAttest(
+ depositInfo.ageCommitmentProof,
+ depositInfo.requiredMinimumAge,
+ ),
);
}
} else {
@@ -1094,7 +1134,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
if (req.meltCoinAgeCommitmentProof) {
newAc = await AgeRestriction.commitmentDerive(
req.meltCoinAgeCommitmentProof,
- transferSecretRes.h,
+ decodeCrock(transferSecretRes.h),
);
newAch = AgeRestriction.hashCommitment(newAc.commitment);
}
@@ -1280,6 +1320,9 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
for (const c of req.coins) {
const sigBlob = buildSigPS(TalerSignaturePurpose.WALLET_PURSE_DEPOSIT)
.put(amountToBuffer(Amounts.parseOrThrow(c.contribution)))
+ .put(decodeCrock(c.denomPubHash))
+ // FIXME: use h_age_commitment here
+ .put(new Uint8Array(32))
.put(decodeCrock(req.pursePub))
.put(hExchangeBaseUrl)
.build();
@@ -1300,6 +1343,87 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
deposits,
};
},
+ async encryptContractForMerge(
+ tci: TalerCryptoInterfaceR,
+ req: EncryptContractRequest,
+ ): Promise<EncryptContractResponse> {
+ const contractKeyPair = await this.createEddsaKeypair(tci, {});
+ const enc = await encryptContractForMerge(
+ decodeCrock(req.pursePub),
+ decodeCrock(contractKeyPair.priv),
+ decodeCrock(req.mergePriv),
+ req.contractTerms,
+ );
+ const sigBlob = buildSigPS(TalerSignaturePurpose.WALLET_PURSE_ECONTRACT)
+ .put(hash(enc))
+ .put(decodeCrock(contractKeyPair.pub))
+ .build();
+ const sig = eddsaSign(sigBlob, decodeCrock(req.pursePriv));
+ return {
+ econtract: {
+ contract_pub: contractKeyPair.pub,
+ econtract: encodeCrock(enc),
+ econtract_sig: encodeCrock(sig),
+ },
+ contractPriv: contractKeyPair.priv,
+ };
+ },
+ async decryptContractForMerge(
+ tci: TalerCryptoInterfaceR,
+ req: DecryptContractRequest,
+ ): Promise<DecryptContractResponse> {
+ const res = await decryptContractForMerge(
+ decodeCrock(req.ciphertext),
+ decodeCrock(req.pursePub),
+ decodeCrock(req.contractPriv),
+ );
+ return {
+ contractTerms: res.contractTerms,
+ mergePriv: encodeCrock(res.mergePriv),
+ };
+ },
+ async signPurseMerge(
+ tci: TalerCryptoInterfaceR,
+ req: SignPurseMergeRequest,
+ ): Promise<SignPurseMergeResponse> {
+ const mergeSigBlob = buildSigPS(TalerSignaturePurpose.WALLET_PURSE_MERGE)
+ .put(timestampRoundedToBuffer(req.mergeTimestamp))
+ .put(decodeCrock(req.pursePub))
+ .put(hashTruncate32(stringToBytes(req.reservePayto + "\0")))
+ .build();
+ const mergeSigResp = await tci.eddsaSign(tci, {
+ msg: encodeCrock(mergeSigBlob),
+ priv: req.mergePriv,
+ });
+
+ const reserveSigBlob = buildSigPS(
+ TalerSignaturePurpose.WALLET_ACCOUNT_MERGE,
+ )
+ .put(timestampRoundedToBuffer(req.purseExpiration))
+ .put(amountToBuffer(Amounts.parseOrThrow(req.purseAmount)))
+ .put(amountToBuffer(Amounts.parseOrThrow(req.purseFee)))
+ .put(decodeCrock(req.contractTermsHash))
+ .put(decodeCrock(req.pursePub))
+ .put(timestampRoundedToBuffer(req.mergeTimestamp))
+ // FIXME: put in min_age
+ .put(bufferForUint32(0))
+ .put(bufferForUint32(req.flags))
+ .build();
+
+ logger.info(
+ `signing WALLET_ACCOUNT_MERGE over ${encodeCrock(reserveSigBlob)}`,
+ );
+
+ const reserveSigResp = await tci.eddsaSign(tci, {
+ msg: encodeCrock(reserveSigBlob),
+ priv: req.reservePriv,
+ });
+
+ return {
+ mergeSig: mergeSigResp.sig,
+ accountSig: reserveSigResp.sig,
+ };
+ },
};
function amountToBuffer(amount: AmountJson): Uint8Array {
diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
index 52b96b1a5..6f4a5fa95 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
@@ -30,11 +30,16 @@
import {
AgeCommitmentProof,
AmountJson,
+ AmountString,
CoinEnvelope,
DenominationPubKey,
+ EddsaPublicKeyString,
+ EddsaSignatureString,
ExchangeProtocolVersion,
RefreshPlanchetInfo,
+ TalerProtocolTimestamp,
UnblindedSignature,
+ WalletAccountMergeFlags,
} from "@gnu-taler/taler-util";
export interface RefreshNewDenomInfo {
@@ -148,4 +153,80 @@ export interface CreateRecoupRefreshReqRequest {
denomPub: DenominationPubKey;
denomPubHash: string;
denomSig: UnblindedSignature;
-} \ No newline at end of file
+}
+
+export interface EncryptedContract {
+ /**
+ * Encrypted contract.
+ */
+ econtract: string;
+
+ /**
+ * Signature over the (encrypted) contract.
+ */
+ econtract_sig: EddsaSignatureString;
+
+ /**
+ * Ephemeral public key for the DH operation to decrypt the encrypted contract.
+ */
+ contract_pub: EddsaPublicKeyString;
+}
+
+export interface EncryptContractRequest {
+ contractTerms: any;
+
+ pursePub: string;
+ pursePriv: string;
+
+ mergePriv: string;
+}
+
+export interface EncryptContractResponse {
+ econtract: EncryptedContract;
+
+ contractPriv: string;
+}
+
+export interface DecryptContractRequest {
+ ciphertext: string;
+ pursePub: string;
+ contractPriv: string;
+}
+
+export interface DecryptContractResponse {
+ contractTerms: any;
+ mergePriv: string;
+}
+
+export interface SignPurseMergeRequest {
+ mergeTimestamp: TalerProtocolTimestamp;
+
+ pursePub: string;
+
+ reservePayto: string;
+
+ reservePriv: string;
+
+ mergePriv: string;
+
+ purseExpiration: TalerProtocolTimestamp;
+
+ purseAmount: AmountString;
+ purseFee: AmountString;
+
+ contractTermsHash: string;
+
+ /**
+ * Flags.
+ */
+ flags: WalletAccountMergeFlags;
+}
+
+export interface SignPurseMergeResponse {
+ /**
+ * Signature made by the purse's merge private key.
+ */
+ mergeSig: string;
+
+ accountSig: string;
+}