commit c085f242e6062070e7a6a5413e71e94ed49bea3c
parent acd8d1da4cdfa4d6036a4ec9928962dd2417bc50
Author: Florian Dold <florian@dold.me>
Date: Fri, 25 Apr 2025 16:32:30 +0200
util: fix planchet derivation for refreshV2
Diffstat:
1 file changed, 53 insertions(+), 5 deletions(-)
diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
@@ -233,6 +233,10 @@ export interface TalerCryptoInterface {
req: SetupRefreshPlanchetRequest,
): Promise<FreshCoinEncoded>;
+ setupRefreshPlanchetV2(
+ req: SetupRefreshPlanchetRequestV2,
+ ): Promise<FreshCoinEncoded>;
+
setupWithdrawalPlanchet(
req: SetupWithdrawalPlanchetRequest,
): Promise<FreshCoinEncoded>;
@@ -428,6 +432,11 @@ export const nullCrypto: TalerCryptoInterface = {
): Promise<FreshCoinEncoded> {
throw new Error("Function not implemented.");
},
+ setupRefreshPlanchetV2: function (
+ req: SetupRefreshPlanchetRequestV2,
+ ): Promise<FreshCoinEncoded> {
+ throw new Error("Function not implemented.");
+ },
rsaBlind: function (req: RsaBlindRequest): Promise<RsaBlindResponse> {
throw new Error("Function not implemented.");
},
@@ -557,6 +566,15 @@ export interface SetupRefreshPlanchetRequest {
coinNumber: number;
}
+export interface SetupRefreshPlanchetRequestV2 {
+ /**
+ * Refresh planchet secret, derived from the
+ * secret signature made by the old coin.
+ */
+ refreshPlanchetSecret: string;
+ coinNumber: number;
+}
+
export interface SetupWithdrawalPlanchetRequest {
secretSeed: string;
coinNumber: number;
@@ -840,6 +858,36 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
};
},
+ async setupRefreshPlanchetV2(
+ tci: TalerCryptoInterfaceR,
+ req: SetupRefreshPlanchetRequestV2,
+ ): Promise<FreshCoinEncoded> {
+ const planchetMasterSecret = decodeCrock(req.refreshPlanchetSecret);
+
+ const coinPriv = kdfKw({
+ ikm: planchetMasterSecret,
+ outputLength: 32,
+ salt: stringToBytes("coin"),
+ });
+
+ const bks = kdfKw({
+ ikm: planchetMasterSecret,
+ outputLength: 32,
+ salt: stringToBytes("bks"),
+ });
+
+ const coinPrivEnc = encodeCrock(coinPriv);
+ const coinPubRes = await tci.eddsaGetPublic(tci, {
+ priv: coinPrivEnc,
+ });
+
+ return {
+ bks: encodeCrock(bks),
+ coinPriv: coinPrivEnc,
+ coinPub: coinPubRes.pub,
+ };
+ },
+
async setupWithdrawalPlanchet(
tci: TalerCryptoInterfaceR,
req: SetupWithdrawalPlanchetRequest,
@@ -1539,7 +1587,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
});
signatures.push(sig.sig);
- const planchetSecretBytes = kdfKw({
+ const planchetSecretsBytes = kdfKw({
outputLength: 32 * numCoins,
salt: stringToBytes("refresh-planchet-secret"),
ikm: decodeCrock(sig.sig),
@@ -1549,20 +1597,20 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
const denomSel = newCoinDenoms[j];
for (let k = 0; k < newCoinDenoms[j].count; k++) {
const coinIndex = planchets.length;
- const mySecret = planchetSecretBytes.slice(
+ const myPlanchetSecret = planchetSecretsBytes.slice(
32 * coinIndex,
32 * coinIndex + 32,
);
- let fresh: FreshCoinEncoded = await tci.setupRefreshPlanchet(tci, {
+ let fresh: FreshCoinEncoded = await tci.setupRefreshPlanchetV2(tci, {
coinNumber: coinIndex,
- transferSecret: encodeCrock(mySecret),
+ refreshPlanchetSecret: encodeCrock(myPlanchetSecret),
});
let newAc: AgeCommitmentProof | undefined = undefined;
let newAch: HashCodeString | undefined = undefined;
if (req.meltCoinAgeCommitmentProof) {
newAc = await AgeRestriction.commitmentDerive(
req.meltCoinAgeCommitmentProof,
- mySecret,
+ myPlanchetSecret,
);
newAch = AgeRestriction.hashCommitment(newAc.commitment);
}