urbit-key-generation
Version:
Key derivation and HD wallet generation functions for Urbit.
335 lines (294 loc) • 6.84 kB
JavaScript
/* istanbul ignore file */
var noun = require('./noun.js'),
BigInteger = require('jsbn').BigInteger,
zero = noun.Atom.yes,
one = noun.Atom.no;
// a is native, returns native
function met(a, b) {
var bits = b.number.bitLength(),
full = bits >>> a,
part = (full << a) !== bits;
return part ? full + 1 : full;
}
function gth(a, b) {
return a.number.compareTo(b.number) > 0;
}
function lth(a, b) {
return a.number.compareTo(b.number) < 0;
}
function gte(a, b) {
return a.number.compareTo(b.number) >= 0;
}
function lte(a, b) {
return a.number.compareTo(b.number) <= 0;
}
function add(a, b) {
return new noun.Atom.Atom(a.number.add(b.number));
}
function dec(a) {
return sub(a, one);
}
function bex(a) {
return new noun.Atom.Atom(BigInteger.ONE.shiftLeft(a.number.intValue()));
}
function sub(a, b) {
var r = a.number.subtract(b.number);
if ( r.signum < 0 ) {
throw new Error("subtract underflow");
}
else {
return new noun.Atom.Atom(r);
}
}
function lsh(a, b, c) {
var bits = b.number.shiftLeft(a.number.intValue()).intValue();
return new noun.Atom.Atom(c.number.shiftLeft(bits));
}
function rsh(a, b, c) {
var bits = b.number.shiftLeft(a.number.intValue()).intValue();
return new noun.Atom.Atom(c.number.shiftRight(bits));
}
// to/from little-endian 32-bit word array, as used in vere
// TODO: efficiency is horrible here, but can be improved using internals
function bytesToWords(bytes) {
var len = bytes.length,
trim = len % 4;
var i, b, w;
if ( trim > 0 ) {
len += (4-trim);
for ( i = 0; i < trim; ++i ) {
bytes.push(0);
}
}
var size = len >> 2;
var words = new Array(size);
for ( i = 0, b = 0; i < size; ++i ) {
w = (bytes[b++] << 0) & 0x000000FF;
w ^= (bytes[b++] << 8) & 0x0000FF00;
w ^= (bytes[b++] << 16) & 0x00FF0000;
w ^= (bytes[b++] << 24) & 0xFF000000;
words[i] = w;
}
return words;
};
function wordsToBytes(words) {
var buf = [];
var w, i, b;
for ( i = 0, b = 0; i < words.length; ++i ) {
w = words[i];
buf[b++] = 0xff & (w & 0x000000FF);
buf[b++] = 0xff & ((w & 0x0000FF00) >>> 8);
buf[b++] = 0xff & ((w & 0x00FF0000) >>> 16);
buf[b++] = 0xff & ((w & 0xFF000000) >>> 24);
}
// or here. one of the 'get rid of extra zeros' functions.
while ( buf[--b] === 0 ) {
buf.pop();
}
return buf;
};
function bytesToAtom(bytes) {
var byt, parts = [];
for ( var i = bytes.length - 1; i >= 0; --i ) {
byt = bytes[i] & 0xff;
parts.push(byt < 16 ? ("0" + byt.toString(16)) : byt.toString(16));
}
return new noun.Atom.Atom(new BigInteger(parts.join(''), 16));
}
function atomToBytes(atom) {
return atom.bytes();
}
function atomToWords(atom) {
return bytesToWords(atomToBytes(atom));
}
function wordsToAtom(words) {
return bytesToAtom(wordsToBytes(words));
}
var malt = wordsToAtom;
// XX: INTERNAL
function slaq(bloq, len) {
return new Array(((len << bloq) + 31) >>> 5);
}
// src is atom, all others native
function chop(met, fum, wid, tou, dst, src) {
var buf = atomToWords(src),
len = buf.length,
i, j, mek, baf, bat, hut, san,
wuf, wut, waf, raf, wat, rat, hop;
if ( met < 5 ) {
san = 1 << met;
mek = ((1 << san) - 1);
baf = fum << met;
bat = tou << met;
for ( i = 0; i < wid; ++i ) {
waf = baf >>> 5;
raf = baf & 31;
wat = bat >>> 5;
rat = bat & 31;
hop = ((waf >= len) ? 0 : buf[waf]);
hop = ((hop >>> raf) & mek);
dst[wat] ^= hop << rat;
baf += san;
bat += san;
}
}
else {
hut = met - 5;
san = 1 << hut;
for ( i = 0; i < wid; ++i ) {
wuf = (fum + i) << hut;
wut = (tou + i) << hut;
for ( j = 0; j < san; ++j ) {
dst[wut + j] ^= ((wuf + j) >= len)
? 0
: buf[wuf + j];
}
}
}
}
function cut(a, b, c, d) {
var ai = a.number.intValue(),
bi = b.number.intValue(),
ci = c.number.intValue();
var len = met(ai, d);
if ( zero.equals(c) || bi >= len ) {
return zero;
}
if ( bi + ci > len ) {
ci = len - b;
}
if ( 0 === bi && ci === len ) {
return d;
}
else {
var sal = slaq(ai, ci);
chop(ai, bi, ci, 0, sal, d);
return malt(sal);
}
}
var maxCat = noun.Atom.fromInt(0xffffffff);
var catBits = noun.Atom.fromInt(32);
function end(a, b, c) {
if ( gth(a, catBits) ) {
throw new Error("Fail");
}
else if ( gth(b, maxCat) ) {
return c;
}
else {
var ai = a.number.intValue(),
bi = b.number.intValue(),
len = met(ai, c);
if ( 0 === bi ) {
return zero;
}
else if ( bi >= len ) {
return c;
}
else {
var sal = slaq(ai, bi);
chop(ai, 0, bi, 0, sal, c);
return malt(sal);
}
}
}
function mix(a, b) {
return new noun.Atom.Atom(a.number.xor(b.number));
}
function cat(a, b, c) {
if ( gth(a, catBits) ) {
throw new Error("Fail");
}
else {
var ai = a.number.intValue(),
lew = met(ai, b),
ler = met(ai, c),
all = lew + ler;
if ( 0 === all ) {
return zero;
}
else {
var sal = slaq(ai, all);
chop(ai, 0, lew, 0, sal, b);
chop(ai, 0, ler, lew, sal, c);
return malt(sal);
}
}
}
function can(a, b) {
if ( gth(a, catBits) ) {
throw new Error("Fail");
}
else {
var ai = a.number.intValue(),
tot = 0,
cab = b,
pos, i_cab, pi_cab, qi_cab;
// measure
while ( true ) {
if ( zero.equals(cab) ) {
break;
}
if ( !cab.deep ) {
throw new Error("Fail");
}
i_cab = cab.head;
if ( !i_cab.deep ) {
throw new Error("Fail");
}
pi_cab = i_cab.head;
qi_cab = i_cab.tail;
if ( gth(pi_cab, maxCat) ) {
throw new Error("Fail");
}
if ( qi_cab.deep ) {
throw new Error("Fail");
}
if ( (tot + pi_cab) < tot ) {
throw new Error("Fail");
}
tot += pi_cab;
cab = cab.tail;
}
if ( 0 === tot ) {
return zero;
}
var sal = slaq(ai, tot);
// chop the list atoms in
cab = b;
pos = 0;
while ( !zero.equals(cab) ) {
i_cab = cab.head;
pi_cab = i_cab.head.number.intValue();
qi_cab = i_cab.tail;
chop(ai, 0, pi_cab, pos, sal, qi_cab);
pos += pi_cab;
cab = cab.tail;
}
return malt(sal);
}
}
module.exports = {
met: met,
cut: cut,
add: add,
sub: sub,
dec: dec,
gth: gth,
lth: lth,
gte: gte,
lte: lte,
bex: bex,
lsh: lsh,
rsh: rsh,
end: end,
mix: mix,
cat: cat,
can: can,
bytesToWords: bytesToWords,
wordsToBytes: wordsToBytes,
bytesToAtom: bytesToAtom,
atomToBytes: atomToBytes,
atomToWords: atomToWords,
wordsToAtom: wordsToAtom,
};