Skip to content
Snippets Groups Projects
Commit 40f8e875 authored by Millicent Billette's avatar Millicent Billette
Browse files

ADD: pubKey internal verification (follow Duniter RFC and ed25519

ADD: isPubKey checkKey and return true or false (instead of throwing error)
FIX: checkKey allow key without checksum (and perform internal check on them)
FIX: checkKey allow binary key as input

v3.2.0 is previous commit. This-one will be v3.3.0 if all work like espected.
parent 778dd00d
Branches
Tags
No related merge requests found
......@@ -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(
......
{
"name": "g1lib",
"version": "3.1.1",
"version": "3.2.0",
"description": "An ubiquitous static javascript toolbox lib for Ǧ1 / Duniter ecosystem with reliability in mind.",
"main": "nodejs/all.mjs",
"browser": "browser/all.mjs",
......
Source diff could not be displayed: it is too large. Options to address this: view the blob.
......@@ -33,32 +33,33 @@
"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",
"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",
"@noble/ed25519": "^1.7.1",
"node-fetch": "^3.2.10",
"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": "^4.3.3",
"badgen": "^3.2.2",
"c8": "^7.6.0",
"chokidar-cli": "^2.1.0",
"c8": "^7.12.0",
"chokidar-cli": "^3.0.0",
"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",
"eslint-plugin-ava": "^13.2.0",
"eslint-plugin-promise": "^6.0.1",
"eslint-plugin-unicorn": "^43.0.2",
"jscpd": "^3.4.5",
"mkdirp": "^1.0.4",
"npm-run-all": "^4.1.5",
"rollup": "^2.42.4",
"terser": "^5.7.0",
"xo": "^0.40.3"
"rollup": "^2.79.1",
"terser": "^5.15.0",
"xo": "^0.52.3"
},
"disabledDependenciesTODOAddComplexityQualityCheck": {
"noble-ed25519": "https://github.com/1000i100/noble-ed25519#master",
"ecma-nacl": "^2.5.0",
"codehawk-cli": "^8.3.0"
},
......
......@@ -2,6 +2,7 @@
// 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 * as ed25519 from '../node_modules/@noble/ed25519/lib/esm/index.js';
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';
......@@ -14,7 +15,7 @@ const generateKeypair = nacl.sign.keyPair.fromSeed;
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;
......@@ -67,7 +68,7 @@ export function pubKey2shortKey(pubKey) {
}
export function pubKey2checksum(b58pubKey, b58viewDependant = false, checksumWithLeadingZero = false, doubleSha256 = true) {
const binPubKey = b58viewDependant ? b58.decode(b58pubKey) : b58pubKey2bin(b58pubKey);
const binPubKey = b58viewDependant ? b58.decode(b58pubKey) : pubKey2bin(b58pubKey);
let hash = sha256.digest(binPubKey);
if (doubleSha256) hash = sha256.digest(hash);
if (!checksumWithLeadingZero) {
......@@ -83,7 +84,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);
......@@ -97,21 +106,45 @@ 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;
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 Error('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 isEd25519PubKey(b58pubKey){
try{
const binPubKey = pubKey2bin(b58pubKey);
ed25519.Point.fromHex(binPubKey);
} catch (e){return false;}
return true;
}
export function checkKey(pubKeyWithOrWithoutChecksum, checkRawPubKey= true) {
const binPubKey = pubKey2bin(pubKeyWithOrWithoutChecksum)
const b58pubKey = b58.encode(binPubKey);
if(!checkRawPubKey) return true;
if(!isDuniterPubKey(b58pubKey)) throw new Error("Invalid public key : this string don't follow rfc/0009_Duniter_Blockchain_Protocol_V11.md#public-key");
if(!isEd25519PubKey(b58pubKey)) throw new Error("Invalid public key : not a valid ed25519 point RFC8032 5.1.3 https://www.rfc-editor.org/rfc/rfc8032#page-11");
return true;
}
export function isPubKey(b58pubKey){
try{
return checkKey(b58pubKey)
} catch (e){return false;}
}
export async function signDocument(unsignedDocument, secretKey) {
const signHash = await sign(unsignedDocument.trim() + '\n', secretKey);
......
......@@ -42,6 +42,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);
......@@ -74,35 +75,55 @@ 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 invalid pubkey is given', t => t.throws(() => app.checkKey(pubKey.replace(/6/,'9'))));
test('checkKey throw if empty pubkey is given', t => t.throws(() => app.checkKey('')));
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'))));
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment