commit cf65329a025237308e6a28cd321bac7f05769a22
parent afb8d939ff578f354c46a26cc6c44392bc27ef8f
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Tue, 11 Nov 2025 16:26:10 +0100
update to revamped mailbox API
Diffstat:
3 files changed, 63 insertions(+), 71 deletions(-)
diff --git a/packages/taler-util/src/http-client/mailbox.ts b/packages/taler-util/src/http-client/mailbox.ts
@@ -23,7 +23,6 @@ import {
OperationOk,
ResultByMethod,
TalerMailboxApi,
- MailboxMessageDeletionRequest,
carefullyParseConfig,
codecForTalerMailboxConfigResponse,
codecForTalerMailboxRateLimitedResponse,
@@ -32,18 +31,16 @@ import {
opKnownAlternativeHttpFailure,
opKnownHttpFailure,
opUnknownHttpFailure,
- TalerMailboxConfigResponse,
- MailboxMessageKeys,
- codecForTalerMailboxMessageKeys,
+ MailboxMetadata as MailboxMetadata,
+ codecForTalerMailboxMetadata,
opSuccessFromHttp,
- EmptyObject,
- MailboxMessageKeysUpdateRequest,
+ MailboxRegisterRequest,
encodeCrock,
- sha512,
decodeCrock,
codecForEmptyObject,
MailboxConfiguration,
eddsaGetPublic,
+ EddsaSignatureString,
} from "@gnu-taler/taler-util";
import {
HttpRequestLibrary,
@@ -203,21 +200,23 @@ export class TalerMailboxInstanceHttpClient {
async deleteMessages(args: {
mailboxConf: MailboxConfiguration;
matchIf: string;
- body: MailboxMessageDeletionRequest;
+ count: number;
+ signature: EddsaSignatureString;
}): Promise<
| OperationOk<void>
| OperationFail<HttpStatusCode.Forbidden>
| OperationFail<HttpStatusCode.NotFound>
>{
- const { mailboxConf, matchIf: etag, body } = args;
+ const { mailboxConf, matchIf: etag, count: count, signature: signature } = args;
const mailboxPubkeyString = encodeCrock(eddsaGetPublic(decodeCrock(mailboxConf.privateKey)));
- const url = new URL(`private/${mailboxPubkeyString.toUpperCase()}`, this.baseUrl);
+ const url = new URL(`${mailboxPubkeyString.toUpperCase()}?count=${count}`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
- method: "POST",
- body: body,
- headers: { "If-Match" : etag },
+ method: "DELETE",
+ headers: {
+ "If-Match" : etag,
+ "Taler-Mailbox-Delete-Signature" : signature},
cancellationToken: this.cancellationToken,
});
@@ -234,14 +233,14 @@ export class TalerMailboxInstanceHttpClient {
}
/**
- * https://docs.taler.net/core/api-mailbox.html#key-directory
+ * https://docs.taler.net/core/api-mailbox.html#get--info-$H_MAILBOX
*/
- async getKeys(hMailbox: string) : Promise<
- | OperationOk<MailboxMessageKeys>
+ async getMailboxInfo(hMailbox: string) : Promise<
+ | OperationOk<MailboxMetadata>
| OperationFail<HttpStatusCode.NotFound>
| OperationFail<HttpStatusCode.TooManyRequests>
>{
- const url = new URL(`keys/${hMailbox.toUpperCase()}`, this.baseUrl);
+ const url = new URL(`info/${hMailbox.toUpperCase()}`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "GET",
@@ -251,7 +250,7 @@ export class TalerMailboxInstanceHttpClient {
switch (resp.status) {
case HttpStatusCode.Ok: {
return opSuccessFromHttp(resp,
- codecForTalerMailboxMessageKeys());
+ codecForTalerMailboxMetadata());
}
case HttpStatusCode.NotFound: {
return opKnownAlternativeHttpFailure(resp, resp.status, codecForEmptyObject());
@@ -259,19 +258,20 @@ export class TalerMailboxInstanceHttpClient {
case HttpStatusCode.TooManyRequests: {
return opKnownAlternativeHttpFailure(resp, resp.status, codecForTalerMailboxRateLimitedResponse());
}
- default:
+ default:
return opUnknownHttpFailure(resp);
}
}
/**
- * https://docs.taler.net/core/api-mailbox.html#post--keys
+ * https://docs.taler.net/core/api-mailbox.html#post--register
*/
- async updateKeys(req: MailboxMessageKeysUpdateRequest) : Promise<
+ async updateRegistration(req: MailboxRegisterRequest) : Promise<
| OperationOk<void>
| OperationFail<HttpStatusCode.Forbidden>
+ | OperationFail<HttpStatusCode.PaymentRequired>
>{
- const url = new URL(`keys`, this.baseUrl);
+ const url = new URL(`register`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
@@ -286,6 +286,9 @@ export class TalerMailboxInstanceHttpClient {
case HttpStatusCode.Forbidden: {
return opKnownHttpFailure(resp.status, resp);
}
+ case HttpStatusCode.PaymentRequired: {
+ return opKnownHttpFailure(resp.status, resp);
+ }
default:
return opUnknownHttpFailure(resp);
}
diff --git a/packages/taler-util/src/types-taler-mailbox.ts b/packages/taler-util/src/types-taler-mailbox.ts
@@ -46,7 +46,8 @@ export const codecForTalerMailboxConfigResponse =
buildCodecForObject<TalerMailboxConfigResponse>()
.property("version", codecForString())
.property("name", codecForConstString("taler-mailbox"))
- .property("message_fee", codecForAmountString())
+ .property("monthly_fee", codecForAmountString())
+ .property("registration_update_fee", codecForAmountString())
.property("message_body_bytes", codecForNumber())
.property("message_response_limit", codecForNumber())
.property("delivery_period", codecForDuration)
@@ -62,9 +63,6 @@ export interface TalerMailboxConfigResponse {
// Name of the protocol.
name: "taler-mailbox";
- // Fee per message.
- message_fee: AmountString;
-
// Fixed size of message body.
message_body_bytes: Integer;
@@ -75,20 +73,30 @@ export interface TalerMailboxConfigResponse {
// How long will the service store a message
// before giving up on delivery?
delivery_period: RelativeTime;
+
+ // How much is the cost of a single
+ // registration (update) of a mailbox
+ // May be 0 for a free update/registration.
+ registration_update_fee: AmountString;
+
+ // How much is the cost of a single
+ // registration period (30 days) of a mailbox
+ // May be 0 for a free registration.
+ monthly_fee: AmountString;
}
export const codecForTalerMailboxMessageKeysUpdateRequest =
- (): Codec<MailboxMessageKeysUpdateRequest> =>
-buildCodecForObject<MailboxMessageKeysUpdateRequest>()
- .property("keys", codecForTalerMailboxMessageKeys())
+ (): Codec<MailboxRegisterRequest> =>
+buildCodecForObject<MailboxRegisterRequest>()
+ .property("mailbox_metadata", codecForTalerMailboxMetadata())
.property("signature", codecForString())
- .build("TalerMailboxApi.MailboxMessageKeysUpdateRequest");
+ .build("TalerMailboxApi.MailboxRegisterRequest");
-export interface MailboxMessageKeysUpdateRequest {
+export interface MailboxRegisterRequest {
- // Keys to add/update for a mailbox.
- keys: MailboxMessageKeys;
+ // Mailbox metadata.
+ mailbox_metadata: MailboxMetadata;
// Signature by the mailbox's signing key affirming
// the update of keys, of purpuse
@@ -99,9 +107,9 @@ export interface MailboxMessageKeysUpdateRequest {
}
-export const codecForTalerMailboxMessageKeys =
- (): Codec<MailboxMessageKeys> =>
-buildCodecForObject<MailboxMessageKeys>()
+export const codecForTalerMailboxMetadata =
+ (): Codec<MailboxMetadata> =>
+buildCodecForObject<MailboxMetadata>()
.property("signingKey", codecForEddsaPublicKey())
.property("signingKeyType", codecForConstString("EdDSA"))
.property("encryptionKey", codecForString())
@@ -110,7 +118,7 @@ buildCodecForObject<MailboxMessageKeys>()
.build("TalerMailboxApi.MailboxMessageKeys");
-export interface MailboxMessageKeys {
+export interface MailboxMetadata {
// The mailbox signing key.
// Note that $H_MAILBOX == H(singingKey).
@@ -160,19 +168,3 @@ export interface MailboxRateLimitedResponse {
hint: string;
}
-
-export interface MailboxMessageDeletionRequest {
-
- // Number of messages to delete. (Starting from the beginning
- // of the latest GET response).
- count: Integer;
-
- // SHA-512 hash over all messages to delete.
- checksum: HashCodeString;
-
- // Signature by the mailbox's private key affirming
- // the deletion of the messages, of purpuse
- // TALER_SIGNATURE_WALLET_MAILBOX_DELETE_MESSAGES.
- signature: EddsaSignatureString;
-
-}
diff --git a/packages/taler-wallet-core/src/mailbox.ts b/packages/taler-wallet-core/src/mailbox.ts
@@ -43,12 +43,11 @@ import {
SendTalerUriMailboxMessageRequest,
hpkeSealOneshot,
hpkeSecretKeyGetPublic,
- MailboxMessageKeys,
- MailboxMessageKeysUpdateRequest,
+ MailboxMetadata,
+ MailboxRegisterRequest,
eddsaSign,
Duration,
AbsoluteTime,
- eddsaVerify,
TalerSignaturePurpose,
} from "@gnu-taler/taler-util";
import {
@@ -157,20 +156,20 @@ export async function updateMailboxKeys(
vMsg.setUint32(4, TalerSignaturePurpose.MAILBOX_KEYS_UPDATE);
const msgToSign = new Uint8Array([...(new Uint8Array(messageHeader)), ...digest]);
const signature = eddsaSign(msgToSign, privateSigningKey);
- const keys: MailboxMessageKeys = {
+ const info: MailboxMetadata = {
signingKey: encodeCrock(signingKey),
signingKeyType: "EdDSA", // FIXME only supported key type ATM,
encryptionKey: encodeCrock(encryptionKey),
encryptionKeyType: "X25519", // FIXME only supported encryption key type ATM
expiration: mailboxConf.expiration,
};
- const req: MailboxMessageKeysUpdateRequest = {
- keys: keys,
+ const req: MailboxRegisterRequest = {
+ mailbox_metadata: info,
signature: encodeCrock(signature),
};
const mailboxClient = new TalerMailboxInstanceHttpClient(mailboxConf.mailboxBaseUrl, wex.http);
- succeedOrThrow(await mailboxClient.updateKeys(req));
+ succeedOrThrow(await mailboxClient.updateRegistration(req));
return;
}
@@ -303,22 +302,20 @@ export async function refreshMailbox(
}
// Message header: 8 byte + 64 byte SHA512 digest to sign
// We hash all messages
- const messageHeader = new ArrayBuffer(8);
- const vMsg = new DataView(messageHeader);
- const digest = sha512(new Uint8Array(messages));
- vMsg.setUint32(0, 8 + 64);
+ const messageBuffer = new ArrayBuffer(16);
+ const vMsg = new DataView(messageBuffer);
+ vMsg.setUint32(0, 4 * 4);
vMsg.setUint32(4, TalerSignaturePurpose.MAILBOX_MESSAGES_DELETE);
- const msgToSign: Uint8Array = new Uint8Array([...(new Uint8Array(messageHeader)), ...digest]);
+ vMsg.setUint32(8, parseInt(res.body.etag));
+ vMsg.setUint32(12, numMessages);
+ const msgToSign: Uint8Array = new Uint8Array(messageBuffer);
const privateSigningKey: Uint8Array = decodeCrock(mailboxConf.privateKey);
const signature = eddsaSign(msgToSign, privateSigningKey);
succeedOrThrow(await mailboxClient.deleteMessages({
mailboxConf: mailboxConf,
matchIf: res.body.etag,
- body: {
- count: numMessages,
- checksum: encodeCrock(digest),
- signature: encodeCrock(signature),
- }
+ count: numMessages,
+ signature: encodeCrock(signature),
}));
return records;
}
@@ -364,7 +361,7 @@ export async function sendTalerUriMessage(
default:
return {};
}
- const resKeys = await mailboxClient.getKeys(req.contact.mailboxAddress);
+ const resKeys = await mailboxClient.getMailboxInfo(req.contact.mailboxAddress);
var keys;
switch (resKeys.case) {
case "ok":