diff options
author | Özgür Kesim <oec-taler@kesim.org> | 2023-08-29 19:39:09 +0200 |
---|---|---|
committer | Özgür Kesim <oec-taler@kesim.org> | 2023-08-29 19:39:09 +0200 |
commit | d42a06607b90c540fa3eb87daa3b4aacbfdd19a7 (patch) | |
tree | ccc225e39f420a6bacbd82500f039fd4f5d74ad3 /packages/taler-util | |
parent | dd4b96e1e62f20c9881d532e40531df8c932a379 (diff) | |
parent | a386de8a9c1aa3fff76b4cb37fb3287213981387 (diff) |
Merge branch 'master' into age-withdraw
Diffstat (limited to 'packages/taler-util')
-rw-r--r-- | packages/taler-util/package.json | 2 | ||||
-rw-r--r-- | packages/taler-util/src/http-common.ts | 29 | ||||
-rw-r--r-- | packages/taler-util/src/payto.ts | 22 | ||||
-rw-r--r-- | packages/taler-util/src/taler-crypto.ts | 5 | ||||
-rw-r--r-- | packages/taler-util/src/taler-types.ts | 135 |
5 files changed, 172 insertions, 21 deletions
diff --git a/packages/taler-util/package.json b/packages/taler-util/package.json index 504b8259f..6ac9a2689 100644 --- a/packages/taler-util/package.json +++ b/packages/taler-util/package.json @@ -57,7 +57,7 @@ } }, "scripts": { - "compile": "tsc --build", + "compile": "tsc", "test": "tsc && ava", "clean": "rimraf dist lib tsconfig.tsbuildinfo", "pretty": "prettier --write src" diff --git a/packages/taler-util/src/http-common.ts b/packages/taler-util/src/http-common.ts index 4f6aaaf44..93cf9bba0 100644 --- a/packages/taler-util/src/http-common.ts +++ b/packages/taler-util/src/http-common.ts @@ -19,7 +19,12 @@ import { CancellationToken } from "./CancellationToken.js"; import { Codec } from "./codec.js"; import { j2s } from "./helpers.js"; -import { TalerError, makeErrorDetail } from "./index.js"; +import { + TalerError, + base64FromArrayBuffer, + makeErrorDetail, + stringToBytes, +} from "./index.js"; import { Logger } from "./logging.js"; import { TalerErrorCode } from "./taler-error-codes.js"; import { Duration, AbsoluteTime } from "./time.js"; @@ -306,6 +311,16 @@ export async function readSuccessResponseJsonOrThrow<T>( throwUnexpectedRequestError(httpResponse, r.talerErrorResponse); } +export async function expectSuccessResponseOrThrow<T>( + httpResponse: HttpResponse, +): Promise<void> { + if (httpResponse.status >= 200 && httpResponse.status <= 299) { + return; + } + const errResp = await readTalerErrorResponse(httpResponse); + throwUnexpectedRequestError(httpResponse, errResp); +} + export async function readSuccessResponseTextOrErrorCode<T>( httpResponse: HttpResponse, ): Promise<ResponseOrError<string>> { @@ -452,3 +467,15 @@ export function getDefaultHeaders(method: string): Record<string, string> { return headers; } + +/** + * Helper function to generate the "Authorization" HTTP header. + */ +export function makeBasicAuthHeader( + username: string, + password: string, +): string { + const auth = `${username}:${password}`; + const authEncoded: string = base64FromArrayBuffer(stringToBytes(auth)); + return `Basic ${authEncoded}`; +} diff --git a/packages/taler-util/src/payto.ts b/packages/taler-util/src/payto.ts index dd35b44be..2b0af4cc2 100644 --- a/packages/taler-util/src/payto.ts +++ b/packages/taler-util/src/payto.ts @@ -239,3 +239,25 @@ export function parsePaytoUri(s: string): PaytoUri | undefined { isKnown: false, }; } + +export function talerPaytoFromExchangeReserve( + exchangeBaseUrl: string, + reservePub: string, +): string { + const url = new URL(exchangeBaseUrl); + let proto: string; + if (url.protocol === "http:") { + proto = "taler-reserve-http"; + } else if (url.protocol === "https:") { + proto = "taler-reserve"; + } else { + throw Error(`unsupported exchange base URL protocol (${url.protocol})`); + } + + let path = url.pathname; + if (!path.endsWith("/")) { + path = path + "/"; + } + + return `payto://${proto}/${url.host}${url.pathname}${reservePub}`; +} diff --git a/packages/taler-util/src/taler-crypto.ts b/packages/taler-util/src/taler-crypto.ts index 396ac89e1..cc9c706ba 100644 --- a/packages/taler-util/src/taler-crypto.ts +++ b/packages/taler-util/src/taler-crypto.ts @@ -1004,7 +1004,7 @@ export enum TalerSignaturePurpose { SYNC_BACKUP_UPLOAD = 1450, } -export const enum WalletAccountMergeFlags { +export enum WalletAccountMergeFlags { /** * Not a legal mode! */ @@ -1260,7 +1260,8 @@ export namespace AgeRestriction { } const PublishedAgeRestrictionBaseKey: Edx25519PublicKey = decodeCrock( - "CH0VKFDZ2GWRWHQBBGEK9MWV5YDQVJ0RXEE0KYT3NMB69F0R96TG"); + "CH0VKFDZ2GWRWHQBBGEK9MWV5YDQVJ0RXEE0KYT3NMB69F0R96TG", + ); export async function restrictionCommitSeeded( ageMask: number, diff --git a/packages/taler-util/src/taler-types.ts b/packages/taler-util/src/taler-types.ts index 178da87da..17900129c 100644 --- a/packages/taler-util/src/taler-types.ts +++ b/packages/taler-util/src/taler-types.ts @@ -25,7 +25,7 @@ * Imports. */ -import { codecForAmountString } from "./amounts.js"; +import { Amounts, codecForAmountString } from "./amounts.js"; import { buildCodecForObject, buildCodecForUnion, @@ -719,16 +719,12 @@ export class ExchangeSignKeyJson { * Structure that the exchange gives us in /keys. */ export class ExchangeKeysJson { - /** * Canonical, public base URL of the exchange. */ base_url: string; - /** - * List of offered denominations. - */ - denoms: ExchangeDenomination[]; + currency: string; /** * The exchange's master public key. @@ -764,6 +760,111 @@ export class ExchangeKeysJson { reserve_closing_delay: TalerProtocolDuration; global_fees: GlobalFees[]; + + accounts: AccountInfo[]; + + wire_fees: { [methodName: string]: WireFeesJson[] }; + + denominations: DenomGroup[]; +} + +export type DenomGroup = + | DenomGroupRsa + | DenomGroupCs + | DenomGroupRsaAgeRestricted + | DenomGroupCsAgeRestricted; + +export interface DenomGroupCommon { + // How much are coins of this denomination worth? + value: AmountString; + + // Fee charged by the exchange for withdrawing a coin of this denomination. + fee_withdraw: AmountString; + + // Fee charged by the exchange for depositing a coin of this denomination. + fee_deposit: AmountString; + + // Fee charged by the exchange for refreshing a coin of this denomination. + fee_refresh: AmountString; + + // Fee charged by the exchange for refunding a coin of this denomination. + fee_refund: AmountString; + + // XOR of all the SHA-512 hash values of the denominations' public keys + // in this group. Note that for hashing, the binary format of the + // public keys is used, and not their base32 encoding. + hash: HashCodeString; +} + +export interface DenomCommon { + // Signature of TALER_DenominationKeyValidityPS. + master_sig: EddsaSignatureString; + + // When does the denomination key become valid? + stamp_start: TalerProtocolTimestamp; + + // When is it no longer possible to deposit coins + // of this denomination? + stamp_expire_withdraw: TalerProtocolTimestamp; + + // Timestamp indicating by when legal disputes relating to these coins must + // be settled, as the exchange will afterwards destroy its evidence relating to + // transactions involving this coin. + stamp_expire_legal: TalerProtocolTimestamp; + + stamp_expire_deposit: TalerProtocolTimestamp; + + // Set to 'true' if the exchange somehow "lost" + // the private key. The denomination was not + // necessarily revoked, but still cannot be used + // to withdraw coins at this time (theoretically, + // the private key could be recovered in the + // future; coins signed with the private key + // remain valid). + lost?: boolean; +} + +export type RsaPublicKeySring = string; +export type AgeMask = number; + +/** + * 32-byte value representing a point on Curve25519. + */ +export type Cs25519Point = string; + +export interface DenomGroupRsa extends DenomGroupCommon { + cipher: "RSA"; + + denoms: ({ + rsa_pub: RsaPublicKeySring; + } & DenomCommon)[]; +} + +export interface DenomGroupRsaAgeRestricted extends DenomGroupCommon { + cipher: "RSA+age_restricted"; + age_mask: AgeMask; + + denoms: ({ + rsa_pub: RsaPublicKeySring; + } & DenomCommon)[]; +} + +export interface DenomGroupCs extends DenomGroupCommon { + cipher: "CS"; + age_mask: AgeMask; + + denoms: ({ + cs_pub: Cs25519Point; + } & DenomCommon)[]; +} + +export interface DenomGroupCsAgeRestricted extends DenomGroupCommon { + cipher: "CS+age_restricted"; + age_mask: AgeMask; + + denoms: ({ + cs_pub: Cs25519Point; + } & DenomCommon)[]; } export interface GlobalFees { @@ -847,10 +948,10 @@ export interface AccountInfo { debit_restrictions?: any; } -export interface ExchangeWireJson { - accounts: AccountInfo[]; - fees: { [methodName: string]: WireFeesJson[] }; -} +/** + * @deprecated + */ +export interface ExchangeWireJson {} /** * Proposal returned from the contract URL. @@ -1404,10 +1505,13 @@ export const codecForGlobalFees = (): Codec<GlobalFees> => .property("master_sig", codecForString()) .build("GlobalFees"); +// FIXME: Validate properly! +export const codecForNgDenominations: Codec<DenomGroup> = codecForAny(); + export const codecForExchangeKeysJson = (): Codec<ExchangeKeysJson> => buildCodecForObject<ExchangeKeysJson>() - .property("denoms", codecForList(codecForDenomination())) .property("base_url", codecForString()) + .property("currency", codecForString()) .property("master_public_key", codecForString()) .property("auditors", codecForList(codecForAuditor())) .property("list_issue_date", codecForTimestamp) @@ -1416,6 +1520,9 @@ export const codecForExchangeKeysJson = (): Codec<ExchangeKeysJson> => .property("version", codecForString()) .property("reserve_closing_delay", codecForDuration) .property("global_fees", codecForList(codecForGlobalFees())) + .property("accounts", codecForList(codecForAccountInfo())) + .property("wire_fees", codecForMap(codecForList(codecForWireFeesJson()))) + .property("denominations", codecForList(codecForNgDenominations)) .build("ExchangeKeysJson"); export const codecForWireFeesJson = (): Codec<WireFeesJson> => @@ -1436,12 +1543,6 @@ export const codecForAccountInfo = (): Codec<AccountInfo> => .property("debit_restrictions", codecForAny()) .build("AccountInfo"); -export const codecForExchangeWireJson = (): Codec<ExchangeWireJson> => - buildCodecForObject<ExchangeWireJson>() - .property("accounts", codecForList(codecForAccountInfo())) - .property("fees", codecForMap(codecForList(codecForWireFeesJson()))) - .build("ExchangeWireJson"); - export const codecForProposal = (): Codec<Proposal> => buildCodecForObject<Proposal>() .property("contract_terms", codecForAny()) |