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

modular and test core func + huge perf increase

parent d66f15d2
No related branches found
No related tags found
No related merge requests found
......@@ -2,3 +2,4 @@
*generated*
node_modules/
.idea/
vendors/
build.js 0 → 100644
const fs = require("fs");
try{fs.rmdirSync("public/vendors");} catch (e) {console.error(e);}
try{fs.mkdirSync("public/vendors");} catch (e) {console.error(e);}
try{fs.copyFileSync("node_modules/tweetnacl/nacl-fast.js","public/vendors/nacl.js");} catch (e) {console.error(e);}
try{fs.copyFileSync("node_modules/scrypt-async-modern/src/index.js","public/vendors/scrypt.js");} catch (e) {console.error(e);}
try{fs.writeFileSync("public/vendors/nacl.js",(fs.readFileSync("public/vendors/nacl.js","utf8"))
.replace("(function(nacl) {","var nacl = {};")
.replace("})(typeof module !== 'undefined' && module.exports ? module.exports : (self.nacl = self.nacl || {}));","export {nacl};")
,"utf8");} catch (e) {console.error(e);}
// let js = fs.readFileSync("node_modules/jparticles/production/jparticles.js","utf8");
// js += fs.readFileSync("node_modules/jparticles/production/particle.js","utf8");
// js += fs.readFileSync("main.js","utf8");
// fs.writeFileSync("main.js",js,"utf8");
// fs.writeFileSync("index.html",(fs.readFileSync("index.html","utf8")).replace(/(.*node_modules.*)/g,''),"utf8");
......@@ -4,12 +4,20 @@
"description": "Ǧ'Perdu l'accès à mon compte, Ǧ'spère le retrouver",
"main": "public/index.html",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"postinstall": "node build.js",
"test": "echo open public/test.html in your browser"
},
"repository": {
"type": "git",
"url": "https://git.duniter.org/clients/Gsper.git"
},
"author": "[1000i100] Millicent Billette <git@1000i100.fr> (https://1forma-tic.fr/)",
"license": "AGPL-3.0"
"license": "AGPL-3.0",
"dependencies": {
"scrypt-async-modern": "^3.0.6",
"tweetnacl": "^1.0.0"
},
"devDependencies": {
"jasmine-core": "^3.1.0"
}
}
export {b58, saltPass2seed, seed2keyPair,idSecPass2rawAll, raw2b58, idSecPass2cleanKeys}
import {nacl} from "./vendors/nacl.js";
async function idSecPass2rawAll(idSec,pass) {
const rawSeed = await saltPass2seed(idSec,pass);
const keyPair = seed2keyPair(rawSeed);
return {
seed:rawSeed,
publicKey:keyPair.publicKey,
secretKey:keyPair.secretKey
}
}
function raw2b58(raws){
const res = {};
for(let r in raws) res[r] = b58.encode(raws[r]);
return res;
}
async function idSecPass2cleanKeys(idSec,pass){
const raw = await idSecPass2rawAll(idSec,pass);
return Object.assign(raw2b58(raw),{idSec,password:pass});
}
function seed2keyPair(seed){
return nacl.sign.keyPair.fromSeed(seed);
}
import scrypt from "./vendors/scrypt.js";
async function saltPass2seed(idSec,pass) {
const options = {
logN: 12,
r: 16,
p: 1,
//dkLen: 32,
encoding: 'binary'
};
return await scrypt(pass.normalize('NFKC'), idSec.normalize('NFKC'), options);
}
//inspired by bs58 and base-x module
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
const b58 = basex(ALPHABET);
function basex (ALPHABET) {
const ALPHABET_MAP = {};
const BASE = ALPHABET.length;
const LEADER = ALPHABET.charAt(0);
// pre-compute lookup table
for (let z = 0; z < ALPHABET.length; z++) {
let x = ALPHABET.charAt(z);
if (ALPHABET_MAP[x] !== undefined) throw new TypeError(x + ' is ambiguous');
ALPHABET_MAP[x] = z;
}
function encode (source) {
if (source.length === 0) return '';
const digits = [0];
for (let i = 0; i < source.length; ++i) {
let carry = source[i];
for (let j = 0; j < digits.length; ++j) {
carry += digits[j] << 8;
digits[j] = carry % BASE;
carry = (carry / BASE) | 0;
}
while (carry > 0) { digits.push(carry % BASE); carry = (carry / BASE) | 0; }
}
let string = '';
for (let k = 0; source[k] === 0 && k < source.length - 1; ++k) string += LEADER; // deal with leading zeros
for (let q = digits.length - 1; q >= 0; --q) string += ALPHABET[digits[q]]; // convert digits to a string
return string;
}
function decodeUnsafe (string) {
if (typeof string !== 'string') throw new TypeError('Expected String');
if (string.length === 0) return new Uint8Array(0);
const bytes = [0];
for (let i = 0; i < string.length; i++) {
const value = ALPHABET_MAP[string[i]];
if (value === undefined) return ;
let carry = value;
for (let j = 0; j < bytes.length; ++j) {
carry += bytes[j] * BASE;
bytes[j] = carry & 0xff;
carry >>= 8;
}
while (carry > 0) { bytes.push(carry & 0xff); carry >>= 8; }
}
for (let k = 0; string[k] === LEADER && k < string.length - 1; ++k) bytes.push(0); // deal with leading zeros
return new Uint8Array(bytes.reverse());
}
function decode (string) {
const buffer = decodeUnsafe(string);
if (buffer) return buffer;
throw new Error('Non-base' + BASE + ' character')
}
return { encode, decodeUnsafe, decode }
}
const idSec = "a";
const mdp = "b";
//base58
const pubKey = "AoxVA41dGL2s4ogMNdbCw3FFYjFo5FPK36LuiW1tjGbG";
const secretKey = "3ZsmZhnRv137dS1s7Q3jFGKLTDyhkwguPHfnWBxzDCTTHKWGnYw9zBk3gcCUJCc72TEUuyzM7cqpo7c5LYhs1Qtv";
const seed = "9eADqX8V6VcPdJCHCVYiE1Vnift9nFNrvr9aTaXA5RJc";
import * as app from "./crypto.js";
describe('crypto', () => {
it('b58 should decode/encode well', () => {
expect(app.b58.encode(app.b58.decode(pubKey))).toEqual(pubKey);
});
it('saltPass2seed should convert salt & password to seed with scrypt', async () => {
expect(app.b58.encode(await app.saltPass2seed(idSec,mdp))).toEqual(seed);
});
it('seed2keyPair should generate public and private key nacl/sodium way.', async () => {
const rawSeed = app.b58.decode(seed);
const rawKeyPair = app.seed2keyPair(rawSeed);
expect(app.b58.encode(rawKeyPair.publicKey)).toEqual(pubKey);
expect(app.b58.encode(rawKeyPair.secretKey)).toEqual(secretKey);
});
it('idSecPass2cleanKeys should output clean base58 keys and seed', async () => {
const res = await app.idSecPass2cleanKeys(idSec,mdp);
expect(res.publicKey).toEqual(pubKey);
expect(res.secretKey).toEqual(secretKey);
expect(res.seed).toEqual(seed);
expect(res.idSec).toEqual(idSec);
expect(res.password).toEqual(mdp);
});
});
......@@ -13,50 +13,7 @@
<meta property="og:url" content="https://gsper.duniter.io/" />
<meta property="og:image" content="https://gsper.duniter.io/img/gsper-logo.png" />
<meta property="og:description" content="Ǧ'Perdu l'accès à mon compte, Ǧ'spère le retrouver ! ǦéDéClé à tester !" />
<style>
* {font-family: sans-serif; box-sizing: border-box; margin: 0; padding: 0}
h1{font-weight: normal; width: 100%; text-align: center; line-height: 70px;}
input[type="text"],textarea{border: 0; padding: 5px;}
legend {margin-left: 10px; padding: 0 5px}
em{font-size: 70%}
.pasEncore{display: none}
fieldset, #info {margin: 10px;}
header, #lists, header fieldset{
display: flex;
flex-wrap: wrap;
}
header fieldset {flex: 8; margin-top: 0; display: block;}
header #info {flex: 2}
header fieldset label, header fieldset input { padding: 5px;line-height: 60px; font-size: 16px;}
header fieldset label {white-space: nowrap; flex: 1;}
header fieldset input {flex: 99; width: calc(100% - 130px);}
#lists fieldset {flex: 1}
textarea {height: 100%; width: 100%;}
button {width: 100%; height: 32px}
header {height: 180px;position: fixed;top:0;left:0;right: 0;}
#lists{position: fixed;top:180px;left:0;right: 0;bottom: 0;}
h1{padding-left: 3em; margin-top: 10px;}
.logo {height: 3em; position: absolute; margin: -0.6em -2.2em;}
.g1june {height: 1.2em; vertical-align: text-top;}
a {color: #666; text-decoration-style: dotted; text-decoration-color: #ccc;}
a:hover {color: #666; text-decoration-style: solid; text-decoration-color: #ccc;}
#resultat {
position: fixed;
z-index: 2;
top: 50%;
left: 50%;
width: 1000px;
margin-left: -500px;
height: 200px;
margin-top: -100px;
background-color: #efe;
padding: 20px;
border: 2px groove grey;
}
</style>
<link href="style.css" rel="stylesheet"/>
</head>
<body>
<header>
......@@ -105,7 +62,6 @@ password
</style>
</a>
</footer>
<script src="https://paperwallet.duniter.io/lib/duniter_tools.js"></script>
<script src="main.js"></script>
<script type="module" src="main.js"></script>
</body>
</html>
const cores = navigator.hardwareConcurrency;
import * as crypto from "./crypto.js";
addEventsListeners(document.querySelectorAll("#salt, #pass"),"change keyup",updateEstimate);
updateEstimate();
......@@ -8,18 +9,19 @@ function updateEstimate(){
document.getElementById("combi").innerHTML = idSecList.length*passList.length;
document.getElementById("temps").innerHTML = (idSecList.length*passList.length)+"s";
}
document.getElementById("compute").addEventListener("click",e=>{
document.getElementById("compute").addEventListener("click", async e=>{
const pub = document.getElementById("pubkey").value.trim();
const pubRaw = Base58.decode(pub);
const pubRaw = crypto.b58.decode(pub);
const idSecList = multiLineString2cleanArray(document.getElementById("salt").value);
const passList = multiLineString2cleanArray(document.getElementById("pass").value);
const combi = idSecList.length*passList.length;
updateEstimate();
document.getElementById("percent").innerHTML = "0%";
let count = 0;
for(let idSec of idSecList){
for (let pass of passList){
const seed = generate_seed_from_scrypt(idSec,pass)
if(pub === short_publickey_from_seed(seed)){
const keys = await crypto.idSecPass2cleanKeys(idSec,pass);
if(pub === keys.publicKey){
const resArea = document.getElementById("resultat");
resArea.classList.remove("pasEncore");
resArea.innerHTML = `<h2>Ǧ'trouvé ! ǦéLéClé !</h2><br/>
......@@ -27,43 +29,22 @@ document.getElementById("compute").addEventListener("click",e=>{
Mot de passe : ${pass}<br/>
-- Avancé --<br/>
Clef publique : ${pub}<br/>
Clef privée : ${privatekey_from_seed(seed)}<br/>
Graine : ${Base58.encode(seed)}<br/>
Clef privée : ${keys.secretKey}<br/>
Graine : ${keys.seed}<br/>
`;
document.getElementById("percent").innerHTML = "Trouvé :)";
return;
}
count++;
document.getElementById("percent").innerHTML = `${Math.round(10000*count/combi)/100}%`;
}
}
document.getElementById("percent").innerHTML = "Fini sans résultats";
});
async function main(){
console.log(short_publickey_from_seed(generate_seed_from_scrypt("a","b")));
}
main();
async function crypt(password, salt){
return new Promise( (resolve ,reject) => {
const options = {
logN: 12,
r: 16,
p: 1,
//dkLen: 32,
encoding: 'binary'
};
scrypt(password, salt, options, (res)=>{
resolve(res);
});
});
}
function addEventsListeners(triggerNodes,events,functions){
if(!triggerNodes.length) triggerNodes = [triggerNodes];
if(typeof events !== "object") events = events.split(" ");
if(typeof functions !== "object") functions = [functions];
console.log(triggerNodes[0].addEventListener, events, functions);
for(let n of triggerNodes) events.forEach(e=> functions.forEach(f=>n.addEventListener(e,f)));
}
function multiLineString2cleanArray(rawStr){ return rawStr.split("\n").map(str=>str.trim()).filter(str=>str !== ""); }
* {font-family: sans-serif; box-sizing: border-box; margin: 0; padding: 0}
h1{font-weight: normal; width: 100%; text-align: center; line-height: 70px;}
input[type="text"],textarea{border: 0; padding: 5px;}
legend {margin-left: 10px; padding: 0 5px}
em{font-size: 70%}
.pasEncore{display: none}
fieldset, #info {margin: 10px;}
header, #lists, header fieldset{
display: flex;
flex-wrap: wrap;
}
header fieldset {flex: 8; margin-top: 0; display: block;}
header #info {flex: 2}
header fieldset label, header fieldset input { padding: 5px;line-height: 60px; font-size: 16px;}
header fieldset label {white-space: nowrap; flex: 1;}
header fieldset input {flex: 99; width: calc(100% - 130px);}
#lists fieldset {flex: 1}
textarea {height: 100%; width: 100%;}
button {width: 100%; height: 32px}
header {height: 180px;position: fixed;top:0;left:0;right: 0;}
#lists{position: fixed;top:180px;left:0;right: 0;bottom: 0;}
h1{padding-left: 3em; margin-top: 10px;}
.logo {height: 3em; position: absolute; margin: -0.6em -2.2em;}
.g1june {height: 1.2em; vertical-align: text-top;}
a {color: #666; text-decoration-style: dotted; text-decoration-color: #ccc;}
a:hover {color: #666; text-decoration-style: solid; text-decoration-color: #ccc;}
#resultat {
position: fixed;
z-index: 2;
top: 50%;
left: 50%;
width: 1000px;
margin-left: -500px;
height: 200px;
margin-top: -100px;
background-color: #efe;
padding: 20px;
border: 2px groove grey;
}
<meta charset="utf-8" />
<link rel="shortcut icon" type="image/png" href="../node_modules/jasmine-core/images/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
<script type="text/javascript" src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script type="text/javascript" src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
<script type="text/javascript" src="../node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
<script type="module" src="crypto.test.js"></script>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment