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 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.
*/
@ -958,7 +965,7 @@ export interface AgeCommitment {
/**
* Public keys, one for each age group specified in the age mask.
*/
publicKeys: Edx25519PublicKey[];
publicKeys: Edx25519PublicKeyEnc[];
}
export interface AgeProof {
@ -966,7 +973,7 @@ export interface AgeProof {
* Private keys. Typically smaller than the number of public keys,
* because we drop private keys from age groups that are restricted.
*/
privateKeys: Edx25519PrivateKey[];
privateKeys: Edx25519PrivateKeyEnc[];
}
export interface AgeCommitmentProof {
@ -984,7 +991,7 @@ export namespace AgeRestriction {
export function hashCommitment(ac: AgeCommitment): HashCodeString {
const hc = new nacl.HashState();
for (const pub of ac.publicKeys) {
hc.update(pub);
hc.update(decodeCrock(pub));
}
return encodeCrock(hc.finish().subarray(0, 32));
}
@ -1042,10 +1049,10 @@ export namespace AgeRestriction {
return {
commitment: {
mask: ageMask,
publicKeys: pubs,
publicKeys: pubs.map((x) => encodeCrock(x)),
},
proof: {
privateKeys: privs,
privateKeys: privs.map((x) => encodeCrock(x)),
},
};
}
@ -1062,8 +1069,11 @@ export namespace AgeRestriction {
return false;
}
for (let i = 0; i < c1.publicKeys.length; i++) {
const k1 = c1.publicKeys[i];
const k2 = await Edx25519.publicKeyDerive(c2.publicKeys[i], salt);
const k1 = decodeCrock(c1.publicKeys[i]);
const k2 = await Edx25519.publicKeyDerive(
decodeCrock(c2.publicKeys[i]),
salt,
);
if (k1 != k2) {
return false;
}
@ -1079,20 +1089,22 @@ export namespace AgeRestriction {
const newPubs: Edx25519PublicKey[] = [];
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) {
newPrivs.push(await Edx25519.privateKeyDerive(oldPriv, salt));
newPrivs.push(
await Edx25519.privateKeyDerive(decodeCrock(oldPriv), salt),
);
}
return {
commitment: {
mask: commitmentProof.commitment.mask,
publicKeys: newPubs,
publicKeys: newPubs.map((x) => encodeCrock(x)),
},
proof: {
privateKeys: newPrivs,
privateKeys: newPrivs.map((x) => encodeCrock(x)),
},
};
}
@ -1112,7 +1124,11 @@ export namespace AgeRestriction {
}
const priv = commitmentProof.proof.privateKeys[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;
}
@ -1131,7 +1147,11 @@ export namespace AgeRestriction {
return true;
}
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.
*/
import { codecForAmountString } from "./amounts.js";
import {
buildCodecForObject,
codecForString,
codecForList,
codecOptional,
codecForAny,
codecForNumber,
codecForBoolean,
codecForMap,
Codec,
codecForConstNumber,
buildCodecForUnion,
Codec,
codecForAny,
codecForBoolean,
codecForConstNumber,
codecForConstString,
codecForList,
codecForMap,
codecForNumber,
codecForString,
codecOptional,
} from "./codec.js";
import {
codecForTimestamp,
codecForDuration,
TalerProtocolTimestamp,
TalerProtocolDuration,
} from "./time.js";
import { codecForAmountString } from "./amounts.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.
@ -287,7 +287,7 @@ export interface CoinDepositPermission {
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
* of public keys.
*/
old_age_commitment?: Edx25519PublicKey[];
old_age_commitment?: Edx25519PublicKeyEnc[];
}
export interface DepositSuccess {

View File

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

View File

@ -242,7 +242,7 @@ export function selectWithdrawalDenominations(
for (const d of denoms) {
let count = 0;
const cost = Amounts.add(d.value, d.feeWithdraw).amount;
for (; ;) {
for (;;) {
if (Amounts.cmp(remaining, cost) < 0) {
break;
}
@ -903,7 +903,8 @@ export async function updateWithdrawalDenoms(
denom.verificationStatus === DenominationVerificationStatus.Unverified
) {
logger.trace(
`Validating denomination (${current + 1}/${denominations.length
`Validating denomination (${current + 1}/${
denominations.length
}) signature of ${denom.denomPubHash}`,
);
let valid = false;
@ -1030,7 +1031,7 @@ async function queryReserve(
if (
resp.status === 404 &&
result.talerErrorResponse.code ===
TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
) {
ws.notify({
type: NotificationType.ReserveNotYetFound,
@ -1336,7 +1337,7 @@ export async function getExchangeWithdrawalInfo(
) {
logger.warn(
`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,
},
},
restrictAge: req.restrictAge,
forcedDenomSel: req.forcedDenomSel,
reserveStatus: ReserveRecordStatus.RegisteringBank,
});

View File

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