ES6 Plato on Github
Report Home
Summary Display
crypto.mjs
Maintainability
69.52
Lines of code
253
Difficulty
60.19
Estimated Errors
3.27
Function weight
By Complexity
By SLOC
// 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 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, sha256}; const sha256Instance = sha256(); const generateKeypair = nacl.sign.keyPair.fromSeed; export async function idSecPass2rawAll(idSec, pass) { const rawSeed = await saltPass2seed(idSec, pass); const keyPair = seed2keyPair(rawSeed); keyPair.seed = rawSeed; keyPair.pubKey = keyPair.publicKey; return keyPair; } export function raw2b58(raws) { const result = {}; for (const r in raws) result[r] = b58.encode(raws[r]); return result; } export async function idSecPass2cleanKeys(idSec, pass) { const raw = await idSecPass2rawAll(idSec, pass); return Object.assign(raw2b58(raw), {idSec, password: pass}); } export function seed2keyPair(seed) { return generateKeypair(seed); } /* Noble edition export async function seed2keyPair(rawSeed) { const pubKey = await ed25519.getPublicKey(rawSeed); console.log('pubKey',pubKey); const naclLikePrivateKey = new Uint8Array(64); naclLikePrivateKey.set(rawSeed); naclLikePrivateKey.set(pubKey,32); return { publicKey:pubKey, secretKey:naclLikePrivateKey }; } */ export async function saltPass2seed(idSec, pass) { const options = { logN: 12, r: 16, p: 1, // Default: dkLen: 32, encoding: 'binary' }; return scrypt(pass.normalize('NFKC'), idSec.normalize('NFKC'), options); } export function pubKey2shortKey(pubKey) { const pubKeyBegin = pubKey.substr(0, 4); const pubKeyEnd = pubKey.substr(-4, 4); const checksum = pubKey2checksum(pubKey); return `${pubKeyBegin}…${pubKeyEnd}:${checksum}`; } export function pubKey2checksum(b58pubKey, b58viewDependant = false, checksumWithLeadingZero = false, doubleSha256 = true) { 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); } return b58.encode(hash).substr(0, 3); } export function sliceInitialsZero(array) { let zero = 0; 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); binPubKey.set(decoded, 32 - decoded.length); return binPubKey; } export function b58secretKey2bin(b58secretKey) { const binSecretKey = new Uint8Array(64); const decoded = b58.decode(b58secretKey); binSecretKey.set(decoded, 64 - decoded.length); return binSecretKey; } export function onlyPubKey(pubKeyWithChecksum){ const part = pubKeyWithChecksum.split(':'); const b58pubKey = part[0]; const checkSum = part[1]; 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 function sign(str, secretKey, outputFormat = 'b64') { const encoder = new TextEncoder(); const raw = rawSign(encoder.encode(str), b58secretKey2bin(secretKey).slice(0, 32)); switch (outputFormat.toLocaleLowerCase()) { case 'raw': case 'array': case 'uint8array': return raw; case 'b64': return b64.encode(raw); case 'b58': return b58.encode(raw); default: throw new Error(`OutputFormat ${outputFormat} not handled.`); } } 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; } }