[age-withdraw] WiP: first types and adjustments

This commit is contained in:
Özgür Kesim 2023-08-15 13:48:37 +02:00
parent 819949d7f2
commit 70fca92e78
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
4 changed files with 105 additions and 24 deletions

View File

@ -1254,30 +1254,9 @@ export namespace AgeRestriction {
age: number, age: number,
): Promise<AgeCommitmentProof> { ): Promise<AgeCommitmentProof> {
invariant((ageMask & 1) === 1); invariant((ageMask & 1) === 1);
const numPubs = countAgeGroups(ageMask) - 1; const seed = getRandomBytes(32);
const numPrivs = getAgeGroupIndex(ageMask, age);
const pubs: Edx25519PublicKey[] = []; return restrictionCommitSeeded(ageMask, age, seed);
const privs: Edx25519PrivateKey[] = [];
for (let i = 0; i < numPubs; i++) {
const priv = await Edx25519.keyCreate();
const pub = await Edx25519.getPublic(priv);
pubs.push(pub);
if (i < numPrivs) {
privs.push(priv);
}
}
return {
commitment: {
mask: ageMask,
publicKeys: pubs.map((x) => encodeCrock(x)),
},
proof: {
privateKeys: privs.map((x) => encodeCrock(x)),
},
};
} }
const PublishedAgeRestrictionBaseKey: Edx25519PublicKey = decodeCrock( const PublishedAgeRestrictionBaseKey: Edx25519PublicKey = decodeCrock(

View File

@ -1788,6 +1788,89 @@ export interface ExchangeRefreshRevealRequest {
old_age_commitment?: Edx25519PublicKeyEnc[]; old_age_commitment?: Edx25519PublicKeyEnc[];
} }
export interface ExchangeAgeWithdrawRequest {
// Array of n hash codes of denomination public keys to order.
// These denominations MUST support age restriction as defined in the
// output to /keys.
// The sum of all denomination's values and fees MUST be at most the
// balance of the reserve. The balance of the reserve will be
// immediatley reduced by that amount.
denoms_h: HashCodeString[];
// n arrays of kappa entries with blinded coin envelopes. Each
// (toplevel) entry represents kappa canditates for a particular
// coin. The exchange will respond with an index gamma, which is
// the index that shall remain undisclosed during the reveal phase.
// The SHA512 hash $ACH over the blinded coin envelopes is the commitment
// that is later used as the key to the reveal-URL.
blinded_coins_evs: CoinEnvelope[][];
// The maximum age to commit to. MUST be the same as the maximum
// age value assigned to the reserve, based on its birthday date.
max_age: number;
// Signature of TALER_AgeWithdrawRequestPS created with
// the reserves's private key
// using purpose TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW.
reserve_sig: EddsaSignatureString;
}
export interface ExchangeAgeWithdrawResponse {
// index of the commitments that the client doesn't
// have to disclose
noreveal_index: number;
// Signature of TALER_AgeWithdrawConfirmationPS whereby
// the exchange confirms the noreveal_index.
exchange_sig: EddsaSignatureString;
// Public EdDSA key of the exchange that was used to
// generate the signature. Should match one of the exchange's signing
// keys from /keys. Again given explicitly as the client might
// otherwise be confused by clock skew as to which signing key was used.
exchange_pub: EddsaPublicKeyString;
}
export interface ExchangeAgeWithdrawRevealRequest {
// Array of n of (kappa - 1) disclosed coin master secrets, from
// which the coins' private key, blinding, nonce (for Clause-Schnorr) and
// age-restriction is calculated.
//
// Given each coin's private key and age commitment, the exchange will
// calculate each coin's blinded hash value und use all those (disclosed)
// blinded hashes together with the non-disclosed envelopes coin_evs
// during the verification of the original age-withdraw-commitment.
disclosed_coin_secrets: AgeRestrictedCoinSecret[][];
}
// The Master key material from which the coins' private key coin_priv,
// blinding beta and nonce nonce (for Clause-Schnorr) itself are
// derived as usually in wallet-core. Given a coin's master key material,
// the age commitment for the coin MUST be derived from this private key as
// follows:
//
// Let m ∈ {1,...,M} be the maximum age group as defined in the reserve
// that the wallet can commit to.
//
// For age group $AG ∈ {1,...m}, set
// seed = HDKF(coin_secret, "age-commitment", $AG)
// p[$AG] = Edx25519_generate_private(seed)
// and calculate the corresponding Edx25519PublicKey as
// q[$AG] = Edx25519_public_from_private(p[$AG])
//
// For age groups $AG ∈ {m,...,M}, set
// f[$AG] = HDKF(coin_secret, "age-factor", $AG)
// and calculate the corresponding Edx25519PublicKey as
// q[$AG] = Edx25519_derive_public(PublishedAgeRestrictionBaseKey, f[$AG])
//
// FIXME: shall we add some flavor to this string?
export type AgeRestrictedCoinSecret = string;
export interface ExchangeAgeWithdrawRevealResponse {
// List of the exchange's blinded RSA or CS signatures on the new coins.
ev_sigs : BlindedDenominationSignature[];
}
export interface DepositSuccess { export interface DepositSuccess {
// Optional base URL of the exchange for looking up wire transfers // Optional base URL of the exchange for looking up wire transfers
// associated with this transaction. If not given, // associated with this transaction. If not given,

View File

@ -657,6 +657,7 @@ export interface PlanchetRecord {
*/ */
coinIdx: number; coinIdx: number;
planchetStatus: PlanchetStatus; planchetStatus: PlanchetStatus;
lastError: TalerErrorDetail | undefined; lastError: TalerErrorDetail | undefined;
@ -671,6 +672,12 @@ export interface PlanchetRecord {
coinEvHash: string; coinEvHash: string;
/**
* Index into the kappa-many planchet commitments per coin
* for the age-withdraw operation.
*/
ageWithdrawIdx?: number;
ageCommitmentProof?: AgeCommitmentProof; ageCommitmentProof?: AgeCommitmentProof;
} }
@ -1423,7 +1430,7 @@ export interface KycPendingInfo {
} }
/** /**
* Group of withdrawal operations that need to be executed. * Group of withdrawal operations that need to be executed.
* (Either for a normal withdrawal or from a reward.) * (Either for a normal {single-|batch-|age-} withdrawal or from a reward.)
* *
* The withdrawal group record is only created after we know * The withdrawal group record is only created after we know
* the coin selection we want to withdraw. * the coin selection we want to withdraw.
@ -2513,6 +2520,11 @@ export const WalletStoresV1 = {
"planchets", "planchets",
describeContents<PlanchetRecord>({ keyPath: "coinPub" }), describeContents<PlanchetRecord>({ keyPath: "coinPub" }),
{ {
byGroupAgeCoin: describeIndex("byGroupAgeCoin", [
"withdrawalGroupId",
"ageWithdrawIdx",
"coinIdx",
]),
byGroupAndIndex: describeIndex("byGroupAndIndex", [ byGroupAndIndex: describeIndex("byGroupAndIndex", [
"withdrawalGroupId", "withdrawalGroupId",
"coinIdx", "coinIdx",

View File

@ -62,6 +62,10 @@ import {
ExchangeWithdrawResponse, ExchangeWithdrawResponse,
WithdrawUriInfoResponse, WithdrawUriInfoResponse,
ExchangeBatchWithdrawRequest, ExchangeBatchWithdrawRequest,
ExchangeAgeWithdrawRequest,
ExchangeAgeWithdrawRevealRequest,
ExchangeAgeWithdrawResponse,
ExchangeAgeWithdrawRevealResponse,
TransactionState, TransactionState,
TransactionMajorState, TransactionMajorState,
TransactionMinorState, TransactionMinorState,
@ -861,6 +865,7 @@ async function processPlanchetExchangeBatchRequest(
coinIdx < wgContext.numPlanchets; coinIdx < wgContext.numPlanchets;
coinIdx++ coinIdx++
) { ) {
// FIXME[oec]: Add lookup of planchet for age-withdraw here
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([ let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
withdrawalGroup.withdrawalGroupId, withdrawalGroup.withdrawalGroupId,
coinIdx, coinIdx,
@ -923,6 +928,8 @@ async function processPlanchetExchangeBatchRequest(
// FIXME: handle individual error codes better! // FIXME: handle individual error codes better!
// FIXME[oec]: add age-withdraw-request here
if (args.useBatchRequest) { if (args.useBatchRequest) {
const reqUrl = new URL( const reqUrl = new URL(
`reserves/${withdrawalGroup.reservePub}/batch-withdraw`, `reserves/${withdrawalGroup.reservePub}/batch-withdraw`,