summaryrefslogtreecommitdiff
path: root/packages/taler-util
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-11-27 20:56:58 +0100
committerFlorian Dold <florian@dold.me>2021-11-27 20:57:07 +0100
commit5c4c25516df9d65d29dc7f3f38b5a2a1a8e9e374 (patch)
tree4665e79a6033ab949de211fd9de8de8ca681c2e0 /packages/taler-util
parent403de8170ef538ef74505859b1c04e3542cad9fb (diff)
downloadwallet-core-5c4c25516df9d65d29dc7f3f38b5a2a1a8e9e374.tar.gz
wallet-core-5c4c25516df9d65d29dc7f3f38b5a2a1a8e9e374.tar.bz2
wallet-core-5c4c25516df9d65d29dc7f3f38b5a2a1a8e9e374.zip
wallet: support both protocol versions
Diffstat (limited to 'packages/taler-util')
-rw-r--r--packages/taler-util/src/codec.ts23
-rw-r--r--packages/taler-util/src/libtool-version.ts7
-rw-r--r--packages/taler-util/src/logging.ts3
-rw-r--r--packages/taler-util/src/talerCrypto.ts27
-rw-r--r--packages/taler-util/src/talerTypes.ts111
-rw-r--r--packages/taler-util/src/walletTypes.ts2
6 files changed, 139 insertions, 34 deletions
diff --git a/packages/taler-util/src/codec.ts b/packages/taler-util/src/codec.ts
index 8605ff335..2ea64a249 100644
--- a/packages/taler-util/src/codec.ts
+++ b/packages/taler-util/src/codec.ts
@@ -417,3 +417,26 @@ export function codecOptional<V>(innerCodec: Codec<V>): Codec<V | undefined> {
},
};
}
+
+export type CodecType<T> = T extends Codec<infer X> ? X : any;
+
+export function codecForEither<T extends Array<Codec<unknown>>>(
+ ...alts: [...T]
+): Codec<CodecType<T[number]>> {
+ return {
+ decode(x: any, c?: Context): any {
+ for (const alt of alts) {
+ try {
+ return alt.decode(x, c);
+ } catch (e) {
+ continue;
+ }
+ }
+ throw new DecodingError(
+ `No alternative matched at at ${renderContext(c)}`,
+ );
+ },
+ };
+}
+
+const x = codecForEither(codecForString(), codecForNumber());
diff --git a/packages/taler-util/src/libtool-version.ts b/packages/taler-util/src/libtool-version.ts
index 17d2bbbdc..ed11a4e95 100644
--- a/packages/taler-util/src/libtool-version.ts
+++ b/packages/taler-util/src/libtool-version.ts
@@ -27,14 +27,15 @@ export interface VersionMatchResult {
* Is the first version compatible with the second?
*/
compatible: boolean;
+
/**
- * Is the first version older (-1), newser (+1) or
+ * Is the first version older (-1), newer (+1) or
* identical (0)?
*/
currentCmp: number;
}
-interface Version {
+export interface Version {
current: number;
revision: number;
age: number;
@@ -64,7 +65,7 @@ export namespace LibtoolVersion {
return { compatible, currentCmp };
}
- function parseVersion(v: string): Version | undefined {
+ export function parseVersion(v: string): Version | undefined {
const [currentStr, revisionStr, ageStr, ...rest] = v.split(":");
if (rest.length !== 0) {
return undefined;
diff --git a/packages/taler-util/src/logging.ts b/packages/taler-util/src/logging.ts
index 8b9de1ab0..117664d8c 100644
--- a/packages/taler-util/src/logging.ts
+++ b/packages/taler-util/src/logging.ts
@@ -55,7 +55,7 @@ export function setGlobalLogLevelFromString(logLevelStr: string) {
break;
default:
if (isNode) {
- process.stderr.write(`Invalid log level, defaulting to WARNING`);
+ process.stderr.write(`Invalid log level, defaulting to WARNING\n`);
} else {
console.warn(`Invalid log level, defaulting to WARNING`);
}
@@ -143,6 +143,7 @@ export class Logger {
case LogLevel.Info:
case LogLevel.Warn:
case LogLevel.Error:
+ return true;
case LogLevel.None:
return false;
}
diff --git a/packages/taler-util/src/talerCrypto.ts b/packages/taler-util/src/talerCrypto.ts
index c20ce72a6..d96c23236 100644
--- a/packages/taler-util/src/talerCrypto.ts
+++ b/packages/taler-util/src/talerCrypto.ts
@@ -349,18 +349,25 @@ export function hash(d: Uint8Array): Uint8Array {
return nacl.hash(d);
}
+/**
+ * Hash a denomination public key according to the
+ * algorithm of exchange protocol v10.
+ */
export function hashDenomPub(pub: DenominationPubKey): Uint8Array {
- if (pub.cipher !== DenomKeyType.Rsa) {
- throw Error("unsupported cipher");
+ if (pub.cipher === DenomKeyType.Rsa) {
+ const pubBuf = decodeCrock(pub.rsa_public_key);
+ const hashInputBuf = new ArrayBuffer(pubBuf.length + 4 + 4);
+ const uint8ArrayBuf = new Uint8Array(hashInputBuf);
+ const dv = new DataView(hashInputBuf);
+ dv.setUint32(0, pub.age_mask ?? 0);
+ dv.setUint32(4, pub.cipher);
+ uint8ArrayBuf.set(pubBuf, 8);
+ return nacl.hash(uint8ArrayBuf);
+ } else if (pub.cipher === DenomKeyType.LegacyRsa) {
+ return hash(decodeCrock(pub.rsa_public_key));
+ } else {
+ throw Error(`unsupported cipher (${pub.cipher}), unable to hash`);
}
- const pubBuf = decodeCrock(pub.rsa_public_key);
- const hashInputBuf = new ArrayBuffer(pubBuf.length + 4 + 4);
- const uint8ArrayBuf = new Uint8Array(hashInputBuf);
- const dv = new DataView(hashInputBuf);
- dv.setUint32(0, pub.age_mask ?? 0);
- dv.setUint32(4, pub.cipher);
- uint8ArrayBuf.set(pubBuf, 8);
- return nacl.hash(uint8ArrayBuf);
}
export function eddsaSign(msg: Uint8Array, eddsaPriv: Uint8Array): Uint8Array {
diff --git a/packages/taler-util/src/talerTypes.ts b/packages/taler-util/src/talerTypes.ts
index bd9c67d7e..15dc88ca5 100644
--- a/packages/taler-util/src/talerTypes.ts
+++ b/packages/taler-util/src/talerTypes.ts
@@ -38,6 +38,7 @@ import {
codecForConstNumber,
buildCodecForUnion,
codecForConstString,
+ codecForEither,
} from "./codec.js";
import {
Timestamp,
@@ -50,7 +51,7 @@ import { codecForAmountString } from "./amounts.js";
/**
* Denomination as found in the /keys response from the exchange.
*/
-export class Denomination {
+export class ExchangeDenomination {
/**
* Value of one coin of the denomination.
*/
@@ -58,8 +59,11 @@ export class Denomination {
/**
* Public signing key of the denomination.
+ *
+ * The "string" alternative is for the old exchange protocol (v9) that
+ * only supports RSA keys.
*/
- denom_pub: DenominationPubKey;
+ denom_pub: DenominationPubKey | string;
/**
* Fee for withdrawing.
@@ -128,7 +132,7 @@ export class AuditorDenomSig {
/**
* Auditor information as given by the exchange in /keys.
*/
-export class Auditor {
+export class ExchangeAuditor {
/**
* Auditor's public key.
*/
@@ -157,8 +161,10 @@ export interface RecoupRequest {
/**
* Signature over the coin public key by the denomination.
+ *
+ * The string variant is for the legacy exchange protocol.
*/
- denom_sig: UnblindedSignature;
+ denom_sig: UnblindedSignature | string;
/**
* Coin public key of the coin we want to refund.
@@ -198,11 +204,20 @@ export interface RecoupConfirmation {
old_coin_pub?: string;
}
-export interface UnblindedSignature {
+export type UnblindedSignature =
+ | RsaUnblindedSignature
+ | LegacyRsaUnblindedSignature;
+
+export interface RsaUnblindedSignature {
cipher: DenomKeyType.Rsa;
rsa_signature: string;
}
+export interface LegacyRsaUnblindedSignature {
+ cipher: DenomKeyType.LegacyRsa;
+ rsa_signature: string;
+}
+
/**
* Deposit permission for a single coin.
*/
@@ -211,18 +226,25 @@ export interface CoinDepositPermission {
* Signature by the coin.
*/
coin_sig: string;
+
/**
* Public key of the coin being spend.
*/
coin_pub: string;
+
/**
* Signature made by the denomination public key.
+ *
+ * The string variant is for legacy protocol support.
*/
- ub_sig: UnblindedSignature;
+
+ ub_sig: UnblindedSignature | string;
+
/**
* The denomination public key associated with this coin.
*/
h_denom: string;
+
/**
* The amount that is subtracted from this coin with this payment.
*/
@@ -359,6 +381,11 @@ export interface ContractTerms {
h_wire: string;
/**
+ * Legacy wire hash, used for deposit operations with an older exchange.
+ */
+ h_wire_legacy?: string;
+
+ /**
* Hash of the merchant's wire details.
*/
auto_refund?: Duration;
@@ -662,7 +689,7 @@ export class ExchangeKeysJson {
/**
* List of offered denominations.
*/
- denoms: Denomination[];
+ denoms: ExchangeDenomination[];
/**
* The exchange's master public key.
@@ -672,7 +699,7 @@ export class ExchangeKeysJson {
/**
* The list of auditors (partially) auditing the exchange.
*/
- auditors: Auditor[];
+ auditors: ExchangeAuditor[];
/**
* Timestamp when this response was issued.
@@ -802,6 +829,7 @@ export class TipPickupGetResponse {
export enum DenomKeyType {
Rsa = 1,
ClauseSchnorr = 2,
+ LegacyRsa = 3,
}
export interface RsaBlindedDenominationSignature {
@@ -809,18 +837,25 @@ export interface RsaBlindedDenominationSignature {
blinded_rsa_signature: string;
}
+export interface LegacyRsaBlindedDenominationSignature {
+ cipher: DenomKeyType.LegacyRsa;
+ blinded_rsa_signature: string;
+}
+
export interface CSBlindedDenominationSignature {
cipher: DenomKeyType.ClauseSchnorr;
}
export type BlindedDenominationSignature =
| RsaBlindedDenominationSignature
- | CSBlindedDenominationSignature;
+ | CSBlindedDenominationSignature
+ | LegacyRsaBlindedDenominationSignature;
export const codecForBlindedDenominationSignature = () =>
buildCodecForUnion<BlindedDenominationSignature>()
.discriminateOn("cipher")
.alternative(1, codecForRsaBlindedDenominationSignature())
+ .alternative(3, codecForLegacyRsaBlindedDenominationSignature())
.build("BlindedDenominationSignature");
export const codecForRsaBlindedDenominationSignature = () =>
@@ -829,8 +864,17 @@ export const codecForRsaBlindedDenominationSignature = () =>
.property("blinded_rsa_signature", codecForString())
.build("RsaBlindedDenominationSignature");
+export const codecForLegacyRsaBlindedDenominationSignature = () =>
+ buildCodecForObject<LegacyRsaBlindedDenominationSignature>()
+ .property("cipher", codecForConstNumber(1))
+ .property("blinded_rsa_signature", codecForString())
+ .build("LegacyRsaBlindedDenominationSignature");
+
export class WithdrawResponse {
- ev_sig: BlindedDenominationSignature;
+ /**
+ * The string variant is for legacy protocol support.
+ */
+ ev_sig: BlindedDenominationSignature | string;
}
/**
@@ -925,7 +969,10 @@ export interface ExchangeMeltResponse {
}
export interface ExchangeRevealItem {
- ev_sig: BlindedDenominationSignature;
+ /**
+ * The string variant is for the legacy v9 protocol.
+ */
+ ev_sig: BlindedDenominationSignature | string;
}
export interface ExchangeRevealResponse {
@@ -1044,7 +1091,15 @@ export interface BankWithdrawalOperationPostResponse {
transfer_done: boolean;
}
-export type DenominationPubKey = RsaDenominationPubKey | CsDenominationPubKey;
+export type DenominationPubKey =
+ | RsaDenominationPubKey
+ | CsDenominationPubKey
+ | LegacyRsaDenominationPubKey;
+
+export interface LegacyRsaDenominationPubKey {
+ cipher: DenomKeyType.LegacyRsa;
+ rsa_public_key: string;
+}
export interface RsaDenominationPubKey {
cipher: DenomKeyType.Rsa;
@@ -1061,6 +1116,7 @@ export const codecForDenominationPubKey = () =>
buildCodecForUnion<DenominationPubKey>()
.discriminateOn("cipher")
.alternative(1, codecForRsaDenominationPubKey())
+ .alternative(3, codecForLegacyRsaDenominationPubKey())
.build("DenominationPubKey");
export const codecForRsaDenominationPubKey = () =>
@@ -1069,6 +1125,12 @@ export const codecForRsaDenominationPubKey = () =>
.property("rsa_public_key", codecForString())
.build("DenominationPubKey");
+export const codecForLegacyRsaDenominationPubKey = () =>
+ buildCodecForObject<LegacyRsaDenominationPubKey>()
+ .property("cipher", codecForConstNumber(3))
+ .property("rsa_public_key", codecForString())
+ .build("LegacyRsaDenominationPubKey");
+
export const codecForBankWithdrawalOperationPostResponse = (): Codec<BankWithdrawalOperationPostResponse> =>
buildCodecForObject<BankWithdrawalOperationPostResponse>()
.property("transfer_done", codecForBoolean())
@@ -1080,10 +1142,13 @@ export type EddsaSignatureString = string;
export type EddsaPublicKeyString = string;
export type CoinPublicKeyString = string;
-export const codecForDenomination = (): Codec<Denomination> =>
- buildCodecForObject<Denomination>()
+export const codecForDenomination = (): Codec<ExchangeDenomination> =>
+ buildCodecForObject<ExchangeDenomination>()
.property("value", codecForString())
- .property("denom_pub", codecForDenominationPubKey())
+ .property(
+ "denom_pub",
+ codecForEither(codecForDenominationPubKey(), codecForString()),
+ )
.property("fee_withdraw", codecForString())
.property("fee_deposit", codecForString())
.property("fee_refresh", codecForString())
@@ -1101,8 +1166,8 @@ export const codecForAuditorDenomSig = (): Codec<AuditorDenomSig> =>
.property("auditor_sig", codecForString())
.build("AuditorDenomSig");
-export const codecForAuditor = (): Codec<Auditor> =>
- buildCodecForObject<Auditor>()
+export const codecForAuditor = (): Codec<ExchangeAuditor> =>
+ buildCodecForObject<ExchangeAuditor>()
.property("auditor_pub", codecForString())
.property("auditor_url", codecForString())
.property("denomination_keys", codecForList(codecForAuditorDenomSig()))
@@ -1261,7 +1326,7 @@ export const codecForExchangeKeysJson = (): Codec<ExchangeKeysJson> =>
.property("signkeys", codecForList(codecForExchangeSigningKey()))
.property("version", codecForString())
.property("reserve_closing_delay", codecForDuration)
- .build("KeysJson");
+ .build("ExchangeKeysJson");
export const codecForWireFeesJson = (): Codec<WireFeesJson> =>
buildCodecForObject<WireFeesJson>()
@@ -1327,7 +1392,10 @@ export const codecForRecoupConfirmation = (): Codec<RecoupConfirmation> =>
export const codecForWithdrawResponse = (): Codec<WithdrawResponse> =>
buildCodecForObject<WithdrawResponse>()
- .property("ev_sig", codecForBlindedDenominationSignature())
+ .property(
+ "ev_sig",
+ codecForEither(codecForBlindedDenominationSignature(), codecForString()),
+ )
.build("WithdrawResponse");
export const codecForMerchantPayResponse = (): Codec<MerchantPayResponse> =>
@@ -1345,7 +1413,10 @@ export const codecForExchangeMeltResponse = (): Codec<ExchangeMeltResponse> =>
export const codecForExchangeRevealItem = (): Codec<ExchangeRevealItem> =>
buildCodecForObject<ExchangeRevealItem>()
- .property("ev_sig", codecForBlindedDenominationSignature())
+ .property(
+ "ev_sig",
+ codecForEither(codecForBlindedDenominationSignature(), codecForString()),
+ )
.build("ExchangeRevealItem");
export const codecForExchangeRevealResponse = (): Codec<ExchangeRevealResponse> =>
diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts
index f00e2907f..ced30e4db 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -49,6 +49,7 @@ import {
codecForContractTerms,
ContractTerms,
DenominationPubKey,
+ DenomKeyType,
UnblindedSignature,
} from "./talerTypes.js";
import { OrderShortInfo, codecForOrderShortInfo } from "./transactionsTypes.js";
@@ -515,6 +516,7 @@ export interface DepositInfo {
merchantPub: string;
feeDeposit: AmountJson;
wireInfoHash: string;
+ denomKeyType: DenomKeyType;
denomPubHash: string;
denomSig: UnblindedSignature;
}