wallet-core: fix issue with crock encoding of age restrictions

This commit is contained in:
Florian Dold 2022-09-01 22:26:22 +02:00
parent d6a172c4a0
commit ec43b6a5bf
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
5 changed files with 68 additions and 45 deletions

View File

@ -857,6 +857,13 @@ export type Edx25519PublicKey = FlavorP<Uint8Array, "Edx25519PublicKey", 32>;
export type Edx25519PrivateKey = FlavorP<Uint8Array, "Edx25519PrivateKey", 64>; export type Edx25519PrivateKey = FlavorP<Uint8Array, "Edx25519PrivateKey", 64>;
export type Edx25519Signature = FlavorP<Uint8Array, "Edx25519Signature", 64>; export type Edx25519Signature = FlavorP<Uint8Array, "Edx25519Signature", 64>;
export type Edx25519PublicKeyEnc = FlavorP<string, "Edx25519PublicKeyEnc", 32>;
export type Edx25519PrivateKeyEnc = FlavorP<
string,
"Edx25519PrivateKeyEnc",
64
>;
/** /**
* Convert a big integer to a fixed-size, little-endian array. * Convert a big integer to a fixed-size, little-endian array.
*/ */
@ -958,7 +965,7 @@ export interface AgeCommitment {
/** /**
* Public keys, one for each age group specified in the age mask. * Public keys, one for each age group specified in the age mask.
*/ */
publicKeys: Edx25519PublicKey[]; publicKeys: Edx25519PublicKeyEnc[];
} }
export interface AgeProof { export interface AgeProof {
@ -966,7 +973,7 @@ export interface AgeProof {
* Private keys. Typically smaller than the number of public keys, * Private keys. Typically smaller than the number of public keys,
* because we drop private keys from age groups that are restricted. * because we drop private keys from age groups that are restricted.
*/ */
privateKeys: Edx25519PrivateKey[]; privateKeys: Edx25519PrivateKeyEnc[];
} }
export interface AgeCommitmentProof { export interface AgeCommitmentProof {
@ -984,7 +991,7 @@ export namespace AgeRestriction {
export function hashCommitment(ac: AgeCommitment): HashCodeString { export function hashCommitment(ac: AgeCommitment): HashCodeString {
const hc = new nacl.HashState(); const hc = new nacl.HashState();
for (const pub of ac.publicKeys) { for (const pub of ac.publicKeys) {
hc.update(pub); hc.update(decodeCrock(pub));
} }
return encodeCrock(hc.finish().subarray(0, 32)); return encodeCrock(hc.finish().subarray(0, 32));
} }
@ -1042,10 +1049,10 @@ export namespace AgeRestriction {
return { return {
commitment: { commitment: {
mask: ageMask, mask: ageMask,
publicKeys: pubs, publicKeys: pubs.map((x) => encodeCrock(x)),
}, },
proof: { proof: {
privateKeys: privs, privateKeys: privs.map((x) => encodeCrock(x)),
}, },
}; };
} }
@ -1062,8 +1069,11 @@ export namespace AgeRestriction {
return false; return false;
} }
for (let i = 0; i < c1.publicKeys.length; i++) { for (let i = 0; i < c1.publicKeys.length; i++) {
const k1 = c1.publicKeys[i]; const k1 = decodeCrock(c1.publicKeys[i]);
const k2 = await Edx25519.publicKeyDerive(c2.publicKeys[i], salt); const k2 = await Edx25519.publicKeyDerive(
decodeCrock(c2.publicKeys[i]),
salt,
);
if (k1 != k2) { if (k1 != k2) {
return false; return false;
} }
@ -1079,20 +1089,22 @@ export namespace AgeRestriction {
const newPubs: Edx25519PublicKey[] = []; const newPubs: Edx25519PublicKey[] = [];
for (const oldPub of commitmentProof.commitment.publicKeys) { for (const oldPub of commitmentProof.commitment.publicKeys) {
newPubs.push(await Edx25519.publicKeyDerive(oldPub, salt)); newPubs.push(await Edx25519.publicKeyDerive(decodeCrock(oldPub), salt));
} }
for (const oldPriv of commitmentProof.proof.privateKeys) { for (const oldPriv of commitmentProof.proof.privateKeys) {
newPrivs.push(await Edx25519.privateKeyDerive(oldPriv, salt)); newPrivs.push(
await Edx25519.privateKeyDerive(decodeCrock(oldPriv), salt),
);
} }
return { return {
commitment: { commitment: {
mask: commitmentProof.commitment.mask, mask: commitmentProof.commitment.mask,
publicKeys: newPubs, publicKeys: newPubs.map((x) => encodeCrock(x)),
}, },
proof: { proof: {
privateKeys: newPrivs, privateKeys: newPrivs.map((x) => encodeCrock(x)),
}, },
}; };
} }
@ -1112,7 +1124,11 @@ export namespace AgeRestriction {
} }
const priv = commitmentProof.proof.privateKeys[group - 1]; const priv = commitmentProof.proof.privateKeys[group - 1];
const pub = commitmentProof.commitment.publicKeys[group - 1]; const pub = commitmentProof.commitment.publicKeys[group - 1];
const sig = nacl.crypto_edx25519_sign_detached(d, priv, pub); const sig = nacl.crypto_edx25519_sign_detached(
d,
decodeCrock(priv),
decodeCrock(pub),
);
return sig; return sig;
} }
@ -1131,7 +1147,11 @@ export namespace AgeRestriction {
return true; return true;
} }
const pub = commitment.publicKeys[group - 1]; const pub = commitment.publicKeys[group - 1];
return nacl.crypto_edx25519_sign_detached_verify(d, decodeCrock(sig), pub); return nacl.crypto_edx25519_sign_detached_verify(
d,
decodeCrock(sig),
decodeCrock(pub),
);
} }
} }

View File

@ -25,29 +25,29 @@
* Imports. * Imports.
*/ */
import { codecForAmountString } from "./amounts.js";
import { import {
buildCodecForObject, buildCodecForObject,
codecForString,
codecForList,
codecOptional,
codecForAny,
codecForNumber,
codecForBoolean,
codecForMap,
Codec,
codecForConstNumber,
buildCodecForUnion, buildCodecForUnion,
Codec,
codecForAny,
codecForBoolean,
codecForConstNumber,
codecForConstString, codecForConstString,
codecForList,
codecForMap,
codecForNumber,
codecForString,
codecOptional,
} from "./codec.js"; } from "./codec.js";
import {
codecForTimestamp,
codecForDuration,
TalerProtocolTimestamp,
TalerProtocolDuration,
} from "./time.js";
import { codecForAmountString } from "./amounts.js";
import { strcmp } from "./helpers.js"; import { strcmp } from "./helpers.js";
import { AgeCommitmentProof, Edx25519PublicKey } from "./talerCrypto.js"; import { AgeCommitmentProof, Edx25519PublicKeyEnc } from "./talerCrypto.js";
import {
codecForDuration,
codecForTimestamp,
TalerProtocolDuration,
TalerProtocolTimestamp,
} from "./time.js";
/** /**
* Denomination as found in the /keys response from the exchange. * Denomination as found in the /keys response from the exchange.
@ -287,7 +287,7 @@ export interface CoinDepositPermission {
minimum_age_sig?: EddsaSignatureString; minimum_age_sig?: EddsaSignatureString;
age_commitment?: Edx25519PublicKey[]; age_commitment?: Edx25519PublicKeyEnc[];
} }
/** /**
@ -1755,7 +1755,7 @@ export interface ExchangeRefreshRevealRequest {
* the client MUST provide the original age commitment, i.e. the vector * the client MUST provide the original age commitment, i.e. the vector
* of public keys. * of public keys.
*/ */
old_age_commitment?: Edx25519PublicKey[]; old_age_commitment?: Edx25519PublicKeyEnc[];
} }
export interface DepositSuccess { export interface DepositSuccess {

View File

@ -1084,7 +1084,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
s.age_commitment = s.age_commitment =
depositInfo.ageCommitmentProof?.commitment.publicKeys; depositInfo.ageCommitmentProof?.commitment.publicKeys;
} else if (depositInfo.ageCommitmentProof) { } else if (depositInfo.ageCommitmentProof) {
(s as any).h_age_commitment = hAgeCommitment; (s as any).h_age_commitment = encodeCrock(hAgeCommitment);
} }
return s; return s;
@ -1518,9 +1518,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
}); });
logger.info(`payto URI: ${req.reservePayto}`); logger.info(`payto URI: ${req.reservePayto}`);
logger.info( logger.info(`signing WALLET_PURSE_MERGE over ${encodeCrock(mergeSigBlob)}`);
`signing WALLET_PURSE_MERGE over ${encodeCrock(mergeSigBlob)}`,
);
const reserveSigBlob = buildSigPS( const reserveSigBlob = buildSigPS(
TalerSignaturePurpose.WALLET_ACCOUNT_MERGE, TalerSignaturePurpose.WALLET_ACCOUNT_MERGE,

View File

@ -242,7 +242,7 @@ export function selectWithdrawalDenominations(
for (const d of denoms) { for (const d of denoms) {
let count = 0; let count = 0;
const cost = Amounts.add(d.value, d.feeWithdraw).amount; const cost = Amounts.add(d.value, d.feeWithdraw).amount;
for (; ;) { for (;;) {
if (Amounts.cmp(remaining, cost) < 0) { if (Amounts.cmp(remaining, cost) < 0) {
break; break;
} }
@ -903,7 +903,8 @@ export async function updateWithdrawalDenoms(
denom.verificationStatus === DenominationVerificationStatus.Unverified denom.verificationStatus === DenominationVerificationStatus.Unverified
) { ) {
logger.trace( logger.trace(
`Validating denomination (${current + 1}/${denominations.length `Validating denomination (${current + 1}/${
denominations.length
}) signature of ${denom.denomPubHash}`, }) signature of ${denom.denomPubHash}`,
); );
let valid = false; let valid = false;
@ -1030,7 +1031,7 @@ async function queryReserve(
if ( if (
resp.status === 404 && resp.status === 404 &&
result.talerErrorResponse.code === result.talerErrorResponse.code ===
TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
) { ) {
ws.notify({ ws.notify({
type: NotificationType.ReserveNotYetFound, type: NotificationType.ReserveNotYetFound,
@ -1336,7 +1337,7 @@ export async function getExchangeWithdrawalInfo(
) { ) {
logger.warn( logger.warn(
`wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` + `wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` +
`(exchange has ${exchangeDetails.protocolVersion}), checking for updates`, `(exchange has ${exchangeDetails.protocolVersion}), checking for updates`,
); );
} }
} }
@ -1869,6 +1870,7 @@ export async function acceptWithdrawalFromUri(
confirmUrl: withdrawInfo.confirmTransferUrl, confirmUrl: withdrawInfo.confirmTransferUrl,
}, },
}, },
restrictAge: req.restrictAge,
forcedDenomSel: req.forcedDenomSel, forcedDenomSel: req.forcedDenomSel,
reserveStatus: ReserveRecordStatus.RegisteringBank, reserveStatus: ReserveRecordStatus.RegisteringBank,
}); });

View File

@ -291,11 +291,14 @@ export function selectPayCoins(
aci.denomPub.age_mask, aci.denomPub.age_mask,
req.requiredMinimumAge, req.requiredMinimumAge,
); );
if (!aci.ageCommitmentProof) { // if (!aci.ageCommitmentProof) {
// No age restriction, can't use for this payment // // No age restriction, can't use for this payment
continue; // continue;
} // }
if (aci.ageCommitmentProof.proof.privateKeys.length < index) { if (
aci.ageCommitmentProof &&
aci.ageCommitmentProof.proof.privateKeys.length < index
) {
// Available age proofs to low, can't use for this payment // Available age proofs to low, can't use for this payment
continue; continue;
} }