add anastasis skeleton, put crypto in taler-util

This commit is contained in:
Florian Dold 2021-10-07 12:01:40 +02:00
parent 2c3456608e
commit e2fe2d6db1
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
48 changed files with 1500 additions and 374 deletions

View File

@ -0,0 +1,30 @@
{
"name": "anastasis-core",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"prepare": "tsc",
"compile": "tsc",
"pretty": "prettier --write src",
"test": "tsc && ava",
"coverage": "tsc && nyc ava",
"clean": "rimraf dist lib tsconfig.tsbuildinfo"
},
"author": "Florian Dold <dold@taler.net>",
"license": "AGPL-3-or-later",
"type": "module",
"devDependencies": {
"ava": "^3.15.0",
"typescript": "^4.4.3"
},
"dependencies": {
"@gnu-taler/taler-util": "workspace:^0.8.3",
"hash-wasm": "^4.9.0"
},
"ava": {
"files": [
"lib/**/*test.*"
]
}
}

View File

@ -0,0 +1,742 @@
// This file is auto-generated, do not modify.
// Generated from v0.2.0-4-g61ea83c on Tue, 05 Oct 2021 10:40:32 +0200
// To re-generate, run contrib/gen-ts.sh from the main anastasis code base.
export const anastasisData = {
providersList: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
anastasis_provider: [
{
url: "https://anastasis.demo.taler.net/",
currency: "KUDOS",
},
{
url: "https://kudos.demo.anastasis.lu/",
currency: "KUDOS",
},
{
url: "http://localhost:8086/",
currency: "TESTKUDOS",
},
{
url: "http://localhost:8087/",
currency: "TESTKUDOS",
},
{
url: "http://localhost:8088/",
currency: "TESTKUDOS",
},
{
url: "http://localhost:8089/",
currency: "TESTKUDOS",
},
],
},
countriesList: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
countries: [
{
code: "al",
name: "Albania",
continent: "Europe",
name_i18n: {
de_DE: "Albanien",
en_UK: "Albania",
},
currency: "ALL",
call_code: "+355",
},
{
code: "be",
name: "Belgium",
continent: "Europe",
name_i18n: {
de_DE: "Belgien",
en_UK: "Belgium",
},
currency: "EUR",
call_code: "+32",
},
{
code: "ch",
name: "Switzerland",
continent: "Europe",
name_i18n: {
de_DE: "Schweiz",
de_CH: "Schwiiz",
fr_FR: "Suisse",
en_UK: "Swiss",
},
currency: "CHF",
call_code: "+41",
},
{
code: "cz",
name: "Czech Republic",
continent: "Europe",
name_i18n: {
en_UK: "Czech Republic",
},
currency: "CZK",
call_code: "+420",
},
{
code: "de",
name: "Germany",
continent: "Europe",
continent_i18n: { de_DE: "Europa" },
name_i18n: {
de_DE: "Deutschland",
de_CH: "Deutschland",
fr_FR: "Allemagne",
en_UK: "Germany",
},
currency: "EUR",
call_code: "+49",
},
{
code: "dk",
name: "Denmark",
continent: "Europe",
continent_i18n: { de_DE: "Europa" },
name_i18n: {
en_UK: "Denmark",
},
currency: "DKK",
call_code: "+45",
},
{
code: "es",
name: "Spain",
continent: "Europe",
continent_i18n: { es_ES: "Europa" },
name_i18n: {
es_ES: "España",
},
currency: "EUR",
call_code: "+44",
},
{
code: "in",
name: "India",
continent: "India",
continent_i18n: { en_EN: "India" },
name_i18n: {
de_DE: "Indien",
de_CH: "Indien",
fr_FR: "l'Inde",
en_UK: "India",
},
currency: "INR",
call_code: "+91",
},
{
code: "it",
name: "Italy",
continent: "Europe",
name_i18n: {
de_DE: "Italien",
en_UK: "Italy",
},
currency: "EUR",
call_code: "+39",
},
{
code: "jp",
name: "Japan",
continent: "Asia",
continent_i18n: { en_EN: "Japan" },
name_i18n: {
de_DE: "Japan",
de_CH: "Japan",
en_UK: "Japan",
},
currency: "JPY",
call_code: "+81",
},
{
code: "sl",
name: "Slovakia",
continent: "Europe",
name_i18n: {
en_UK: "Slovakia",
},
currency: "EUR",
call_code: "+421",
},
{
code: "us",
name: "United States of America (USA)",
continent: "North America",
continent_i18n: { de_DE: "Nordamerika" },
name_i18n: {
de_DE: "Vereinigte Staaten von Amerika (USA)",
de_CH: "Vereinigte Staaten von Amerika (USA)",
fr_FR: "États-Unis d'Amérique (USA)",
en_UK: "United States of America (USA)",
},
currency: "USD",
call_code: "+1",
},
{
code: "xx",
name: "Testland",
continent: "Testcontinent",
continent_i18n: { de_DE: "Testkontinent" },
name_i18n: {
de_DE: "Testlandt",
de_CH: "Testlandi",
fr_FR: "Testpais",
en_UK: "Testland",
},
currency: "TESTKUDOS",
call_code: "+00",
},
{
code: "xy",
name: "Demoland",
continent: "Testcontinent",
continent_i18n: { de_DE: "Testkontinent" },
name_i18n: {
de_DE: "Demolandt",
de_CH: "Demolandi",
fr_FR: "Demopais",
en_UK: "Demoland",
},
currency: "KUDOS",
call_code: "+01",
},
],
},
countryDetails: {
al: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "nid_number",
label: "Numri i Identitetit",
label_i18n: {
en: "Identity Number",
al: "Numri i Identitetit",
},
widget: "anastasis_gtk_ia_nid_al",
uuid: "256e5d30-d65e-481b-9ac4-55f5ac03b24a",
"validation-regex":
"^[0-9A-T][0-9](((0|5)[0-9])|10|11|51|52)[0-9]{3}[A-W]$",
"validation-logic": "AL_NID_check",
},
],
},
be: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "date",
name: "birthdate",
label: "Birthdate",
widget: "anastasis_gtk_ia_birthdate",
uuid: "83d655c7-bdb6-484d-904e-80c1058c8854",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "nrn_number",
label: "National Register Number",
label_i18n: {
en: "National Register Number",
},
widget: "anastasis_gtk_ia_nid_be",
uuid: "0452f99a-06f7-48bd-8ac0-2e4ed9a24560",
"validation-regex": "^[0-9]{11}$",
"validation-logic": "BE_NRN_check",
},
],
},
ch: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "date",
name: "birthdate",
label: "Birthdate",
widget: "anastasis_gtk_ia_birthdate",
uuid: "83d655c7-bdb6-484d-904e-80c1058c8854",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "ahv_number",
label: "AHV number",
label_i18n: {
de_DE: "AHV-Nummer",
de_CH: "AHV-Nummer",
},
widget: "anastasis_gtk_ia_ahv",
uuid: "1da87570-ba16-4f62-8a7e-cbda92f51591",
"validation-regex":
"^(756).[0-9]{4}.[0-9]{4}.[0-9]{2}|(756)[0-9]{10}$",
"validation-logic": "CH_AHV_check",
},
],
},
cz: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "birth_number",
label: "Birth Number",
label_i18n: {
en: "Birth Number",
cz: "rodné číslo",
},
widget: "anastasis_gtk_ia_birthnumber_cz",
uuid: "03e3a05b-1192-44f1-ac36-7425512eee1a",
"validation-regex":
"^[0-9]{2}(((0|2|5|7)[0-9])|10|11|31|32|51|52|81|82)/[0-9]{3}[0-9]?$",
"validation-logic": "CZ_BN_check",
},
],
},
de: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "date",
name: "birthdate",
label: "Birthdate",
widget: "anastasis_gtk_ia_birthdate",
uuid: "83d655c7-bdb6-484d-904e-80c1058c8854",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "tax_number",
label: "Taxpayer identification number",
label_i18n: {
de_DE: "Steuerliche Identifikationsnummer",
en: "German taxpayer identification number",
},
widget: "anastasis_gtk_ia_tax_de",
uuid: "dae48f85-e3ff-47a4-a4a3-ed981ed8c3c6",
"validation-regex": "^[0-9]{11}$",
"validation-logic": "DE_TIN_check",
},
{
type: "string",
name: "social_security_number",
label: "Social security number",
label_i18n: {
de_DE: "Deutsche Sozialversicherungsnummer",
en: "German Social security number",
},
widget: "anastasis_gtk_ia_ssn_de",
uuid: "d5e2aa79-1c88-4cf4-a4d2-252508b38e05",
"validation-regex": "^[0-9]{8}[[:upper:]][0-9]{3}$",
"validation-logic": "DE_SVN_check",
optional: true,
},
],
},
dk: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "cpr_number",
label: "CPR-nummer",
label_i18n: {
en: "CPR Number",
dk: "CPR-nummer",
},
widget: "anastasis_gtk_ia_cpr_dk",
uuid: "38f13a4d-4302-4ada-ada1-c3ff4a8ff689",
"validation-regex":
"^(0[1-9]|[1-2][0-9]|30|31)((0[1-9]|10|11|12))[0-9]{2}-[0-9A-Z]{4}$",
},
],
},
es: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "date",
name: "birthdate",
label: "Birthdate",
widget: "anastasis_gtk_ia_birthdate",
uuid: "83d655c7-bdb6-484d-904e-80c1058c8854",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "tax_number",
label: "Tax number",
label_i18n: {
es_ES: "Número de Identificación Fiscal (DNI, NIE)",
},
widget: "anastasis_gtk_ia_es_dni",
uuid: "ac8bd865-6be8-445c-b650-6a18eef16a49",
"validation-regex": "^[0-9MXYZ][0-9]{7}[TRWAGMYFPDXBNJZSQVHLCKE]$",
"validation-logic": "ES_DNI_check",
},
{
type: "string",
name: "ssn_number",
label: "Social security number",
label_i18n: {
es_ES: "Número de Seguridad Social",
},
widget: "anastasis_gtk_ia_es_ssn",
uuid: "22396a19-f3bb-497e-b63a-961fd639140e",
"validation-regex": "^[0-9]{11}$",
},
],
},
in: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "date",
name: "birthdate",
label: "Birthdate",
widget: "anastasis_gtk_ia_birthdate",
uuid: "83d655c7-bdb6-484d-904e-80c1058c8854",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "aadhar_number",
label: "Aadhar number",
label_i18n: {
en: "Aadhar number",
},
widget: "anastasis_gtk_ia_aadhar_in",
uuid: "55afe97a-98bc-48d1-bb37-a9658be3fdc9",
"validation-regex": "^[2-9]{1}[0-9]{3}\\s[0-9]{4}\\s[0-9]{4}$",
"validation-logic": "IN_AADHAR_check",
},
],
},
it: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "fiscal_code",
label: "Codice fiscale",
label_i18n: {
it: "Codice fiscale",
en: "Fiscal code",
},
widget: "anastasis_gtk_ia_cf_it",
uuid: "88f53c51-52ad-4d63-a163-ec042589f925",
"validation-regex":
"^[[:upper:]]{6}[0-9]{2}[A-EHLMPRT](([0-24-6][0-9])|(30|31|70|71))[A-MZ][0-9]{3}[A-Z]$",
"validation-logic": "IT_CF_check",
},
],
},
jp: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "date",
name: "birthdate",
label: "Birthdate",
widget: "anastasis_gtk_ia_birthdate",
uuid: "83d655c7-bdb6-484d-904e-80c1058c8854",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "my_number",
label: "My number",
label_i18n: {
en: "My number",
jp: "マイナンバー",
},
widget: "anastasis_gtk_ia_my_jp",
uuid: "90848f42-a83e-4226-8186-329696c14152",
"validation-regex": "^[0-9]{12}$",
},
],
},
sk: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "birth_number",
label: "Birth Number",
label_i18n: {
en: "Birth Number",
sk: "rodné číslo",
},
widget: "anastasis_gtk_ia_birthnumber_sk",
uuid: "1cd372fe-2cea-4928-9f29-66f2bdd8555c",
"validation-regex":
"^[0-9]{2}(((0|2|5|7)[0-9])|10|11|31|32|51|52|81|82)/[0-9]{3}[0-9]?$",
"validation-logic": "CZ_BN_check",
},
],
},
us: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "date",
name: "birthdate",
label: "Birthdate",
widget: "anastasis_gtk_ia_birthdate",
uuid: "83d655c7-bdb6-484d-904e-80c1058c8854",
},
{
type: "string",
name: "birthplace",
label: "Birthplace",
widget: "anastasis_gtk_ia_birthplace",
uuid: "4c822e8e-89c6-11eb-95c4-8b077ad8489f",
},
{
type: "string",
name: "social_security_number",
label: "Social security number",
label_i18n: {
en: "US Social security number",
},
widget: "anastasis_gtk_ia_ssn_us",
uuid: "310a138c-b0b7-4985-b8b8-d00e765e9f9b",
"validation-regex": "^d{3}-d{2}-d{4}$",
},
],
},
xx: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "date",
name: "birthdate",
label: "Birthdate",
widget: "anastasis_gtk_ia_birthdate",
uuid: "83d655c7-bdb6-484d-904e-80c1058c8854",
},
{
type: "string",
name: "sq_number",
label: "Square number",
widget: "anastasis_gtk_xx_square",
uuid: "ed790bca-89bf-11eb-96f2-233996cf644e",
"validation-regex": "^[0-9]+$",
"validation-logic": "XX_SQUARE_check",
},
],
},
xy: {
license: "GPLv3+",
"SPDX-License-Identifier": "GPL3.0-or-later",
required_attributes: [
{
type: "string",
name: "full_name",
label: "Full name",
widget: "anastasis_gtk_ia_full_name",
uuid: "9e8f463f-575f-42cb-85f3-759559997331",
},
{
type: "date",
name: "birthdate",
label: "Birthdate",
widget: "anastasis_gtk_ia_birthdate",
uuid: "83d655c7-bdb6-484d-904e-80c1058c8854",
},
{
type: "string",
name: "prime_number",
label: "Prime number",
widget: "anastasis_gtk_xx_prime",
uuid: "39190a95-cacb-4412-8bae-1f7da3f980b4",
"validation-regex": "^[0-9]+$",
"validation-logic": "XY_PRIME_check",
},
],
},
},
};

View File

@ -0,0 +1,16 @@
import test from "ava";
// Vector generated with taler-anastasis-tvg
const userIdVector = {
input_id_data: {
name: "Fleabag",
ssn: "AB123",
},
input_server_salt: "FZ48EFS7WS3R2ZR4V53A3GFFY4",
output_id:
"YS45R6CGJV84K1NN7T14ZBCPVTZ6H15XJSM1FV0R748MHPV82SM0126EBZKBAAGCR34Q9AFKPEW1HRT2Q9GQ5JRA3642AB571DKZS18",
};
test("user ID derivation", async (t) => {
t.fail();
});

View File

@ -0,0 +1,21 @@
import { argon2id } from "hash-wasm";
async function userIdentifierDerive(
idData: any,
serverSalt: string,
): Promise<string> {
throw Error("not implemented");
}
// interface Keypair {
// pub: string;
// priv: string;
// }
// async function accountKeypairDerive(): Promise<Keypair> {}
// async function secureAnswerHash(
// answer: string,
// truthUuid: string,
// questionSalt: string,
// ): Promise<string> {}

View File

@ -0,0 +1,14 @@
import { md5, sha1, sha512, sha3 } from 'hash-wasm';
async function run() {
console.log('MD5:', await md5('demo'));
const int8Buffer = new Uint8Array([0, 1, 2, 3]);
console.log('SHA1:', await sha1(int8Buffer));
console.log('SHA512:', await sha512(int8Buffer));
const int32Buffer = new Uint32Array([1056, 641]);
console.log('SHA3-256:', await sha3(int32Buffer, 256));
}
run();

View File

@ -0,0 +1,30 @@
{
"compileOnSave": true,
"compilerOptions": {
"composite": true,
"target": "ES2018",
"module": "ESNext",
"moduleResolution": "node",
"sourceMap": true,
"lib": ["es6"],
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"strict": true,
"strictPropertyInitialization": false,
"outDir": "lib",
"noImplicitAny": true,
"noImplicitThis": true,
"incremental": true,
"esModuleInterop": true,
"importHelpers": true,
"rootDir": "src",
"baseUrl": "./src",
"typeRoots": ["./node_modules/@types"]
},
"include": ["src/**/*"],
"references": [
{
"path": "../taler-util/"
}
]
}

View File

@ -805,7 +805,7 @@ export class BridgeIDBFactory {
oldVersion,
});
request.dispatchEvent(event2);
} catch (err) {
} catch (err: any) {
request.error = new Error();
request.error.name = err.name;
request.readyState = "done";
@ -846,7 +846,7 @@ export class BridgeIDBFactory {
if (BridgeIDBFactory.enableTracing) {
console.log("TRACE: connected!");
}
} catch (err) {
} catch (err: any) {
if (BridgeIDBFactory.enableTracing) {
console.log(
"TRACE: caught exception while trying to connect with backend",
@ -2704,7 +2704,7 @@ export class BridgeIDBTransaction
this._active = false;
throw err;
}
} catch (err) {
} catch (err: any) {
if (BridgeIDBFactory.enableTracing) {
console.log("TRACING: error during operation: ", err);
}

View File

@ -542,7 +542,7 @@ export function is_transaction_active(
e.stopPropagation();
};
return true;
} catch (ex) {
} catch (ex: any) {
console.log(ex.stack);
t.deepEqual(
ex.name,

View File

@ -3,19 +3,17 @@
"version": "0.8.3",
"description": "Generic helper functionality for GNU Taler",
"exports": {
".": "./lib/index.js"
},
"module": "./lib/index.js",
"main": "./lib/index.js",
"browser": {
"./lib/index.js": "./lib/index.browser.js"
".": "./lib/index.node.js"
},
"module": "./lib/index.node.js",
"main": "./lib/index.node.js",
"browser": "./lib/index.browser.js",
"type": "module",
"types": "./lib/index.d.ts",
"types": "./lib/index.node.d.ts",
"typesVersions": {
"*": {
"lib/index.d.ts": [
"lib/index.d.ts"
"lib/index.node.d.ts": [
"lib/index.node.d.ts"
],
"src/*": [],
"*": []
@ -40,6 +38,7 @@
"typescript": "^4.2.3"
},
"dependencies": {
"big-integer": "^1.6.48",
"jed": "^1.1.1",
"tslib": "^2.1.0"
},

View File

@ -1,22 +1,21 @@
import { TalerErrorCode } from "./taler-error-codes.js";
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
export { TalerErrorCode };
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
export * from "./amounts.js";
export * from "./backupTypes.js";
export * from "./codec.js";
export * from "./helpers.js";
export * from "./libtool-version.js";
export * from "./notifications.js";
export * from "./payto.js";
export * from "./ReserveStatus.js";
export * from "./ReserveTransaction.js";
export * from "./talerTypes.js";
export * from "./taleruri.js";
export * from "./time.js";
export * from "./transactionsTypes.js";
export * from "./walletTypes.js";
export * from "./i18n.js";
export * from "./logging.js";
export * from "./url.js";
export { fnutil } from "./fnutils.js";
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
// Entry point for the browser.
import { loadBrowserPrng } from "./prng-browser.js";
loadBrowserPrng();
export * from "./index.js";

View File

@ -0,0 +1,23 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
// Entry point for nodejs.
import { initNodePrng } from "./prng-node.js";
initNodePrng();
export * from "./index.js";
export * from "./talerconfig.js";
export * from "./globbing/minimatch.js";

View File

@ -1,3 +1,25 @@
export * from "./index.browser.js";
export * from "./talerconfig.js";
export * from "./globbing/minimatch.js";
import { TalerErrorCode } from "./taler-error-codes.js";
export { TalerErrorCode };
export * from "./amounts.js";
export * from "./backupTypes.js";
export * from "./codec.js";
export * from "./helpers.js";
export * from "./libtool-version.js";
export * from "./notifications.js";
export * from "./payto.js";
export * from "./ReserveStatus.js";
export * from "./ReserveTransaction.js";
export * from "./talerTypes.js";
export * from "./taleruri.js";
export * from "./time.js";
export * from "./transactionsTypes.js";
export * from "./walletTypes.js";
export * from "./i18n.js";
export * from "./logging.js";
export * from "./url.js";
export { fnutil } from "./fnutils.js";
export * from "./kdf.js";
export * from "./talerCrypto.js";
export { randomBytes, secretbox, secretbox_open } from "./nacl-fast.js";

5
packages/taler-util/src/kdf.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
export declare function sha512(data: Uint8Array): Uint8Array;
export declare function hmac(digest: (d: Uint8Array) => Uint8Array, blockSize: number, key: Uint8Array, message: Uint8Array): Uint8Array;
export declare function hmacSha512(key: Uint8Array, message: Uint8Array): Uint8Array;
export declare function hmacSha256(key: Uint8Array, message: Uint8Array): Uint8Array;
export declare function kdf(outputLength: number, ikm: Uint8Array, salt: Uint8Array, info: Uint8Array): Uint8Array;

View File

@ -0,0 +1,76 @@
/*
This file is part of GNU Taler
(C) 2019 GNUnet e.V.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import * as nacl from "./nacl-fast.js";
import { sha256 } from "./sha256.js";
export function sha512(data) {
return nacl.hash(data);
}
export function hmac(digest, blockSize, key, message) {
if (key.byteLength > blockSize) {
key = digest(key);
}
if (key.byteLength < blockSize) {
const k = key;
key = new Uint8Array(blockSize);
key.set(k, 0);
}
const okp = new Uint8Array(blockSize);
const ikp = new Uint8Array(blockSize);
for (let i = 0; i < blockSize; i++) {
ikp[i] = key[i] ^ 0x36;
okp[i] = key[i] ^ 0x5c;
}
const b1 = new Uint8Array(blockSize + message.byteLength);
b1.set(ikp, 0);
b1.set(message, blockSize);
const h0 = digest(b1);
const b2 = new Uint8Array(blockSize + h0.length);
b2.set(okp, 0);
b2.set(h0, blockSize);
return digest(b2);
}
export function hmacSha512(key, message) {
return hmac(sha512, 128, key, message);
}
export function hmacSha256(key, message) {
return hmac(sha256, 64, key, message);
}
export function kdf(outputLength, ikm, salt, info) {
// extract
const prk = hmacSha512(salt, ikm);
// expand
const N = Math.ceil(outputLength / 32);
const output = new Uint8Array(N * 32);
for (let i = 0; i < N; i++) {
let buf;
if (i == 0) {
buf = new Uint8Array(info.byteLength + 1);
buf.set(info, 0);
}
else {
buf = new Uint8Array(info.byteLength + 1 + 32);
for (let j = 0; j < 32; j++) {
buf[j] = output[(i - 1) * 32 + j];
}
buf.set(info, 32);
}
buf[buf.length - 1] = i + 1;
const chunk = hmacSha256(prk, buf);
output.set(chunk, i * 32);
}
return output.slice(0, outputLength);
}
//# sourceMappingURL=kdf.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"kdf.js","sourceRoot":"","sources":["kdf.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,19 @@
import { setPRNG } from "./nacl-fast.js";
export function loadBrowserPrng() {
// Initialize PRNG if environment provides CSPRNG.
// If not, methods calling randombytes will throw.
// @ts-ignore-error
const cr = typeof self !== "undefined" ? self.crypto || self.msCrypto : null;
const QUOTA = 65536;
setPRNG(function (x: Uint8Array, n: number) {
let i;
const v = new Uint8Array(n);
for (i = 0; i < n; i += QUOTA) {
cr.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA)));
}
for (i = 0; i < n; i++) x[i] = v[i];
for (i = 0; i < v.length; i++) v[i] = 0;
});
}

View File

@ -0,0 +1,30 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { setPRNG } from "./nacl-fast.js";
import cr from "crypto";
export function initNodePrng() {
// Initialize PRNG if environment provides CSPRNG.
// If not, methods calling randombytes will throw.
if (cr && cr.randomBytes) {
setPRNG(function (x: Uint8Array, n: number) {
const v = cr.randomBytes(n);
for (let i = 0; i < n; i++) x[i] = v[i];
for (let i = 0; i < v.length; i++) v[i] = 0;
});
}
}

View File

@ -25,14 +25,11 @@ import {
eddsaGetPublic,
keyExchangeEddsaEcdhe,
keyExchangeEcdheEddsa,
rsaBlind,
rsaUnblind,
stringToBytes,
bytesToString,
rsaVerify,
} from "./talerCrypto.js";
import { sha512, kdf } from "./primitives/kdf.js";
import * as nacl from "./primitives/nacl-fast.js";
import { sha512, kdf } from "./kdf.js";
import * as nacl from "./nacl-fast.js";
test("encoding", (t) => {
const s = "Hello, World";

View File

@ -21,9 +21,12 @@
/**
* Imports.
*/
import * as nacl from "./primitives/nacl-fast.js";
import * as nacl from "./nacl-fast.js";
import { kdf } from "./kdf.js";
import bigint from "big-integer";
import { kdf } from "./primitives/kdf.js";
import { initNodePrng } from "./prng-node.js";
initNodePrng();
export function getRandomBytes(n: number): Uint8Array {
return nacl.randomBytes(n);

View File

@ -40,14 +40,14 @@ import {
codecForString,
Logger,
Configuration,
decodeCrock,
rsaBlind,
} from "@gnu-taler/taler-util";
import {
NodeHttpLib,
getDefaultNodeWallet,
OperationFailedAndReportedError,
OperationFailedError,
decodeCrock,
rsaBlind,
NodeThreadCryptoWorkerFactory,
CryptoApi,
walletCoreDebugFlags,
@ -810,7 +810,7 @@ advancedCli
coinPubList = coinPubListCodec.decode(
JSON.parse(args.suspendCoins.coinPubSpec),
);
} catch (e) {
} catch (e: any) {
console.log("could not parse coin list:", e.message);
process.exit(1);
}
@ -835,7 +835,7 @@ advancedCli
coinPubList = coinPubListCodec.decode(
JSON.parse(args.unsuspendCoins.coinPubSpec),
);
} catch (e) {
} catch (e: any) {
console.log("could not parse coin list:", e.message);
process.exit(1);
}

View File

@ -44,11 +44,6 @@ import {
MerchantInstancesResponse,
} from "./merchantApiTypes";
import {
createEddsaKeyPair,
eddsaGetPublic,
EddsaKeyPair,
encodeCrock,
getRandomBytes,
openPromise,
OperationFailedError,
WalletCoreApiClient,
@ -64,6 +59,11 @@ import {
Duration,
parsePaytoUri,
CoreApiResponse,
createEddsaKeyPair,
eddsaGetPublic,
EddsaKeyPair,
encodeCrock,
getRandomBytes,
} from "@gnu-taler/taler-util";
import { CoinConfig } from "./denomStructures.js";
@ -441,7 +441,7 @@ export async function pingProc(
const resp = await axios.get(url);
console.log(`service ${serviceName} available`);
return;
} catch (e) {
} catch (e: any) {
console.log(`service ${serviceName} not ready:`, e.toString());
await delayMs(1000);
}
@ -1074,8 +1074,12 @@ export class ExchangeService implements ExchangeServiceInterface {
async purgeSecmodKeys(): Promise<void> {
const cfg = Configuration.load(this.configFilename);
const rsaKeydir = cfg.getPath("taler-exchange-secmod-rsa", "KEY_DIR").required();
const eddsaKeydir = cfg.getPath("taler-exchange-secmod-eddsa", "KEY_DIR").required();
const rsaKeydir = cfg
.getPath("taler-exchange-secmod-rsa", "KEY_DIR")
.required();
const eddsaKeydir = cfg
.getPath("taler-exchange-secmod-eddsa", "KEY_DIR")
.required();
// Be *VERY* careful when changing this, or you will accidentally delete user data.
await sh(this.globalState, "rm-secmod-keys", `rm -rf ${rsaKeydir}/COIN_*`);
await sh(this.globalState, "rm-secmod-keys", `rm ${eddsaKeydir}/*`);
@ -1119,11 +1123,7 @@ export class ExchangeService implements ExchangeServiceInterface {
this.exchangeHttpProc = this.globalState.spawnService(
"taler-exchange-httpd",
[
"-c",
this.configFilename,
...this.timetravelArgArr,
],
["-c", this.configFilename, ...this.timetravelArgArr],
`exchange-httpd-${this.name}`,
);

View File

@ -28,7 +28,7 @@ import {
BankAccessApi,
CreditDebitIndicator,
} from "./harness";
import { createEddsaKeyPair, encodeCrock } from "@gnu-taler/taler-wallet-core";
import { createEddsaKeyPair, encodeCrock } from "@gnu-taler/taler-util";
import { defaultCoinConfig } from "./denomStructures";
/**

View File

@ -21,13 +21,10 @@ import {
ConfirmPayResultType,
PreparePayResultType,
URL,
} from "@gnu-taler/taler-util";
import {
encodeCrock,
getRandomBytes,
NodeHttpLib,
WalletApiOperation,
} from "@gnu-taler/taler-wallet-core";
} from "@gnu-taler/taler-util";
import { NodeHttpLib, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
BankService,
ExchangeService,
@ -566,11 +563,9 @@ async function testWithoutClaimToken(
* specification of the endpoint.
*/
export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {
const {
bank,
exchange,
merchant,
} = await createSimpleTestkudosEnvironment(t);
const { bank, exchange, merchant } = await createSimpleTestkudosEnvironment(
t,
);
// Base URL for the default instance.
const merchantBaseUrl = merchant.makeInstanceBaseUrl();

View File

@ -35,9 +35,9 @@ import {
codecForExchangeKeysJson,
codecForKeysManagementResponse,
Configuration,
decodeCrock,
} from "@gnu-taler/taler-util";
import {
decodeCrock,
NodeHttpLib,
readSuccessResponseJsonOrThrow,
} from "@gnu-taler/taler-wallet-core";

View File

@ -63,9 +63,9 @@ import {
setupTipPlanchet,
setupWithdrawPlanchet,
eddsaGetPublic,
} from "../talerCrypto.js";
import { randomBytes } from "../primitives/nacl-fast.js";
import { kdf } from "../primitives/kdf.js";
} from "@gnu-taler/taler-util";
import { randomBytes } from "@gnu-taler/taler-util";
import { kdf } from "@gnu-taler/taler-util";
import { Timestamp, timestampTruncateToSecond } from "@gnu-taler/taler-util";
import { Logger } from "@gnu-taler/taler-util";

View File

@ -28,8 +28,7 @@ import {
import { RequestThrottler } from "../util/RequestThrottler.js";
import Axios, { AxiosResponse } from "axios";
import { OperationFailedError, makeErrorDetails } from "../errors.js";
import { Logger } from "@gnu-taler/taler-util";
import { bytesToString } from "../crypto/talerCrypto.js";
import { Logger, bytesToString } from "@gnu-taler/taler-util";
import { TalerErrorCode, URL } from "@gnu-taler/taler-util";
const logger = new Logger("NodeHttpLib.ts");
@ -83,7 +82,7 @@ export class NodeHttpLib implements HttpRequestLibrary {
timeout,
maxRedirects: 0,
});
} catch (e) {
} catch (e: any) {
throw OperationFailedError.fromCode(
TalerErrorCode.WALLET_NETWORK_ERROR,
`${e.message}`,

View File

@ -92,7 +92,7 @@ export async function getDefaultNodeWallet(
});
const dbContent = JSON.parse(dbContentStr);
myBackend.importDump(dbContent);
} catch (e) {
} catch (e: any) {
const code: string = e.code;
if (code === "ENOENT") {
logger.trace("wallet file doesn't exist yet");

View File

@ -15,58 +15,3 @@
*/
export * from "./index.js";
import { setPRNG } from './crypto/primitives/nacl-fast.js';
// export default API;
function cleanup(arr: Uint8Array): void {
for (let i = 0; i < arr.length; i++) arr[i] = 0;
}
// Initialize PRNG if environment provides CSPRNG.
// If not, methods calling randombytes will throw.
// @ts-ignore-error
const cr = typeof self !== "undefined" ? self.crypto || self.msCrypto : null;
const QUOTA = 65536;
setPRNG(function (x: Uint8Array, n: number) {
let i;
const v = new Uint8Array(n);
for (i = 0; i < n; i += QUOTA) {
cr.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA)));
}
for (i = 0; i < n; i++) x[i] = v[i];
cleanup(v);
});
// function initPRNG() {
// // Initialize PRNG if environment provides CSPRNG.
// // If not, methods calling randombytes will throw.
// // @ts-ignore-error
// const cr = typeof self !== "undefined" ? self.crypto || self.msCrypto : null;
// if (cr && cr.getRandomValues) {
// // Browsers.
// const QUOTA = 65536;
// setPRNG(function (x: Uint8Array, n: number) {
// let i;
// const v = new Uint8Array(n);
// for (i = 0; i < n; i += QUOTA) {
// cr.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA)));
// }
// for (i = 0; i < n; i++) x[i] = v[i];
// cleanup(v);
// });
// } else if (typeof require !== "undefined") {
// // Node.js.
// // eslint-disable-next-line @typescript-eslint/no-var-requires
// const cr = require("crypto");
// if (cr && cr.randomBytes) {
// setPRNG(function (x: Uint8Array, n: number) {
// const v = cr.randomBytes(n);
// for (let i = 0; i < n; i++) x[i] = v[i];
// cleanup(v);
// });
// }
// }
// }
// initPRNG();

View File

@ -22,22 +22,4 @@ export {
getDefaultNodeWallet,
DefaultNodeWalletArgs,
} from "./headless/helpers.js";
import { setPRNG } from './crypto/primitives/nacl-fast.js';
import cr from 'crypto';
function cleanup(arr: Uint8Array): void {
for (let i = 0; i < arr.length; i++) arr[i] = 0;
}
// Initialize PRNG if environment provides CSPRNG.
// If not, methods calling randombytes will throw.
if (cr && cr.randomBytes) {
setPRNG(function (x: Uint8Array, n: number) {
const v = cr.randomBytes(n);
for (let i = 0; i < n; i++) x[i] = v[i];
cleanup(v);
});
}
export * from "./crypto/workers/nodeThreadWorker.js";

View File

@ -36,7 +36,6 @@ export * from "./db-utils.js";
export { CryptoImplementation } from "./crypto/workers/cryptoImplementation.js";
export type { CryptoWorker } from "./crypto/workers/cryptoWorker.js";
export { CryptoWorkerFactory, CryptoApi } from "./crypto/workers/cryptoApi.js";
export * from "./crypto/talerCrypto.js";
export * from "./pending-types.js";

View File

@ -53,14 +53,12 @@ import {
Logger,
timestampToIsoString,
WalletBackupContentV1,
} from "@gnu-taler/taler-util";
import { InternalWalletState } from "../../common.js";
import { hash } from "../../crypto/primitives/nacl-fast.js";
import {
hash,
encodeCrock,
getRandomBytes,
stringToBytes,
} from "../../crypto/talerCrypto.js";
} from "@gnu-taler/taler-util";
import { InternalWalletState } from "../../common.js";
import {
AbortStatus,
CoinSourceType,

View File

@ -55,11 +55,11 @@ import {
} from "@gnu-taler/taler-util";
import { gunzipSync, gzipSync } from "fflate";
import { InternalWalletState } from "../../common.js";
import { kdf } from "../../crypto/primitives/kdf.js";
import { kdf } from "@gnu-taler/taler-util";
import {
secretbox,
secretbox_open,
} from "../../crypto/primitives/nacl-fast.js";
} from "@gnu-taler/taler-util";
import {
bytesToString,
decodeCrock,
@ -70,7 +70,7 @@ import {
hash,
rsaBlind,
stringToBytes,
} from "../../crypto/talerCrypto.js";
} from "@gnu-taler/taler-util";
import { CryptoApi } from "../../crypto/workers/cryptoApi.js";
import {
BackupProviderRecord,

View File

@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { encodeCrock, getRandomBytes } from "../../crypto/talerCrypto.js";
import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";
import {
ConfigRecord,
WalletBackupConfState,

View File

@ -39,12 +39,12 @@ import {
URL,
} from "@gnu-taler/taler-util";
import { InternalWalletState } from "../common.js";
import { kdf } from "../crypto/primitives/kdf.js";
import { kdf } from "@gnu-taler/taler-util";
import {
encodeCrock,
getRandomBytes,
stringToBytes,
} from "../crypto/talerCrypto.js";
} from "@gnu-taler/taler-util";
import { DepositGroupRecord } from "../db.js";
import { guardOperationException } from "../errors.js";
import { selectPayCoins } from "../util/coinSelection.js";

View File

@ -40,7 +40,7 @@ import {
TalerErrorDetails,
Timestamp,
} from "@gnu-taler/taler-util";
import { decodeCrock, encodeCrock, hash } from "../crypto/talerCrypto.js";
import { decodeCrock, encodeCrock, hash } from "@gnu-taler/taler-util";
import { CryptoApi } from "../crypto/workers/cryptoApi.js";
import {
DenominationRecord,

View File

@ -54,7 +54,7 @@ import {
URL,
getDurationRemaining,
} from "@gnu-taler/taler-util";
import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto.js";
import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";
import {
PayCoinSelection,
CoinCandidateSelection,

View File

@ -32,7 +32,7 @@ import {
RefreshReason,
TalerErrorDetails,
} from "@gnu-taler/taler-util";
import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto.js";
import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";
import {
CoinRecord,
CoinSourceType,

View File

@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto.js";
import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";
import {
CoinRecord,
CoinSourceType,

View File

@ -32,7 +32,7 @@ import {
TalerErrorCode,
addPaytoQueryParams,
} from "@gnu-taler/taler-util";
import { randomBytes } from "../crypto/primitives/nacl-fast.js";
import { randomBytes } from "@gnu-taler/taler-util";
import {
ReserveRecordStatus,
ReserveBankInfo,
@ -63,7 +63,7 @@ import {
processWithdrawGroup,
getBankWithdrawalInfo,
} from "./withdraw.js";
import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto.js";
import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";
import { Logger, URL } from "@gnu-taler/taler-util";
import {
readSuccessResponseJsonOrErrorCode,

View File

@ -55,7 +55,7 @@ import {
getHttpResponseErrorDetails,
readSuccessResponseJsonOrThrow,
} from "../util/http.js";
import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto.js";
import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";
const logger = new Logger("operations/tip.ts");

View File

@ -15,14 +15,14 @@
*/
import { canonicalJson } from "@gnu-taler/taler-util";
import { kdf } from "../crypto/primitives/kdf.js";
import { kdf } from "@gnu-taler/taler-util";
import {
decodeCrock,
encodeCrock,
getRandomBytes,
hash,
stringToBytes,
} from "../crypto/talerCrypto.js";
} from "@gnu-taler/taler-util";
export namespace ContractTermsUtil {
export type PathPredicate = (path: string[]) => boolean;

View File

@ -198,7 +198,7 @@ export async function readSuccessResponseJsonOrErrorCode<T>(
let parsedResponse: T;
try {
parsedResponse = codec.decode(respJson);
} catch (e) {
} catch (e: any) {
throw OperationFailedError.fromCode(
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
"Response invalid",

View File

@ -14,13 +14,24 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { Amounts, BackupBackupProviderTerms, canonicalizeBaseUrl, i18n } from "@gnu-taler/taler-util";
import { verify } from "@gnu-taler/taler-wallet-core/src/crypto/primitives/nacl-fast";
import {
Amounts,
BackupBackupProviderTerms,
canonicalizeBaseUrl,
i18n,
} from "@gnu-taler/taler-util";
import { VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import { Checkbox } from "../components/Checkbox";
import { ErrorMessage } from "../components/ErrorMessage";
import { Button, ButtonPrimary, Input, LightText, PopupBox, SmallLightText } from "../components/styled/index";
import {
Button,
ButtonPrimary,
Input,
LightText,
PopupBox,
SmallLightText,
} from "../components/styled/index";
import * as wxApi from "../wxApi";
interface Props {
@ -30,52 +41,65 @@ interface Props {
function getJsonIfOk(r: Response) {
if (r.ok) {
return r.json()
return r.json();
} else {
if (r.status >= 400 && r.status < 500) {
throw new Error(`URL may not be right: (${r.status}) ${r.statusText}`)
throw new Error(`URL may not be right: (${r.status}) ${r.statusText}`);
} else {
throw new Error(`Try another server: (${r.status}) ${r.statusText || 'internal server error'}`)
throw new Error(
`Try another server: (${r.status}) ${
r.statusText || "internal server error"
}`,
);
}
}
}
export function ProviderAddPage({ onBack }: Props): VNode {
const [verifying, setVerifying] = useState<{ url: string, name: string, provider: BackupBackupProviderTerms } | undefined>(undefined)
const [verifying, setVerifying] = useState<
| { url: string; name: string; provider: BackupBackupProviderTerms }
| undefined
>(undefined);
async function getProviderInfo(url: string): Promise<BackupBackupProviderTerms> {
async function getProviderInfo(
url: string,
): Promise<BackupBackupProviderTerms> {
return fetch(`${url}config`)
.catch(e => { throw new Error(`Network error`) })
.then(getJsonIfOk)
.catch((e) => {
throw new Error(`Network error`);
})
.then(getJsonIfOk);
}
if (!verifying) {
return <SetUrlView
return (
<SetUrlView
onCancel={onBack}
onVerify={(url) => getProviderInfo(url)}
onConfirm={(url, name) => getProviderInfo(url)
onConfirm={(url, name) =>
getProviderInfo(url)
.then((provider) => {
setVerifying({ url, name, provider });
})
.catch(e => e.message)
.catch((e) => e.message)
}
/>
);
}
return <ConfirmProviderView
return (
<ConfirmProviderView
provider={verifying.provider}
url={verifying.url}
onCancel={() => {
setVerifying(undefined);
}}
onConfirm={() => {
wxApi.addBackupProvider(verifying.url, verifying.name).then(onBack)
wxApi.addBackupProvider(verifying.url, verifying.name).then(onBack);
}}
/>
);
}
export interface SetUrlViewProps {
initialValue?: string;
onCancel: () => void;
@ -84,83 +108,137 @@ export interface SetUrlViewProps {
withError?: string;
}
export function SetUrlView({ initialValue, onCancel, onVerify, onConfirm, withError }: SetUrlViewProps) {
const [value, setValue] = useState<string>(initialValue || "")
const [urlError, setUrlError] = useState(false)
const [name, setName] = useState<string|undefined>(undefined)
const [error, setError] = useState<string | undefined>(withError)
export function SetUrlView({
initialValue,
onCancel,
onVerify,
onConfirm,
withError,
}: SetUrlViewProps) {
const [value, setValue] = useState<string>(initialValue || "");
const [urlError, setUrlError] = useState(false);
const [name, setName] = useState<string | undefined>(undefined);
const [error, setError] = useState<string | undefined>(withError);
useEffect(() => {
try {
const url = canonicalizeBaseUrl(value)
onVerify(url).then(r => {
setUrlError(false)
setName(new URL(url).hostname)
}).catch(() => {
setUrlError(true)
setName(undefined)
const url = canonicalizeBaseUrl(value);
onVerify(url)
.then((r) => {
setUrlError(false);
setName(new URL(url).hostname);
})
.catch(() => {
setUrlError(true);
setName(undefined);
});
} catch {
setUrlError(true)
setName(undefined)
setUrlError(true);
setName(undefined);
}
}, [value])
return <PopupBox>
}, [value]);
return (
<PopupBox>
<section>
<h1> Add backup provider</h1>
<ErrorMessage title={error && "Could not get provider information"} description={error} />
<ErrorMessage
title={error && "Could not get provider information"}
description={error}
/>
<LightText> Backup providers may charge for their service</LightText>
<p>
<Input invalid={urlError}>
<label>URL</label>
<input type="text" placeholder="https://" value={value} onChange={(e) => setValue(e.currentTarget.value)} />
<input
type="text"
placeholder="https://"
value={value}
onChange={(e) => setValue(e.currentTarget.value)}
/>
</Input>
<Input>
<label>Name</label>
<input type="text" disabled={name === undefined} value={name} onChange={e => setName(e.currentTarget.value)}/>
<input
type="text"
disabled={name === undefined}
value={name}
onChange={(e) => setName(e.currentTarget.value)}
/>
</Input>
</p>
</section>
<footer>
<Button onClick={onCancel}><i18n.Translate> &lt; Back</i18n.Translate></Button>
<Button onClick={onCancel}>
<i18n.Translate> &lt; Back</i18n.Translate>
</Button>
<ButtonPrimary
disabled={!value && !urlError}
onClick={() => {
const url = canonicalizeBaseUrl(value)
return onConfirm(url, name!).then(r => r ? setError(r) : undefined)
}}><i18n.Translate>Next</i18n.Translate></ButtonPrimary>
const url = canonicalizeBaseUrl(value);
return onConfirm(url, name!).then((r) =>
r ? setError(r) : undefined,
);
}}
>
<i18n.Translate>Next</i18n.Translate>
</ButtonPrimary>
</footer>
</PopupBox>
);
}
export interface ConfirmProviderViewProps {
provider: BackupBackupProviderTerms,
url: string,
provider: BackupBackupProviderTerms;
url: string;
onCancel: () => void;
onConfirm: () => void;
}
export function ConfirmProviderView({ url, provider, onCancel, onConfirm }: ConfirmProviderViewProps) {
export function ConfirmProviderView({
url,
provider,
onCancel,
onConfirm,
}: ConfirmProviderViewProps) {
const [accepted, setAccepted] = useState(false);
return <PopupBox>
return (
<PopupBox>
<section>
<h1>Review terms of service</h1>
<div>Provider URL: <a href={url} target="_blank">{url}</a></div>
<SmallLightText>Please review and accept this provider's terms of service</SmallLightText>
<div>
Provider URL:{" "}
<a href={url} target="_blank">
{url}
</a>
</div>
<SmallLightText>
Please review and accept this provider's terms of service
</SmallLightText>
<h2>1. Pricing</h2>
<p>
{Amounts.isZero(provider.annual_fee) ? 'free of charge' : `${provider.annual_fee} per year of service`}
{Amounts.isZero(provider.annual_fee)
? "free of charge"
: `${provider.annual_fee} per year of service`}
</p>
<h2>2. Storage</h2>
<p>
{provider.storage_limit_in_megabytes} megabytes of storage per year of service
{provider.storage_limit_in_megabytes} megabytes of storage per year of
service
</p>
<Checkbox label="Accept terms of service" name="terms" onToggle={() => setAccepted(old => !old)} enabled={accepted} />
<Checkbox
label="Accept terms of service"
name="terms"
onToggle={() => setAccepted((old) => !old)}
enabled={accepted}
/>
</section>
<footer>
<Button onClick={onCancel}><i18n.Translate> &lt; Back</i18n.Translate></Button>
<ButtonPrimary
disabled={!accepted}
onClick={onConfirm}><i18n.Translate>Add provider</i18n.Translate></ButtonPrimary>
<Button onClick={onCancel}>
<i18n.Translate> &lt; Back</i18n.Translate>
</Button>
<ButtonPrimary disabled={!accepted} onClick={onConfirm}>
<i18n.Translate>Add provider</i18n.Translate>
</ButtonPrimary>
</footer>
</PopupBox>
);
}

View File

@ -14,13 +14,24 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { Amounts, BackupBackupProviderTerms, canonicalizeBaseUrl, i18n } from "@gnu-taler/taler-util";
import { verify } from "@gnu-taler/taler-wallet-core/src/crypto/primitives/nacl-fast";
import {
Amounts,
BackupBackupProviderTerms,
canonicalizeBaseUrl,
i18n,
} from "@gnu-taler/taler-util";
import { VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import { Checkbox } from "../components/Checkbox";
import { ErrorMessage } from "../components/ErrorMessage";
import { Button, ButtonPrimary, Input, LightText, WalletBox, SmallLightText } from "../components/styled/index";
import {
Button,
ButtonPrimary,
Input,
LightText,
WalletBox,
SmallLightText,
} from "../components/styled/index";
import * as wxApi from "../wxApi";
interface Props {
@ -30,52 +41,65 @@ interface Props {
function getJsonIfOk(r: Response) {
if (r.ok) {
return r.json()
return r.json();
} else {
if (r.status >= 400 && r.status < 500) {
throw new Error(`URL may not be right: (${r.status}) ${r.statusText}`)
throw new Error(`URL may not be right: (${r.status}) ${r.statusText}`);
} else {
throw new Error(`Try another server: (${r.status}) ${r.statusText || 'internal server error'}`)
throw new Error(
`Try another server: (${r.status}) ${
r.statusText || "internal server error"
}`,
);
}
}
}
export function ProviderAddPage({ onBack }: Props): VNode {
const [verifying, setVerifying] = useState<{ url: string, name: string, provider: BackupBackupProviderTerms } | undefined>(undefined)
const [verifying, setVerifying] = useState<
| { url: string; name: string; provider: BackupBackupProviderTerms }
| undefined
>(undefined);
async function getProviderInfo(url: string): Promise<BackupBackupProviderTerms> {
async function getProviderInfo(
url: string,
): Promise<BackupBackupProviderTerms> {
return fetch(`${url}config`)
.catch(e => { throw new Error(`Network error`) })
.then(getJsonIfOk)
.catch((e) => {
throw new Error(`Network error`);
})
.then(getJsonIfOk);
}
if (!verifying) {
return <SetUrlView
return (
<SetUrlView
onCancel={onBack}
onVerify={(url) => getProviderInfo(url)}
onConfirm={(url, name) => getProviderInfo(url)
onConfirm={(url, name) =>
getProviderInfo(url)
.then((provider) => {
setVerifying({ url, name, provider });
})
.catch(e => e.message)
.catch((e) => e.message)
}
/>
);
}
return <ConfirmProviderView
return (
<ConfirmProviderView
provider={verifying.provider}
url={verifying.url}
onCancel={() => {
setVerifying(undefined);
}}
onConfirm={() => {
wxApi.addBackupProvider(verifying.url, verifying.name).then(onBack)
wxApi.addBackupProvider(verifying.url, verifying.name).then(onBack);
}}
/>
);
}
export interface SetUrlViewProps {
initialValue?: string;
onCancel: () => void;
@ -84,83 +108,137 @@ export interface SetUrlViewProps {
withError?: string;
}
export function SetUrlView({ initialValue, onCancel, onVerify, onConfirm, withError }: SetUrlViewProps) {
const [value, setValue] = useState<string>(initialValue || "")
const [urlError, setUrlError] = useState(false)
const [name, setName] = useState<string|undefined>(undefined)
const [error, setError] = useState<string | undefined>(withError)
export function SetUrlView({
initialValue,
onCancel,
onVerify,
onConfirm,
withError,
}: SetUrlViewProps) {
const [value, setValue] = useState<string>(initialValue || "");
const [urlError, setUrlError] = useState(false);
const [name, setName] = useState<string | undefined>(undefined);
const [error, setError] = useState<string | undefined>(withError);
useEffect(() => {
try {
const url = canonicalizeBaseUrl(value)
onVerify(url).then(r => {
setUrlError(false)
setName(new URL(url).hostname)
}).catch(() => {
setUrlError(true)
setName(undefined)
const url = canonicalizeBaseUrl(value);
onVerify(url)
.then((r) => {
setUrlError(false);
setName(new URL(url).hostname);
})
.catch(() => {
setUrlError(true);
setName(undefined);
});
} catch {
setUrlError(true)
setName(undefined)
setUrlError(true);
setName(undefined);
}
}, [value])
return <WalletBox>
}, [value]);
return (
<WalletBox>
<section>
<h1> Add backup provider</h1>
<ErrorMessage title={error && "Could not get provider information"} description={error} />
<ErrorMessage
title={error && "Could not get provider information"}
description={error}
/>
<LightText> Backup providers may charge for their service</LightText>
<p>
<Input invalid={urlError}>
<label>URL</label>
<input type="text" placeholder="https://" value={value} onChange={(e) => setValue(e.currentTarget.value)} />
<input
type="text"
placeholder="https://"
value={value}
onChange={(e) => setValue(e.currentTarget.value)}
/>
</Input>
<Input>
<label>Name</label>
<input type="text" disabled={name === undefined} value={name} onChange={e => setName(e.currentTarget.value)}/>
<input
type="text"
disabled={name === undefined}
value={name}
onChange={(e) => setName(e.currentTarget.value)}
/>
</Input>
</p>
</section>
<footer>
<Button onClick={onCancel}><i18n.Translate> &lt; Back</i18n.Translate></Button>
<Button onClick={onCancel}>
<i18n.Translate> &lt; Back</i18n.Translate>
</Button>
<ButtonPrimary
disabled={!value && !urlError}
onClick={() => {
const url = canonicalizeBaseUrl(value)
return onConfirm(url, name!).then(r => r ? setError(r) : undefined)
}}><i18n.Translate>Next</i18n.Translate></ButtonPrimary>
const url = canonicalizeBaseUrl(value);
return onConfirm(url, name!).then((r) =>
r ? setError(r) : undefined,
);
}}
>
<i18n.Translate>Next</i18n.Translate>
</ButtonPrimary>
</footer>
</WalletBox>
);
}
export interface ConfirmProviderViewProps {
provider: BackupBackupProviderTerms,
url: string,
provider: BackupBackupProviderTerms;
url: string;
onCancel: () => void;
onConfirm: () => void;
}
export function ConfirmProviderView({ url, provider, onCancel, onConfirm }: ConfirmProviderViewProps) {
export function ConfirmProviderView({
url,
provider,
onCancel,
onConfirm,
}: ConfirmProviderViewProps) {
const [accepted, setAccepted] = useState(false);
return <WalletBox>
return (
<WalletBox>
<section>
<h1>Review terms of service</h1>
<div>Provider URL: <a href={url} target="_blank">{url}</a></div>
<SmallLightText>Please review and accept this provider's terms of service</SmallLightText>
<div>
Provider URL:{" "}
<a href={url} target="_blank">
{url}
</a>
</div>
<SmallLightText>
Please review and accept this provider's terms of service
</SmallLightText>
<h2>1. Pricing</h2>
<p>
{Amounts.isZero(provider.annual_fee) ? 'free of charge' : `${provider.annual_fee} per year of service`}
{Amounts.isZero(provider.annual_fee)
? "free of charge"
: `${provider.annual_fee} per year of service`}
</p>
<h2>2. Storage</h2>
<p>
{provider.storage_limit_in_megabytes} megabytes of storage per year of service
{provider.storage_limit_in_megabytes} megabytes of storage per year of
service
</p>
<Checkbox label="Accept terms of service" name="terms" onToggle={() => setAccepted(old => !old)} enabled={accepted} />
<Checkbox
label="Accept terms of service"
name="terms"
onToggle={() => setAccepted((old) => !old)}
enabled={accepted}
/>
</section>
<footer>
<Button onClick={onCancel}><i18n.Translate> &lt; Back</i18n.Translate></Button>
<ButtonPrimary
disabled={!accepted}
onClick={onConfirm}><i18n.Translate>Add provider</i18n.Translate></ButtonPrimary>
<Button onClick={onCancel}>
<i18n.Translate> &lt; Back</i18n.Translate>
</Button>
<ButtonPrimary disabled={!accepted} onClick={onConfirm}>
<i18n.Translate>Add provider</i18n.Translate>
</ButtonPrimary>
</footer>
</WalletBox>
);
}

View File

@ -12,6 +12,19 @@ importers:
'@linaria/shaker': 3.0.0-beta.7
esbuild: 0.12.21
packages/anastasis-core:
specifiers:
'@gnu-taler/taler-util': workspace:^0.8.3
ava: ^3.15.0
hash-wasm: ^4.9.0
typescript: ^4.4.3
dependencies:
'@gnu-taler/taler-util': link:../taler-util
hash-wasm: 4.9.0
devDependencies:
ava: 3.15.0
typescript: 4.4.3
packages/idb-bridge:
specifiers:
'@rollup/plugin-commonjs': ^17.1.0
@ -52,6 +65,7 @@ importers:
specifiers:
'@types/node': ^14.14.22
ava: ^3.15.0
big-integer: ^1.6.48
esbuild: ^0.9.2
jed: ^1.1.1
prettier: ^2.2.1
@ -59,6 +73,7 @@ importers:
tslib: ^2.1.0
typescript: ^4.2.3
dependencies:
big-integer: 1.6.48
jed: 1.1.1
tslib: 2.1.0
devDependencies:
@ -11813,6 +11828,10 @@ packages:
safe-buffer: 5.2.1
dev: true
/hash-wasm/4.9.0:
resolution: {integrity: sha512-7SW7ejyfnRxuOc7ptQHSf4LDoZaWOivfzqw+5rpcQku0nHfmicPKE51ra9BiRLAmT8+gGLestr1XroUkqdjL6w==}
dev: false
/hash.js/1.1.7:
resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==}
dependencies:
@ -19292,6 +19311,12 @@ packages:
hasBin: true
dev: true
/typescript/4.4.3:
resolution: {integrity: sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/uglify-js/3.12.5:
resolution: {integrity: sha512-SgpgScL4T7Hj/w/GexjnBHi3Ien9WS1Rpfg5y91WXMj9SY997ZCQU76mH4TpLwwfmMvoOU8wiaRkIf6NaH3mtg==}
engines: {node: '>=0.8.0'}