Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • main
  • raw-crypt
  • ts
  • v3.0.1
  • v3.0.2
  • v3.1.0
  • v3.2.0
  • v3.3.0
  • v3.3.1
  • v3.3.2
  • v3.3.3
  • v3.4.0
  • v3.4.1
  • v3.4.2
  • v3.5.0
  • v3.5.1
  • v3.5.2
  • v3.5.3
  • v3.5.4
  • v3.5.5
  • v3.5.6
  • v3.5.7
  • v3.5.8
23 results

Target

Select target project
  • libs/g1lib.js
  • manutopik/g1lib.js
2 results
Select Git revision
  • main
  • v3.0.1
  • v3.0.2
  • v3.1.0
4 results
Show changes
Commits on Source (32)
Showing
with 9966 additions and 9871 deletions
......@@ -2,6 +2,7 @@ stages:
- build
- test
- publish
- release
build:
stage: build
......@@ -58,10 +59,15 @@ mirror-git.duniter.org:
pages:
stage: test
image: node:latest
coverage: '/Statements[^0-9]+(\d+\.\d+)%/'
variables:
VERSION: $CI_COMMIT_TAG || latest
script:
- npm run test:production
- mkdir -p public/dist
- cp -rf generated/npm/* public/dist/
#- 'cp generated/npm/browser/all.mjs public/dist/browser/$VERSION.mjs'
#- 'cp generated/npm/nodejs/all.mjs public/dist/nodejs/$VERSION.mjs'
- mv generated/coverage public/
- mv generated/jscpd/html public/jscpd
- mv generated/maintainability public/
......@@ -84,5 +90,40 @@ npm:
- cd ./generated/npm
- echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}'>.npmrc
- npm publish
- ARTIFACT_NAME="${CI_COMMIT_TAG}_for_nodejs"
- echo $ARTIFACT_NAME
artifacts:
untracked: true
name: $ARTIFACT_NAME
paths:
- generated/npm/nodejs/
only:
- tags
##--------------------------------RELEASE-------------------------------------------------------
release:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:latest
script:
- ls generated/npm/*
- ARTIFACT_BROWSER="${CI_COMMIT_TAG}_for_browser"
- echo $ARTIFACT_BROWSER
release:
tag_name: '$CI_COMMIT_TAG'
name: '$CI_COMMIT_TAG'
description: '$CI_COMMIT_MESSAGE'
assets:
links:
- name: "g1lib_${$CI_COMMIT_TAG}_for_browser"
url: 'https://libs.pages.duniter.org/g1lib.js/public/dist/browser/$CI_COMMIT_TAG.mjs'
link_type: 'package'
- name: "g1lib_${$CI_COMMIT_TAG}_for_nodejs"
url: 'https://libs.pages.duniter.org/g1lib.js/public/dist/nodejs/$CI_COMMIT_TAG.mjs'
link_type: 'package'
artifacts:
untracked: true
name: $ARTIFACT_BROWSER
paths:
- generated/npm/browser/
only:
- tags
......@@ -7,15 +7,104 @@ Le format est basé sur [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
et ce projet adhère au [versionnage sémantique](https://semver.org/spec/v2.0.0.html).
## Evolutions probable / Roadmap :
- GraphQL stuff
- @@@@ comme séparateur entre identifiant secret et mdp pour la génération de combinaison à tester (usage principal Gsper)
- supprimer automatiquement le code inutile dans les lib (Tree Shaking)
- HD wallet (dérivation)
- sharding
- udid / civil_id_hash
- GVA && indexer v2s GraphQL stuff (gestion des paiements, détection des paiements...)
## [Non-publié/Non-Stabilisé] (par [1000i100])
## [Version 3.5.8] - 2023-01-12 (par [1000i100])
### Corrections
- dictionary-parser ne génère plus de doublon superflu lorsque des quantités variables sont indiquées ( comme "A{0,5}" par exemple)
## [Version 3.5.7] - 2023-01-01 (par [1000i100])
### Corrections
- dictionary-tree ne supprime plus les alternatives vides ce qui permet d'avoir le comportement attendu avec des chaines comme "(|a)".
## [Version 3.5.6] - 2023-01-01 (par [1000i100])
### Corrections
- dictionary-parser génère désormais correctement les variantes désaccentuées et majuscule/minuscule, sans couplage entre identifiant secret et mot de passe.
## [Version 3.5.5] - 2022-12-31 (par [1000i100])
### Corrections
- Dans dictionary et dictionary-parser, les différents modes autour des majuscules sont désormais strictement progressif : 0 tel quel, 1 TOUT MAJUSCULE + tout minuscule + Première Lettre En Majuscule, 2 comme 1 mais en passant en minuscule chaque caractère individuellement.
## [Version 3.5.4] - 2022-12-17 (par [1000i100])
### Corrections
- Dictionary bascule en mode sans échec (non regex) si l'interprétation du texte fourni échoue (ou si options.escapeAll=1)
- trivialDedup tri désormais les alternatives pour mieux les dédoublonner.
- dictionary-parse prend en paramètres un tableau associatif 'options' au lieu d'une liste de paramètres
- '\' est désormais considéré comme un caractère spécial
- Les sérialisations internes (rawSerialize) de Dictionary ne reconvertissent plus les caractères spéciaux
## [Version 3.5.3] - 2022-12-16 (par [1000i100])
### Corrections
- '\' est désormais considéré comme un caractère spécial
- Les sérialisations internes (rawSerialize) de Dictionary ne reconvertissent plus les caractères spéciaux
## [Version 3.5.2] - 2022-12-16 (par [1000i100])
### Corrections
- Dictionary échappe correctement les caractères spéciaux
- Dictionary splitGet permet d'avoir séparément idSec et pwd sans ambiguïté liée aux caractères échappés.
## [Version 3.5.1] - 2022-12-09 (par [1000i100])
### Corrections
- Dictionary applique systématiquement les variantes passées en option.
## [Version 3.5.0] - 2022-11-27 (par [1000i100])
### Ajouté
- dictionary intègre un mécanisme de cache pour détecter les doublons. Il est désactivable au-delà d'1_000_000 pour éviter les crashs et saturation de mémoire.
- dictionary-parser gère toutes les syntaxes d'expression régulières qu'utilisaient gsper v2 + des situations plus complexes
- dictionary-parser permet de distinguer identifiant secret et mot de passe via le séparateur `@@`
- dictionary-parser permet plusieurs types de déclinaisons : sans accents, accents optionnels, sans majuscule, majuscule optionnelle, tout en majuscule, et des déclinaisons type leetSpeak.
- dictionary-tree permet de savoir combien de combinaisons sont possibles et d'itérer dessus sans avoir besoin de les pré-générer.
### Corrections
- Vérification à chaque build (et donc dans la CI) que les packets destinés à tourner dans le navigateur n'ont aucunes dépendances.
- Suppression des dépendances résiduelles.
## [Version 3.4.2] - 2022-11-20 (par [1000i100])
### Corrections
- checkKey ne complète plus automatiquement les clefs trop courtes et envoi donc l'erreur attendue pour les clefs trop courtes.
## [Version 3.4.1] - 2022-11-20 (par [1000i100])
### Corrections
- checkKey envoi désormais des erreurs nommées, utilisable pour guider les usagers.
## [Version 3.4.0] - 2022-11-15 (par [1000i100])
### Ajouté
- crypto.textEncrypt(jsonMessage, senderPrivateKey, receiverPubKey) retourne un json format cesium+ avec `jsonMessage.title` et `jsonMessage.content` chiffrés.
- textDecrypt(jsonMessage, receiverPrivateKey)) retourne en json les champs `title` et `content` déchiffrés à partir d'un message chiffré format cesium+.
## [Version 3.3.3] - 2022-11-15 (par [1000i100])
### Corrections
- les versions 3.3.x antérieur à celle-ci, cherchaient à importer la lib crypto de node depuis le navigateur c'est corrigé.
## [Version 3.3.2] - 2022-11-12 (par [1000i100])
### Corrections
- export de sha256 pour le rendre utilisable par les clients.
## [Version 3.3.1] - 2022-09-30 (par [1000i100])
### Corrections
- suppression de crypto.isPubkey(pubkey) qui ne validait qu'à partir d'une regex contrairement à crypto.isPubKey(pubKey) qui effectue les vérifications implémentées dans la v3.3.0
## [Version 3.3.0] - 2022-09-30 (par [1000i100])
### Ajouté
- crypto.isPubKey(pubKey) identique à checkKey mais retourne vrai ou faux là où checkKey throw une erreur en cas d'échec de validation.
- crypto.checkKey(pubKey) accepte désormais aussi les clefs sans checksum grace aux vérifications précises désormais effectuable sur la clef.
- crypto.checkKey(pubKey) accepte également les clefs au format binaire.
- crypto.isDuniterPubKey(b58pubKey) vérifie la conformité au Protocol Blockchain Duniter [V11](https://git.duniter.org/documents/rfcs/-/blob/master/rfc/0009_Duniter_Blockchain_Protocol_V11.md#public-key) et [V12](https://git.duniter.org/documents/rfcs/-/blob/master/rfc/0010_Duniter_Blockchain_Protocol_V12.md#public-key)
- crypto.isEd25519PubKey(b58pubKey) vérifie que la clef désigne bien un point sur la courbe ed25519 tel que défini dans la [RFC8032 5.1.3](https://www.rfc-editor.org/rfc/rfc8032#page-11)
### Corrections
- La CI ajuste désormais les chemins d'inclusion pour construire les builds static, que ces chemins sont décrits entre simple ou double guillemet.
## [Version 3.2.0] - 2022-01-31 (par [1000i100])
### Ajouté
- crypto.checkKey(pubKeyWithChecksum)
- crypto.pubKey2checksum(b58pubKey, optionalBool:b58viewDependant, optionalBool:checksumWithoutLeadingZero)
- EXPERIMENTAL : crypto.pubKey2checksum(b58pubKey, optionalBool:b58viewDependant, optionalBool:checksumWithoutLeadingZero) ATTENTION, la syntaxe pourrait changer selon ce qui est choisi comme algo de checksum recommandé dans les spec.
### Corrections
- correction des encodage et décodage base64 désormais conforme à la RFC 4648 §4 (standard le plus répandu)
## [Version 3.1.0] - 2021-04-01 (par [1000i100] & [Hugo])
### Ajouté
......@@ -29,11 +118,11 @@ et ce projet adhère au [versionnage sémantique](https://semver.org/spec/v2.0.0
## [Version 3.0.2] - 2020-12-10 (par [1000i100])
### Ajouté
- minification des modules g1lib
- publication automatisé sur npm à la publication de tag (sous réserve que la CI passe)
- publication automatisée sur npm à la publication de tag (sous réserve que la CI passe)
## [Version 3.0.1] - 2020-12-10 (par [1000i100])
### Ajouté
- test unitaire exectué dans la CI
- test unitaire exécuté dans la CI
- couverture de test
- suivi de la maintenabilité / complexité
- suivi de la duplication
......@@ -48,7 +137,7 @@ et ce projet adhère au [versionnage sémantique](https://semver.org/spec/v2.0.0
## [Version 2.1.0] - 2018-06-27 (par [1000i100])
### Ajouté
- syntaxe =référence> pour faire des références syncronisé et éviter de générer des variantes non souhaitées.
- syntaxe =référence> pour faire des références syncronisées et éviter de générer des variantes non souhaitées.
## [Version 2.0.0] - 2018-05-10 (par [1000i100])
......@@ -57,12 +146,12 @@ et ce projet adhère au [versionnage sémantique](https://semver.org/spec/v2.0.0
##### Générateur de variantes de mot de passe
- Déclinaisons avec Majuscules
- Désaccentuation
- Déclinaison avancées façon expression régulière
- Déclinaison avancée façon expression régulière
##### Documentation
- Rédaction d'une documentation des générateur de variante de mot de passe
##### Améliorations technique
- Ajout de test unitaire (meilleur fiabilité).
- Différentiation de la lib pour la partie crypto et de celle de génération de variantes (meilleur maintenabilité et évolutivité).
- Rédaction d'une documentation des générateurs de variante de mot de passe
##### Améliorations techniques
- Ajout de test unitaire (meilleure fiabilité).
- Différentiation de la lib pour la partie crypto et de celle de génération de variantes (meilleure maintenabilité et évolutivité).
## [Version 1.0.1 (Proof of Concept)] - 2018-04-18 (par [1000i100])
### Ajouté
......@@ -71,8 +160,24 @@ et ce projet adhère au [versionnage sémantique](https://semver.org/spec/v2.0.0
- intégration des librairies de crypto nécessaires
- calcul de la clef publique correspondant à chaque combinaison de secrets saisie, et comparaison à la clef publique de référence.
[Non-publié/Non-Stabilisé]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.1.0...master
[Non-publié/Non-Stabilisé]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.7...main
[Version 3.5.7]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.6...v3.5.7
[Version 3.5.6]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.5...v3.5.6
[Version 3.5.5]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.4...v3.5.5
[Version 3.5.4]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.3...v3.5.4
[Version 3.5.3]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.2...v3.5.3
[Version 3.5.2]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.1...v3.5.2
[Version 3.5.1]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.5.0...v3.5.1
[Version 3.5.0]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.4.2...v3.5.0
[Version 3.4.2]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.4.1...v3.4.2
[Version 3.4.1]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.4.0...v3.4.1
[Version 3.4.0]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.3.3...v3.4.0
[Version 3.3.3]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.3.2...v3.3.3
[Version 3.3.2]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.3.1...v3.3.2
[Version 3.3.1]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.3.0...v3.3.1
[Version 3.3.0]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.2.0...v3.3.0
[Version 3.2.0]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.1.0...v3.2.0
[Version 3.1.0]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.0.3...v3.1.0
[Version 3.0.2]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.0.1...v3.0.2
[Version 3.0.1]: https://git.duniter.org/libs/g1lib.js/-/compare/v3.0.0...v3.0.1
......
......@@ -28,6 +28,7 @@ fs.writeFileSync(`generated/tmpNodejs/all.mjs`, allMjsContent);
function copy(srcPath, targetPath, fileName = '') {
let content = fs.readFileSync(`${srcPath}${fileName}`, 'utf8');
content = content.replace(/from '\.\.\//g, 'from \'../../');
content = content.replace(/from "\.\.\//g, 'from "../../');
fs.writeFileSync(`${targetPath}${fileName}`, content);
if (!fileName.includes('.test')) return;
content = content.replace(
......
import {readdirSync, readFileSync, writeFileSync} from 'node:fs';
import {minify} from 'terser';
import {transform} from 'esbuild';
const minifyCache = {};
minFold('generated/npm/browser/');
minFold('generated/npm/nodejs/');
async function minFold(folder) {
readdirSync(folder).forEach(async fileName => {
if (!fileName.includes('.mjs')) return;
const orgContent = readFileSync(folder + fileName, 'utf8');
const minified = await minify(orgContent, {
toplevel: true,
ecma: 2020,
nameCache: minifyCache,
compress: {
passes: 2
},
format: {
comments: false
}
});
const orgContent = readFileSync(folder + fileName, 'utf8').replace(/import '[^ ']+';/g,'');
const minified = await transform(orgContent, { minify: true, treeShaking:true, target: "esnext", legalComments:'none', format: 'esm' });
writeFileSync(folder + fileName, minified.code);
});
}
const fs = require('fs');
import * as fs from 'fs';
const toRollup = [];
rollupToDoList('generated/tmpBrowser/', 'generated/npm/browser/');
rollupToDoList('generated/tmpNodejs/', 'generated/npm/nodejs/');
......
const fs = require('fs');
try {
fs.writeFileSync('generated/vendors/nacl.mjs',
fs.writeFileSync('generated/vendors/tweetnacl+ed2curve.mjs',
(fs.readFileSync('node_modules/tweetnacl/nacl-fast.js', 'utf8'))
.replace('(function(nacl) {', 'var nacl = {};')
.replace('})(typeof module !== \'undefined\' && module.exports ? module.exports : (self.nacl = self.nacl || {}));', 'export default nacl;')
+(fs.readFileSync('node_modules/ed2curve/src/index.mjs', 'utf8'))
.replace(/^[\s\S]+----\s+\/\/ Converts Ed25519[^\n]+/,'')
, 'utf8');
} catch (error) {
console.error(error);
......@@ -19,3 +21,18 @@ try {
console.error(error);
}
try {
fs.writeFileSync('generated/vendors/@noble-ed25519-node.mjs',
(fs.readFileSync('node_modules/@noble/ed25519/lib/esm/index.js', 'utf8'))
, 'utf8');
} catch (error) {
console.error(error);
}
try {
fs.writeFileSync('generated/vendors/@noble-ed25519-browser.mjs',
(fs.readFileSync('node_modules/@noble/ed25519/lib/esm/index.js', 'utf8'))
.replace(`import * as nodeCrypto from 'crypto';`,`let nodeCrypto;`)
, 'utf8');
} catch (error) {
console.error(error);
}
import {readdirSync, readFileSync} from 'node:fs';
zeroDep('generated/npm/browser/');
async function zeroDep(folder) {
readdirSync(folder).forEach(async fileName => {
if (!fileName.includes('.mjs') || fileName.includes('.test.mjs') || fileName.includes('.test-e2e.mjs')) return;
const content = readFileSync(folder + fileName, 'utf8');
if(folder.includes('browser') && content.includes('import ')) throw new Error('Dependencies found in '+folder+fileName);//process.exit(1);
});
}
[![pipeline status](https://git.duniter.org/libs/g1lib.js/badges/main/pipeline.svg)](https://git.duniter.org/libs/g1lib.js/-/pipelines)
[![coverage report](https://git.duniter.org/libs/g1lib.js/badges/main/coverage.svg)](https://libs.duniter.io/g1lib.js/coverage/)
[![coverage report](https://git.duniter.org/libs/g1lib.js/badges/main/coverage.svg)](https://libs.pages.duniter.org/g1lib.js/coverage/)
[![duplication](https://libs.duniter.io/g1lib.js/jscpd-badge.svg)](https://libs.duniter.io/g1lib.js/jscpd/)
[![maintainability](https://libs.duniter.io/g1lib.js/worst-maintainability.svg)](https://libs.duniter.io/g1lib.js/maintainability/)
[![duplication](https://libs.pages.duniter.org/g1lib.js/jscpd-badge.svg)](https://libs.pages.duniter.org/g1lib.js/jscpd/)
[![maintainability](https://libs.pages.duniter.org/g1lib.js/worst-maintainability.svg)](https://libs.pages.duniter.org/g1lib.js/maintainability/)
[![release](https://img.shields.io/npm/v/g1lib.svg)](https://www.npmjs.com/package/g1lib)
[![usage as download](https://img.shields.io/npm/dy/g1lib.svg)](https://www.npmjs.com/package/g1lib)
......@@ -27,6 +27,11 @@ pour mutualiser du code métier fiable, maintenable, facile à auditer et facile
## Usage
Pour la confidentialité de la navigation des internautes, les CDN publiques sont à proscrire en production.
Pour du prototypage rapide sans npm : https://unpkg.com/g1lib/
### Via npm
Installez nodejs puis en cli :
```
npm install --save g1lib
......@@ -35,14 +40,11 @@ En js :
```
import * as g1lib from "g1lib"
```
Si vous souhaitez la lib en asm, cjs ou autre packaging, [demandez-le](https://framagit.org/g1/g1lib.js/-/issues)
Pour la confidentialité de la navigation des internautes, les CDN publiques sont à proscrire en production.
Pour du prototypage rapide sans npm : https://g1.frama.io/g1lib.js/dist/all.mjs
Si vous souhaitez la lib en asm, cjs ou autre packaging, [demandez-le](https://git.duniter.org/libs/g1lib.js/-/issues)
## Contribuer
Vous pouvez proposer des évolutions sous forme de [ticket](https://framagit.org/g1/g1lib.js/-/issues) aussi bien que des demandes d'aide (en français ou en anglais).
Vous pouvez proposer des évolutions sous forme de [ticket](https://git.duniter.org/libs/g1lib.js/-/issues) aussi bien que des demandes d'aide (en français ou en anglais).
Les merge-request sont bienvenue.
Elles seront acceptées si :
......@@ -50,7 +52,7 @@ Elles seront acceptées si :
- elles sont couvertes par des tests unitaires
- leur code respecte les bonnes pratiques décrites dans l'ouvrage [Clean Code / Coder proprement](https://dl.leneveu.fr/public/Coder_Proprement.pdf) ISBN : 978-2-7440-4104-4
Si vous avez besoin d'aide pour respecter les critères d'acceptation, n'hésitez pas à demander (par [ticket](https://framagit.org/g1/g1lib.js/-/issues) ou [email](https://1forma-tic.fr/#contact)).
Si vous avez besoin d'aide pour respecter les critères d'acceptation, n'hésitez pas à demander (par [ticket](https://git.duniter.org/libs/g1lib.js/-/issues) ou [email](https://1forma-tic.fr/#contact)).
## Financer le développement
......
[![pipeline status](https://git.duniter.org/libs/g1lib.js/badges/main/pipeline.svg)](https://git.duniter.org/libs/g1lib.js/-/pipelines)
[![coverage report](https://git.duniter.org/libs/g1lib.js/badges/main/coverage.svg)](https://libs.duniter.io/g1lib.js/coverage/)
[![coverage report](https://git.duniter.org/libs/g1lib.js/badges/main/coverage.svg)](https://libs.pages.duniter.org/g1lib.js/coverage/)
[![duplication](https://libs.duniter.io/g1lib.js/jscpd-badge.svg)](https://libs.duniter.io/g1lib.js/jscpd/)
[![maintainability](https://libs.duniter.io/g1lib.js/worst-maintainability.svg)](https://libs.duniter.io/g1lib.js/maintainability/)
[![duplication](https://libs.pages.duniter.org/g1lib.js/jscpd-badge.svg)](https://libs.pages.duniter.org/g1lib.js/jscpd/)
[![maintainability](https://libs.pages.duniter.org/g1lib.js/worst-maintainability.svg)](https://libs.pages.duniter.org/g1lib.js/maintainability/)
[![release](https://img.shields.io/npm/v/g1lib.svg)](https://www.npmjs.com/package/g1lib)
[![usage as download](https://img.shields.io/npm/dy/g1lib.svg)](https://www.npmjs.com/package/g1lib)
......@@ -24,7 +24,10 @@ An ubiquitous static javascript lib for Ǧ1 / Duniter ecosystem with reliability
*modern esm import/export & high modularity improve tree-shaking
## Usage
For user privacy, CDN should not be used in production.
For quick prototyping without npm : https://unpkg.com/browse/g1lib/
### With npm
Install nodejs then in cli :
```
npm install --save g1lib
......@@ -33,14 +36,12 @@ In js :
```
import * as g1lib from "g1lib"
```
Other packaging use-case ? (asm, cjs...), [ask for it](https://framagit.org/g1/g1lib.js/-/issues)
Other packaging use-case ? (asm, cjs...), [ask for it](https://git.duniter.org/libs/g1lib.js/-/issues)
For user privacy, CDN should not be used in production.
For quick prototyping without npm : https://g1.frama.io/g1lib.js/dist/all.mjs
## Contribute
Ask for features or fix in [issues](https://framagit.org/g1/g1lib.js/-/issues).
Ask for features or fix in [issues](https://git.duniter.org/libs/g1lib.js/-/issues).
Merge-request welcome.
Quality requirement :
......@@ -48,7 +49,7 @@ Quality requirement :
- covered by unit-tests
- follow Clean Code good practices ISBN : 978-0-1323-5088-4
If you need help to reach quality requirement, ask it (in [issue](https://framagit.org/g1/g1lib.js/-/issues) or merge-request).
If you need help to reach quality requirement, ask it (in [issue](https://git.duniter.org/libs/g1lib.js/-/issues) or merge-request).
## Funding
......
{
"name": "g1lib",
"version": "3.1.0",
"version": "3.5.8",
"description": "An ubiquitous static javascript toolbox lib for Ǧ1 / Duniter ecosystem with reliability in mind.",
"main": "nodejs/all.mjs",
"browser": "browser/all.mjs",
......
This diff is collapsed.
......@@ -8,16 +8,17 @@
"build:mk": "node CI/mk-generated.mjs",
"build:vendors": "node CI/vendors.js",
"build:cpSrcToContextSpecific": "node CI/cp-src-to-context-specific.js",
"build:npm:rollup": "rollup --config CI/rollup.config.js",
"build:npm:rollup": "rollup --config CI/rollup.config.mjs",
"build:npm:cp": "cp npm/* generated/npm/",
"build:npm:cp:readme": "cp README* generated/npm/",
"build:npm:min:terser": "node CI/minify.mjs",
"build:npm:min": "node CI/minify.mjs",
"build:npm:zeroDep": "node CI/zeroDependency.mjs",
"test": "run-s test:dev",
"test:dev": "run-s test:dev:**",
"test:dev": "run-p test:dev:**",
"xtest:dev:qualityCheck": "xo",
"test:dev:duplication": "jscpd ./ -s",
"test:dev:runTests": "ava src/**.test.mjs",
"xtest:dev:complexity": "codehawk ./",
"test:dev:complexity": "codehawk ./",
"test-e2e": "ava generated/tmpNodejs/**.test-e2e.mjs",
"test:production": "run-s test:production:**",
"xtest:production:qualityCheck": "xo",
......@@ -29,36 +30,44 @@
"test:production:npm:nodejs:test": "cd generated/npm/nodejs/ && ava **.test.mjs",
"test:production:npm:nodejs:test-e2e": "cd generated/npm/nodejs/ && ava **.test-e2e.mjs",
"test:production:npm:nodejs:clean": "rm -rf generated/npm/nodejs/*.test*.mjs",
"test:browser": "run-s test:browser:**",
"test:browser:cp": "cp generated/tmpBrowser/*.test*.mjs generated/npm/browser/",
"test:browser:test": "cd generated/npm/browser/ && PORT=8478 browser-ava **.test.mjs --chromium --firefox",
"test:browser:test-e2e": "cd generated/npm/browser/ && browser-ava **.test-e2e.mjs --chromium --firefox",
"test:browser:clean": "rm -rf generated/npm/browser/*.test*.mjs",
"watch": "chokidar src/* -c \"npm run test:dev:runTests\"",
"watch2null": "chokidar src/* -c \"npm run test:dev:runTests 2>/dev/null\""
},
"dependencies": {
"cross-fetch": "^3.1.4",
"js-sha256": "https://github.com/1000i100/js-sha256#master",
"@noble/ed25519": "^1.7.1",
"cross-fetch": "^3.1.5",
"ed2curve": "https://github.com/1000i100/ed2curve#master",
"js-sha256": "github:1000i100/js-sha256#master",
"latinize-to-ascii": "^0.5.2",
"node-fetch": "^2.6.1",
"node-fetch": "^2.6.7",
"scrypt-async-modern": "^3.0.12",
"tweetnacl": "^1.0.3"
},
"devDependencies": {
"@jscpd/badge-reporter": "^3.3.23",
"ava": "^3.15.0",
"@jscpd/badge-reporter": "^3.4.5",
"ava": "^5.1.0",
"badgen": "^3.2.2",
"c8": "^7.6.0",
"chokidar-cli": "^2.1.0",
"browser-ava": "^1.3.13",
"c8": "^7.12.0",
"chokidar-cli": "^3.0.0",
"codehawk-cli": "^10.0.1",
"es6-plato": "https://github.com/1000i100/es6-plato#master",
"eslint-plugin-ava": "^12.0.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-unicorn": "^33.0.1",
"jscpd": "^3.3.25",
"esbuild": "^0.16.12",
"eslint-plugin-ava": "^13.2.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-unicorn": "^45.0.2",
"jscpd": "^3.5.3",
"mkdirp": "^1.0.4",
"npm-run-all": "^4.1.5",
"rollup": "^2.42.4",
"terser": "^5.7.0",
"xo": "^0.40.3"
"rollup": "^3.9.0",
"xo": "^0.44.0"
},
"disabledDependenciesTODOAddComplexityQualityCheck": {
"noble-ed25519": "https://github.com/1000i100/noble-ed25519#master",
"ecma-nacl": "^2.5.0",
"codehawk-cli": "^8.3.0"
},
......
......@@ -9,20 +9,22 @@ export default basex;
const _b64 = basex(B64_ALPHABET);
export const b64 = {
encode: source => {
const size = Math.ceil(source.length / 3) * 3;
const sizedArray = new Uint8Array(size);
if (typeof source === 'string') sizedArray.set((new TextEncoder()).encode(source));
else sizedArray.set(source);
const bSource = (typeof source === 'string') ? (new TextEncoder()).encode(source) : new Uint8Array(source);
const paddedSize = Math.ceil(bSource.length / 3) * 3;
const sizedArray = new Uint8Array(paddedSize);
sizedArray.set(bSource);
const b64str = _b64.encode(sizedArray).split('');
for (let i = 0; i < size - source.length; i++) b64str[b64str.length - 1 - i] = '=';
const b64PaddedSize = paddedSize*4/3;
while (b64str.length < b64PaddedSize) b64str.unshift(B64_ALPHABET[0]);
for (let i = 0; i < paddedSize - bSource.length; i++) b64str[b64str.length - 1 - i] = '=';
return b64str.join('');
},
decode: b64str => {
const rawArray = _b64.decode(b64str.replace(/=/g, 'A'));
const targetSize = Math.trunc(3 * b64str.length / 4 - (b64str.length - b64str.replace(/=/g, '').length));
return rawArray.slice(0, targetSize);
const targetSize = Math.ceil(3 * b64str.length / 4);
const postCut = (b64str.match(/=/g)||[]).length;
const preCut = rawArray.length - targetSize;
return rawArray.slice(preCut, rawArray.length-postCut);
}
};
......
......@@ -22,3 +22,14 @@ test('b64 should decode TWE= as Ma', t => t.is((new TextDecoder()).decode(app.b6
// Won't fix test('b64 should decode TWE as Ma', t => t.is((new TextDecoder()).decode(app.b64.decode('TWE')), 'Ma'));
test('b64 should decode TQ== as M', t => t.is((new TextDecoder()).decode(app.b64.decode('TQ==')), 'M'));
// Won't fix test('b64 should decode TQ as M', t => t.is((new TextDecoder()).decode(app.b64.decode('TQ')), 'M'));
test('[0,5,5] should be encoded as AAUF', t => t.is(app.b64.encode([0,5,5]), 'AAUF'));
test('AAUF should be decoded as [0,5,5]', t => t.deepEqual(app.b64.decode('AAUF'), new Uint8Array([0,5,5])));
test('[0,1] should be encoded as AAE=', t => t.is(app.b64.encode([0,1]), 'AAE='));
test('AAE= should be decoded as [0,1]', t => t.deepEqual(app.b64.decode('AAE='), new Uint8Array([0,1])));
//const unstableExample = 'AUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBVCmFAmx3dAyXpsWtwDnGel3LAcACxvXeG6QfGU9IEld9WwlvkDttnDTPgQL6TQkiMl0SCo=';
const unstableExample = 'AAUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBVCmFAmx3dAyXpsWtwDnGel3LAcACxvXeG6QfGU9IEld9WwlvkDttnDTPgQL6TQkiMl0SCo=';
test('b64 should be stable in decoding encoding process even on long string', t => t.is(app.b64.encode(app.b64.decode(unstableExample)), unstableExample));
export const fetch = async () => 0;
export const random = async () => 0;
export const sha512 = async () => 0;
import {fetch,random, ed25519} from './only-nodejs.mjs';
export {fetch,random, ed25519};
export async function fetch(...args) {
return window.fetch(...args); // eslint-disable-line no-undef
import * as ed25519 from '../../generated/vendors/@noble-ed25519-browser.mjs';
const fetch = window.fetch;
async function random(u8a,bytes){
if(!u8a && !bytes) return Math.random();
if(!bytes && typeof u8a === "number"){
bytes = u8a;
u8a = new Uint8Array(bytes);
}
const QUOTA = 65536;
const n = bytes;
const v = new Uint8Array(bytes);
for (let i = 0; i < n; i += QUOTA) crypto.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA)));
for (let i = 0; i < n; i++) u8a[i] = v[i];
return u8a;
}
export {fetch, random, ed25519}
import fetch from '../../node_modules/node-fetch/lib/index.mjs';
export {fetch};
import * as ed25519 from '../../generated/vendors/@noble-ed25519-node.mjs';
//import fetch from '../../node_modules/node-fetch/src/index.js';
//import fetch from 'node-fetch';
//import crypto from "crypto";
export {fetch,random, ed25519};
function random(u8a,bytes){
if(!u8a && !bytes) return Math.random();
if(!bytes && typeof u8a === "number"){
bytes = u8a;
u8a = new Uint8Array(bytes); // Buffer pour les vieilles versions de node
}
const n = bytes;
const v = crypto.randomBytes(n);
for (let i = 0; i < n; i++) u8a[i] = v[i];
return u8a;
}
// Alt deps : import {generate_keypair} from "ecma-nacl/build/lib/signing/sign.js";
// Alt deps : import scrypt from "ecma-nacl/build/lib/scrypt/scrypt.js";
import scrypt from '../generated/vendors/scrypt.mjs';
import nacl from '../generated/vendors/nacl.mjs';
import sha from '../node_modules/js-sha256/src/sha256.mjs';
// Alt import * as ed25519 from '../node_modules/noble-ed25519/index.mjs';
import {b58, b64} from './basex.mjs';
import sha256 from '../node_modules/js-sha256/src/sha256.mjs';
import nacl from '../generated/vendors/tweetnacl+ed2curve.mjs';
import {convertPublicKey, convertSecretKey} from '../generated/vendors/tweetnacl+ed2curve.mjs';
import {b16, b58, b64} from './basex.mjs';
import {random, ed25519} from './context-dependant/generics.mjs';
nacl.setPRNG(random);
export const mockRandom = nacl.setPRNG;
export {b58, b64};
export {b58, b64, sha256};
const sha256 = sha();
const sha256Instance = sha256();
const generateKeypair = nacl.sign.keyPair.fromSeed;
export const isPubkey = (value) => /^[A-HJ-NP-Za-km-z1-9]{42,45}$/.test(value)
export async function idSecPass2rawAll(idSec, pass) {
const rawSeed = await saltPass2seed(idSec, pass);
const keyPair = await seed2keyPair(rawSeed);
const keyPair = seed2keyPair(rawSeed);
keyPair.seed = rawSeed;
keyPair.pubKey = keyPair.publicKey;
return keyPair;
......@@ -69,9 +70,9 @@ export function pubKey2shortKey(pubKey) {
}
export function pubKey2checksum(b58pubKey, b58viewDependant = false, checksumWithLeadingZero = false, doubleSha256 = true) {
const binPubKey = b58viewDependant ? b58.decode(b58pubKey) : b58pubKey2bin(b58pubKey);
let hash = sha256.digest(binPubKey);
if (doubleSha256) hash = sha256.digest(hash);
const binPubKey = b58viewDependant ? b58.decode(b58pubKey) : pubKey2bin(b58pubKey);
let hash = sha256Instance.digest(binPubKey);
if (doubleSha256) hash = sha256Instance.digest(hash);
if (!checksumWithLeadingZero) {
const shorterHash = sliceInitialsZero(hash);
return b58.encode(shorterHash).substr(0, 3);
......@@ -85,7 +86,15 @@ export function sliceInitialsZero(array) {
while (array[zero] === 0) zero++;
return array.slice(zero);
}
export function pubKey2bin(b58orBinPubKey){
if(typeof b58orBinPubKey === 'string') {
if(b58orBinPubKey.includes(':')) {
return b58pubKey2bin(onlyPubKey(b58orBinPubKey));
}
return b58pubKey2bin(b58orBinPubKey);
}
return b58orBinPubKey;
}
export function b58pubKey2bin(b58pubKey) {
const binPubKey = new Uint8Array(32);
const decoded = b58.decode(b58pubKey);
......@@ -99,30 +108,76 @@ export function b58secretKey2bin(b58secretKey) {
binSecretKey.set(decoded, 64 - decoded.length);
return binSecretKey;
}
export function checkKey(pubKeyWithChecksum) {
export function onlyPubKey(pubKeyWithChecksum){
const part = pubKeyWithChecksum.split(':');
const b58pubKey = part[0];
const checkSum = part[1];
if (pubKey2checksum(b58pubKey) === checkSum) return true;
if (pubKey2checksum(b58pubKey, true) === checkSum) return true;
if (pubKey2checksum(b58pubKey, false, true) === checkSum) return true;
if (pubKey2checksum(b58pubKey, true, true) === checkSum) return true;
if (pubKey2checksum(b58pubKey, false, false, false) === checkSum) return true;
if (pubKey2checksum(b58pubKey, true, false, false) === checkSum) return true;
if (pubKey2checksum(b58pubKey, false, true, false) === checkSum) return true;
if (pubKey2checksum(b58pubKey, true, true, false) === checkSum) return true;
throw new Error('Bad checksum');
if (pubKey2checksum(b58pubKey) === checkSum) return b58pubKey;
if (pubKey2checksum(b58pubKey, true) === checkSum) return b58pubKey;
if (pubKey2checksum(b58pubKey, false, true) === checkSum) return b58pubKey;
if (pubKey2checksum(b58pubKey, true, true) === checkSum) return b58pubKey;
if (pubKey2checksum(b58pubKey, false, false, false) === checkSum) return b58pubKey;
if (pubKey2checksum(b58pubKey, true, false, false) === checkSum) return b58pubKey;
if (pubKey2checksum(b58pubKey, false, true, false) === checkSum) return b58pubKey;
if (pubKey2checksum(b58pubKey, true, true, false) === checkSum) return b58pubKey;
throw new CustomError('bad_checksum','Bad checksum');
}
export function isDuniterPubKey(b58pubKey){
return /^[A-HJ-NP-Za-km-z1-9]{43,44}$/.test(b58pubKey) && b58.decode(b58pubKey).length <=32;
}
export function checkDuniterPubKey(b58pubKey){
if(b58pubKey.length<43) throw new CustomError('too_short','Too short, see rfc/0009_Duniter_Blockchain_Protocol_V11.md#public-key for details.');
if(b58pubKey.length>44) throw new CustomError('too_long','Base58 string too long, see rfc/0009_Duniter_Blockchain_Protocol_V11.md#public-key for details.');
if(!/^[A-HJ-NP-Za-km-z1-9]+$/.test(b58pubKey)) throw new CustomError('not_b58', 'Character out of base 58, see rfc/0009_Duniter_Blockchain_Protocol_V11.md#public-key for details.');
if(b58.decode(b58pubKey).length > 32) throw new CustomError('too_long','binary key too long, see rfc/0009_Duniter_Blockchain_Protocol_V11.md#public-key for details.');
return true;
}
export function checkEd25519PubKey(b58pubKey){
const binPubKey = pubKey2bin(b58pubKey);
try{
ed25519.Point.fromHex(binPubKey);
} catch (err){
throw new CustomError('bad_ed25519_point',`Invalid public key : not a valid ed25519 point RFC8032 5.1.3 https://www.rfc-editor.org/rfc/rfc8032#page-11 Internal:${err}`);
}
return true;
}
export function isEd25519PubKey(b58pubKey){
try{
return checkEd25519PubKey(b58pubKey);
} catch (e){return false;}
}
export function checkKey(pubKey, checkRawPubKey= true) {
if(!pubKey) throw new CustomError('empty','Invalid public key : empty input.')
let b58pubKey;
try {
const binPubKey = pubKey2bin(pubKey)
b58pubKey = typeof pubKey === 'string' ? pubKey : b58.encode(binPubKey);
} catch (err){
if(err.message.match(/base58/)) throw new CustomError('not_b58', 'Character out of base 58, see rfc/0009_Duniter_Blockchain_Protocol_V11.md#public-key for details.');
if(err.name === "RangeError") throw new CustomError('too_long','Binary key too long, see rfc/0009_Duniter_Blockchain_Protocol_V11.md#public-key for details.');
throw err;
}
if(!checkRawPubKey) return true;
checkDuniterPubKey(b58pubKey);
checkEd25519PubKey(b58pubKey);
return true;
}
export function isPubKey(pubKey){
try{
return checkKey(pubKey)
} catch (e){return false;}
}
export async function signDocument(unsignedDocument, secretKey) {
const signHash = await sign(unsignedDocument.trim() + '\n', secretKey);
return `${unsignedDocument.trim()}\n${signHash}`;
}
export async function sign(str, secretKey, outputFormat = 'b64') {
export function sign(str, secretKey, outputFormat = 'b64') {
const encoder = new TextEncoder();
const raw = await rawSign(encoder.encode(str), b58secretKey2bin(secretKey).slice(0, 32));
const raw = rawSign(encoder.encode(str), b58secretKey2bin(secretKey).slice(0, 32));
switch (outputFormat.toLocaleLowerCase()) {
case 'raw':
case 'array':
......@@ -137,7 +192,61 @@ export async function sign(str, secretKey, outputFormat = 'b64') {
}
}
export async function rawSign(uint8Array, rawSeed) {
const keys = await seed2keyPair(rawSeed);
export function rawSign(uint8Array, rawSeed) {
const keys = seed2keyPair(rawSeed);
return nacl.sign.detached(uint8Array, keys.secretKey);
}
const NONCE_BYTES = 24;
const PUBKEY_BYTES = 32;
export function typedStrOrBin2Bin(tStrOrBin,type2binFunc){
return (typeof tStrOrBin === 'string')?type2binFunc(tStrOrBin):tStrOrBin;
}
export function strOrBin2bin(strOrBin){
const encoder = new TextEncoder();
return (typeof strOrBin === 'string')?encoder.encode(strOrBin):strOrBin;
}
export function b58orBin2bin(b58orBin){
return typedStrOrBin2Bin(b58orBin,b58.decode);
}
export function bin2str(bin){
const decoder = new TextDecoder();
return decoder.decode(bin);
}
function secKey2bin(secretKey){
return b58orBin2bin(secretKey).slice(0,PUBKEY_BYTES);
}
export function textEncrypt(jsonMessage, senderPrivateKey, receiverPubKey, nowInSecond=0, b58nonce=''){
const bNonce = b58nonce?b58.decode(b58nonce):nacl.randomBytes(NONCE_BYTES);
const bReceiverPubKey = convertPublicKey(pubKey2bin(receiverPubKey));
const bSenderPrivateKey = convertSecretKey(secKey2bin(senderPrivateKey));
const encrypt = utf8text => b64.encode(nacl.box(strOrBin2bin(utf8text),bNonce,bReceiverPubKey,bSenderPrivateKey));
const b58sendPubKey = b58.encode(seed2keyPair(secKey2bin(senderPrivateKey)).publicKey);
const sTimestamp = nowInSecond || Math.trunc(Date.now()/1000);
const res = {nonce:b58.encode(bNonce),issuer:b58sendPubKey,recipient:receiverPubKey,time:sTimestamp,version:2};
if(jsonMessage.title) res.title = encrypt(jsonMessage.title);
if(jsonMessage.content) res.content = encrypt(jsonMessage.content);
res.hash = b16.encode(sha256Instance.digest(JSON.stringify(res))).toUpperCase();
res.signature = sign(res.hash,senderPrivateKey)
return res;
}
export function textDecrypt(jsonMessage, receiverPrivateKey){
const bNonce = b58.decode(jsonMessage.nonce);
const bReceiverPrivateKey = convertSecretKey(secKey2bin(receiverPrivateKey));
const bSenderPubKey = convertPublicKey(pubKey2bin(jsonMessage.issuer));
const decrypt = b64text => bin2str(nacl.box.open(b64.decode(b64text),bNonce,bSenderPubKey,bReceiverPrivateKey));
const res = {};
if(jsonMessage.content) res.content = decrypt(jsonMessage.content);
if(jsonMessage.title) res.title = decrypt(jsonMessage.title);
return res;
}
class CustomError extends Error {
constructor(name, message) {
super(message);
this.name = name;
}
}
......@@ -28,9 +28,16 @@ test('b58 sign string', async t => t.is(await app.sign(unsignedDocument, secretK
test('raw sign string', async t => t.is((await app.sign(unsignedDocument, secretKey, 'raw'))[0], 27));
test('array sign string', async t => t.is((await app.sign(unsignedDocument, secretKey, 'Array'))[0], 27));
test('uint8array sign string', async t => t.is((await app.sign(unsignedDocument, secretKey, 'uint8array'))[0], 27));
test('sign throw for bad output format', async t => t.throwsAsync(() => app.sign(unsignedDocument, secretKey, 'whattt ?')));
test('sign throw for bad output format', t => t.throws(() => app.sign(unsignedDocument, secretKey, 'whattt ?')));
//test('signOnly(message, privateKey)', t => t.is(app.signOnly('a message', secretKey), 'signature'));
//test('signOnly(message, privateKey, returnFormat=uint8array)', t => t.is(app.signOnly([0,1,2,3], secretKey), [0,1,2,3]));
//test('signDocument or signMessage(message, privateKey)', t => t.is(app.sign('a message', secretKey), 'message avec sa signature'));
//test('sign(uint8array, privateKey, returnFormat=uint8array)', async t => t.is(await app.sign([0,1,2,3], secretKey,'uint8array'), [0,0,0,0]));
//test('checkSign(message, issuerPubKey) succeed', t => t.is(app.checkSign('a message', secretKey), true));
//test('checkSign(message, issuerPubKey) fail', t => t.is(app.checkSign([0,1,2,3], secretKey,'uint8array'), false));
test("is a pubkey", (t) => t.is(app.isPubkey(pubKey), true));
test('b58 should decode/encode well', t => t.is(app.b58.encode(app.b58.decode(pubKey)), pubKey));
test('b58 on pubKey with leading 1', t => t.is(app.b58.encode(app.b58.decode('12BjyvjoAf5qik7R8TKDJAHJugsX23YgJGi2LmBUv2nx')), '12BjyvjoAf5qik7R8TKDJAHJugsX23YgJGi2LmBUv2nx'));
test('b58 on pubKey without leading 1', t => t.is(app.b58.encode(app.b58.decode('2BjyvjoAf5qik7R8TKDJAHJugsX23YgJGi2LmBUv2nx')), '2BjyvjoAf5qik7R8TKDJAHJugsX23YgJGi2LmBUv2nx'));
......@@ -43,6 +50,7 @@ test('seed2keyPair should generate public and private key nacl/sodium way.', asy
const rawKeyPair = await app.seed2keyPair(rawSeed);
t.is(app.b58.encode(rawKeyPair.publicKey), pubKey);
t.is(app.b58.encode(rawKeyPair.secretKey), secretKey);
t.deepEqual(app.b58.decode(secretKey), rawKeyPair.secretKey);
});
test('idSecPass2cleanKeys should output clean base58 keys and seed', async t => {
const r = await app.idSecPass2cleanKeys(idSec, mdp);
......@@ -75,35 +83,94 @@ test('pubKey2checksum ascii 1pubKey542 : DSs', t => t.is(app.pubKey2checksum('1p
test('pubKey2checksum ascii pubKey542 : DEE', t => t.is(app.pubKey2checksum('pubKey542', true), 'DEE'));
test('pubKey2checksum checksumWithLeadingZero 1pubKey542 : 1ML', t => t.is(app.pubKey2checksum('pubKey542', false, true), '1ML'));
test('checkKey pubKey542:1ML', t => t.true(app.checkKey('pubKey542:1ML')));
test('checkKey pubKey542:MLT', t => t.true(app.checkKey('pubKey542:MLT')));
test('checkKey pubKey542:DEE', t => t.true(app.checkKey('pubKey542:DEE')));
test('checkKey 11111111111111111111111pubKey49311:4Ru', t => t.true(app.checkKey('11111111111111111111111pubKey49311:4Ru')));
test('checkKey 11111111111111111111111pubKey49311:14R', t => t.true(app.checkKey('11111111111111111111111pubKey49311:14R')));
test('checkKey 111pubKey49311:14R', t => t.true(app.checkKey('111pubKey49311:14R')));
test('checkKey 11pubKey49311:14R', t => t.true(app.checkKey('11pubKey49311:14R')));
test('checkKey 1pubKey49311:14R', t => t.true(app.checkKey('1pubKey49311:14R')));
test('checkKey pubKey49311:14R', t => t.true(app.checkKey('pubKey49311:14R')));
test('checkKey pubKey49311:4Ru', t => t.true(app.checkKey('pubKey49311:4Ru')));
test('checkKey pubKey49311:12p', t => t.true(app.checkKey('pubKey49311:12p')));
test('checkKey pubKey49311:2p7', t => t.true(app.checkKey('pubKey49311:2p7')));
test('checkKey false 11111111111111111111111pubKey49311:12p', t => t.throws(() => app.checkKey('11111111111111111111111pubKey49311:12p')));
test('checkKey false 11111111111111111111111pubKey49311:2p7', t => t.throws(() => app.checkKey('11111111111111111111111pubKey49311:2p7')));
test('checkKey false pubKey49311:111', t => t.throws(() => app.checkKey('pubKey49311:111')));
test('checkKey false 0pubKey49311:any', t => t.throws(() => app.checkKey('0pubKey49311:any')));
test('checkKey pubKey542:1ML', t => t.true(app.checkKey('pubKey542:1ML', false)));
test('checkKey pubKey542:MLT', t => t.true(app.checkKey('pubKey542:MLT', false)));
test('checkKey pubKey542:DEE', t => t.true(app.checkKey('pubKey542:DEE', false)));
test('checkKey 11111111111111111111111pubKey49311:4Ru', t => t.true(app.checkKey('11111111111111111111111pubKey49311:4Ru', false)));
test('checkKey 11111111111111111111111pubKey49311:14R', t => t.true(app.checkKey('11111111111111111111111pubKey49311:14R', false)));
test('checkKey 111pubKey49311:14R', t => t.true(app.checkKey('111pubKey49311:14R', false)));
test('checkKey 11pubKey49311:14R', t => t.true(app.checkKey('11pubKey49311:14R', false)));
test('checkKey 1pubKey49311:14R', t => t.true(app.checkKey('1pubKey49311:14R', false)));
test('checkKey pubKey49311:14R', t => t.true(app.checkKey('pubKey49311:14R', false)));
test('checkKey pubKey49311:4Ru', t => t.true(app.checkKey('pubKey49311:4Ru', false)));
test('checkKey pubKey49311:12p', t => t.true(app.checkKey('pubKey49311:12p', false)));
test('checkKey pubKey49311:2p7', t => t.true(app.checkKey('pubKey49311:2p7', false)));
test('checkKey false 11111111111111111111111pubKey49311:12p', t => t.throws(() => app.checkKey('11111111111111111111111pubKey49311:12p', false)));
test('checkKey false 11111111111111111111111pubKey49311:2p7', t => t.throws(() => app.checkKey('11111111111111111111111pubKey49311:2p7', false)));
test('checkKey false pubKey49311:111', t => t.throws(() => app.checkKey('pubKey49311:111', false)));
test('checkKey false 0pubKey49311:any', t => t.throws(() => app.checkKey('0pubKey49311:any', false)));
test('pubKey2checksum 11111111111111111111111pubKey49311 : 4Ru', t => t.is(app.pubKey2checksum('11111111111111111111111pubKey49311'), '4Ru'));
test('pubKey2checksum pubKey49311 : 4Ru', t => t.is(app.pubKey2checksum('pubKey49311'), '4Ru'));
test('pubKey2checksum de Nd5...21o:3Q3', t => t.is(app.pubKey2checksum('Nd5kTAZmFDuKoi1mAZkZERenV6efyYyyLoHMTe721o'), '3Q3'));
test('pubKey2checksum simpleSha de Nd5...21o:3Q3', t => t.is(app.pubKey2checksum('Nd5kTAZmFDuKoi1mAZkZERenV6efyYyyLoHMTe721o', false, false, false), 'FCd'));
test('checkKey accept simpleSha pub664777:4fv', t => t.true(app.checkKey('pub664777:4fv')));
test('checkKey accept simpleSha pub664777:14f', t => t.true(app.checkKey('pub664777:14f')));
test('checkKey accept simpleSha pub664777:2u3', t => t.true(app.checkKey('pub664777:2u3')));
test('checkKey accept simpleSha pub664777:12u', t => t.true(app.checkKey('pub664777:12u')));
test('checkKey accept simpleSha pub664777:4fv', t => t.true(app.checkKey('pub664777:4fv', false)));
test('checkKey accept simpleSha pub664777:14f', t => t.true(app.checkKey('pub664777:14f', false)));
test('checkKey accept simpleSha pub664777:2u3', t => t.true(app.checkKey('pub664777:2u3', false)));
test('checkKey accept simpleSha pub664777:12u', t => t.true(app.checkKey('pub664777:12u', false)));
test('pubKey2checksum simpleSha viewDependant Nd5...21o', t => t.is(app.pubKey2checksum('Nd5kTAZmFDuKoi1mAZkZERenV6efyYyyLoHMTe721o', true, false, false), '7G5'));
test('pubKey2checksum simpleSha checksumWithLeadingZero Nd5...21o', t => t.is(app.pubKey2checksum('Nd5kTAZmFDuKoi1mAZkZERenV6efyYyyLoHMTe721o', false, true, false), 'FCd'));
test('pubKey2checksum simpleSha viewDependant checksumWithLeadingZero Nd5...21o', t => t.is(app.pubKey2checksum('Nd5kTAZmFDuKoi1mAZkZERenV6efyYyyLoHMTe721o', true, true, false), '7G5'));
test("isDuniterPubKey verify key size follow duniter specification https://git.duniter.org/nodes/common/doc/blob/master/rfc/0009_Duniter_Blockchain_Protocol_V11.md#public-key",
(t) => t.true(app.isDuniterPubKey(pubKey)));
test("isDuniterPubKey fail if the key is not in base 58", (t) => t.false(app.isDuniterPubKey(pubKey.replace(/6/,'0'))));
test("isDuniterPubKey fail if the key is to short for duniter specifications", (t) => t.false(app.isDuniterPubKey(pubKey.replace(/F/g,''))));
test("isDuniterPubKey fail if the key is to long for duniter specifications", (t) => t.false(app.isDuniterPubKey(pubKey.replace(/6/,'99'))));
test("isDuniterPubKey fail if the binary key decoded from base 58 is longer than 32bytes", (t) => t.false(app.isDuniterPubKey(Array(44).fill('z').join('') )));
test("isEd25519PubKey verify key validity with ed25519 point decode verification", (t) => t.true(app.isEd25519PubKey(pubKey)));
test("isEd25519PubKey fail if point is not on ed25519", (t) => t.false(app.isEd25519PubKey(pubKey.replace(/6/,'9'))));
test('checkKey accept valid pubKey with no checksum', t => t.true(app.checkKey(pubKey)));
test('checkKey throw if empty pubkey is given', t => t.throws(() => app.checkKey(''),{name:'empty'}));
test('checkKey throw if under_sized string is given', t => t.throws(() => app.checkKey('test'),{name:'too_short'}));
test('checkKey throw too_short if 41 characters long string is given', t => t.throws(() => app.checkKey('fffffffffffffffffffffffffffffffffffffffff'),{name:'too_short'}));
test('checkKey throw too_short if 42 characters long string is given', t => t.throws(() => app.checkKey('ffffffffffffffffffffffffffffffffffffffffff'),{name:'too_short'}));
test('checkKey throw too_long if given string is 44 characters but more than 32bits', t => t.throws(() => app.checkKey('ffffffffffffffffffffffffffffffffffffffffffff'),{name:'too_long'}));
test('checkKey throw if over_sized string is given', t => t.throws(() => app.checkKey(pubKey+pubKey),{name:'too_long'}));
test('checkKey throw too_long if 45 characters long string is given', t => t.throws(() => app.checkKey('fffffffffffffffffffffffffffffffffffffffffffff'),{name:'too_long'}));
test('checkKey throw if invalid pubkey is given (not on ed25519 curve)', t => t.throws(() => app.checkKey(pubKey.replace(/6/,'9')),{name:'bad_ed25519_point'}));
test('checkKey throw if checksum is incorrect', t => t.throws(() => app.checkKey(`${pubKey}:111`),{name:'bad_checksum'}));
test('checkKey throw if not b58 string is given', t => t.throws(() => app.checkKey(`___`),{name:'not_b58'}));
test('checkKey accept valid binary pubKey', t => t.true(app.checkKey(app.b58.decode(pubKey))));
test("isPubKey return true when checkKey return true ", (t) => t.true(app.isPubKey(pubKey)));
test("isPubKey return false when checkKey throw an error", (t) => t.false(app.isPubKey(pubKey.replace(/6/,'9'))));
app.mockRandom((u8a, n)=>{
if(!u8a && !n) return 0.5;
if(!n && typeof u8a === "number"){n = u8a;u8a = new Uint8Array(n);}
for (let i = 0; i < n; i++) u8a[i] = 5;
return u8a;
});
const user1 = await app.idSecPass2cleanKeys('1','1');
const user2 = await app.idSecPass2cleanKeys('2','2');
const timestampInSeconds = 1222111000;
const sampleMessage = {title:"Mon Titre",content:"Mon message"};
const cryptedMessage = {
content: 'G0ZAirsaoeAt/pOcsjg0milDkeBu8Uo3BgrZ',
hash: '65EC1622D2C38422EA21124397AB7A9F36A94BB6FC2C474C706ECAC33658114C',
issuer: 'BUhLyJT17bzDVXW66xxfk1F7947vytmwJVadTaWb8sJS',
nonce: 'TYPjCiGbKiwP6r12cdkmVjySbQpSHavp',
recipient: '7nge6q7F4k7FQ2q4FRMMPvt2tK7AEx8gNNRLr6LwZN38',
signature: 'u4m2YHp5nVxcXxMjQvv9H4kfSV8Q3/Jh68jHplbCjizXlA9XXksu+YuFrS9B5KuQ2HX68g9+kRdLoCspMuWtBg==',
time: timestampInSeconds,
title: 'yV5cDZCuJWqMBXsFmEm1FSlDkeBX/U02Ag==',
version: 2
};
test('textEncrypt(jsonMessage, senderPrivateKey, receiverPubKey) encrypt to cesium+ message format',
t => t.deepEqual(
app.textEncrypt(sampleMessage, user1.secretKey, user2.publicKey,timestampInSeconds),
cryptedMessage)
);
test('textDecrypt(jsonMessage, receiverPrivateKey)', t => t.deepEqual(app.textDecrypt(cryptedMessage, user2.secretKey),
{title:"Mon Titre",content:"Mon message"}));
import latinize from '../node_modules/latinize-to-ascii/latinize.mjs';
export {dedup, noAccentVariant, casesVariants, regLikeVariants, resetCache};
function dedup(array) {
const result = {};
array.forEach(v => result[v] = v); // eslint-disable-line no-return-assign
return Object.keys(result);
}
function noAccentVariant(string) {
return dedup([string, latinize(string)]);
}
function casesVariants(string) {
return dedup([
string,
string.toLowerCase(),
string.split(' ').map(str => str.charAt(0).toUpperCase() + str.slice(1).toLowerCase()).join(' '),
string.toUpperCase()
]);
}
function regLikeVariants(theString, allStrings = []) {
return _regLikeVariants(escape2utfSpecial(theString), allStrings.map(escape2utfSpecial)).map(utfSpecial2unEscaped);
}
function swapKeyValue(object) {
const result = {};
for (const key in object) {
result[object[key]] = key;
}
return result;
}
const specialMap = {
'(': String.fromCharCode(0x01),
')': String.fromCharCode(0x02),
'|': String.fromCharCode(0x03),
'{': String.fromCharCode(0x04),
'}': String.fromCharCode(0x05),
',': String.fromCharCode(0x06),
'[': String.fromCharCode(0x07),
']': String.fromCharCode(0x08),
'-': String.fromCharCode(0x09),
'<': String.fromCharCode(0x0a),
'>': String.fromCharCode(0x0b),
':': String.fromCharCode(0x0c),
'=': String.fromCharCode(0x0d)
};
const revertSpecial = swapKeyValue(specialMap);
function escape2utfSpecial(str) {
return str.replace(/\\(.)/g, (a, chr) => specialMap[chr] ? specialMap[chr] : chr);
}
function utfSpecial2unEscaped(str) {
return str.split('').map(chr => revertSpecial[chr] ? revertSpecial[chr] : chr).join('');
}
function bracketsHandler(theString) {
// Handle []
const lower = 'abcdefghijklmnopqrstuvwxyz';
const upper = lower.toUpperCase();
const number = '0123456789';
theString = theString.replace(/(\[[^\]]*)([a-z]-[a-z])([^\]]*])/g, (osef, before, chrs, after) => before + lower.slice(lower.indexOf(chrs.split('-')[0]), lower.indexOf(chrs.split('-')[1]) + 1) + after);
theString = theString.replace(/(\[[^\]]*)([A-Z]-[A-Z])([^\]]*])/g, (osef, before, chrs, after) => before + upper.slice(upper.indexOf(chrs.split('-')[0]), upper.indexOf(chrs.split('-')[1]) + 1) + after);
theString = theString.replace(/(\[[^\]]*)([0-9]-[0-9])([^\]]*])/g, (osef, before, chrs, after) => before + number.slice(number.indexOf(chrs.split('-')[0]), number.indexOf(chrs.split('-')[1]) + 1) + after); // eslint-disable-line unicorn/better-regex
theString = theString.replace(/\[([^\]]+)]/g, (osef, chrs) => `(${chrs.split('').join('|')})`);
return theString;
}
function resetCache() {
cache();
}
function cache(key, func, ...args) {
/* Init */ if (!cache.cached) cache.cached = {};
/* Reset cache */ if (arguments.length === 0) return cache.cached = {}; // eslint-disable-line no-return-assign
/* Compute and cache */ if (typeof cache.cached[key] === 'undefined') cache.cached[key] = func(...args);
/* Answer from cache */ return cache.cached[key];
}
function labelExpressions(str) {
return str.slice(str.indexOf('::') + 2);
}
function computedLabel(label, allStrings) {
return cache(`label::${label}`, _computedLabel, label, allStrings);
}
function _computedLabel(label, allStrings) {
const matchingLabel = allStrings.filter(str => str.trim().indexOf(`${label}::`) === 0);
return flatten(matchingLabel.map(str => _regLikeVariants(labelExpressions(str)), allStrings));
}
function refHandler(theString, allStrings) {
// Handle <ref>
theString = theString.replace(/<([^>]+)>/g, (osef, ref) => `(${computedLabel(ref, allStrings).join('|')})`);
return theString;
}
function syncRefHandler(theString, allStrings) {
// Handle =ref>
const syncRef = dedup(theString.match(/=[^>]+>/g) || []).map(str => str.substring(1, str.length - 1));
let results = [theString];
for (const ref of syncRef) {
const variantes = computedLabel(ref, allStrings);
const tmpRes = [];
for (const v of variantes) {
for (const r of results) {
tmpRes.push(r.replace(new RegExp(`=${ref}>`, 'g'), v));
}
}
results = tmpRes;
}
return results;
}
function qtyHandlerReplaceCallback(all, chr, qty) {
const mm = qty.split(',').map(n => n.trim() * 1); // eslint-disable-line no-implicit-coercion
const min = mm[0];
const max = (mm.length === 2) ? mm[1] : min;
const result = [];
for (let i = min; i <= max; i++) result.push(new Array(i + 1).join(chr)); // eslint-disable-line unicorn/no-new-array
return `(${result.join('|')})`;
}
function qtyHandler(theString) {
// Handle {qty} and {min,max}
theString = theString.replace(/([^)]){([^}]+)}/g, qtyHandlerReplaceCallback);
theString = theString.replace(/^(.*)\(([^)]*)\){([^}]+)}(.*)$/, (all, before, choices, qty, after) => before + qtyHandlerReplaceCallback('', `(${choices})`, qty) + after); // eslint-disable-line max-params
return theString;
}
function optionsHandler(theString, allStrings) {
// Handle (|)
let multiString = theString.replace(/^(.*)\(([^)]*)\)(.*)$/, (all, before, choices, after) => choices.split('|').map(c => before + c + after).join('=$##$=')).split('=$##$=');
multiString = [].concat(...multiString.map(str => (str.includes('(')) ? _regLikeVariants(str, allStrings) : str));
return dedup(multiString);
}
function _regLikeVariants(theString, allStrings) {
if (theString.indexOf('::') > 0) return []; // Remove label definition lines.
theString = bracketsHandler(theString); // Handle []
theString = refHandler(theString, allStrings);// Handle <ref> and ref::
theString = qtyHandler(theString); // Handle {qty} and {min,max}
const strings = optionsHandler(theString, allStrings); // Handle (|)
return flatten(strings.map(string => syncRefHandler(string, allStrings)));// Handle =ref> and ref::
}
function flatten(arrayOfArray) {
return dedup([].concat(...arrayOfArray));
}