urbit-ob

JavaScript utilities for phonemic base wrangling.
git clone git://git.jtobin.io/urbit-ob.git
Log | Files | Refs | README

ob.js (3925B)


      1 // ++  ob
      2 //
      3 // See arvo/sys/hoon.hoon.
      4 
      5 const BN = require('bn.js')
      6 const { muk } = require('./muk')
      7 
      8 const ux_1_0000 = new BN('10000', 'hex')
      9 const ux_ffff_ffff = new BN('ffffffff', 'hex')
     10 const ux_1_0000_0000 = new BN('100000000', 'hex')
     11 const ux_ffff_ffff_ffff_ffff = new BN('ffffffffffffffff', 'hex')
     12 const ux_ffff_ffff_0000_0000 = new BN('ffffffff00000000', 'hex')
     13 
     14 const u_65535 = new BN('65535')
     15 const u_65536 = new BN('65536')
     16 
     17 // a PRF for j in { 0, .., 3 }
     18 const F = (j, arg) => {
     19   const raku = [
     20     0xb76d5eed,
     21     0xee281300,
     22     0x85bcae01,
     23     0x4b387af7,
     24   ]
     25 
     26   return muk(raku[j], 2, arg)
     27 }
     28 
     29 /**
     30  * Conceal structure v3.
     31  *
     32  * @param {String, Number, BN} pyn
     33  * @return  {BN}
     34  */
     35 const fein = (arg) => {
     36   const loop = (pyn) => {
     37     const lo = pyn.and(ux_ffff_ffff)
     38     const hi = pyn.and(ux_ffff_ffff_0000_0000)
     39 
     40     return pyn.gte(ux_1_0000) && pyn.lte(ux_ffff_ffff)
     41       ? ux_1_0000.add(feis(pyn.sub(ux_1_0000)))
     42       : pyn.gte(ux_1_0000_0000) && pyn.lte(ux_ffff_ffff_ffff_ffff)
     43       ? hi.or(loop(lo))
     44       : pyn
     45   }
     46 
     47   return loop(new BN(arg))
     48 }
     49 
     50 /**
     51  * Restore structure v3.
     52  *
     53  * @param  {String, Number, BN}  cry
     54  * @return  {BN}
     55  */
     56 const fynd = (arg) => {
     57   const loop = (cry) => {
     58     const lo = cry.and(ux_ffff_ffff)
     59     const hi = cry.and(ux_ffff_ffff_0000_0000)
     60 
     61     return cry.gte(ux_1_0000) && cry.lte(ux_ffff_ffff)
     62       ? ux_1_0000.add(tail(cry.sub(ux_1_0000)))
     63       : cry.gte(ux_1_0000_0000) && cry.lte(ux_ffff_ffff_ffff_ffff)
     64       ? hi.or(loop(lo))
     65       : cry
     66   }
     67 
     68   return loop(new BN(arg))
     69 }
     70 
     71 /**
     72  * Generalised Feistel cipher.
     73  *
     74  * See: Black and Rogaway (2002), "Ciphers with arbitrary finite domains."
     75  *
     76  * Note that this has been adjusted from the reference paper in order to
     77  * support some legacy behaviour.
     78  *
     79  * @param  {String, Number, BN}
     80  * @return  {BN}
     81  */
     82 const feis = arg =>
     83   Fe(4, u_65535, u_65536, ux_ffff_ffff, F, new BN(arg))
     84 
     85 const Fe = (r, a, b, k, f, m) => {
     86   const c = fe(r, a, b, f, m)
     87   return (
     88       c.lt(k)
     89     ? c
     90     : fe(r, a, b, f, c)
     91   )
     92 }
     93 
     94 const fe = (r, a, b, f, m) => {
     95   const loop = (j, ell, arr) => {
     96     if (j > r) {
     97       return (
     98           r % 2 !== 0
     99         ? a.mul(arr).add(ell)
    100         : arr.eq(a)
    101         ? a.mul(arr).add(ell)
    102         : a.mul(ell).add(arr)
    103       )
    104     } else {
    105       const eff = f(j - 1, arr)
    106 
    107       const tmp =
    108           j % 2 !== 0
    109         ? ell.add(eff).mod(a)
    110         : ell.add(eff).mod(b)
    111 
    112       return loop(j + 1, arr, tmp)
    113     }
    114   }
    115 
    116   const L = m.mod(a)
    117   const R = m.div(a)
    118 
    119   return loop(1, L, R)
    120 }
    121 
    122 /**
    123  * Reverse 'feis'.
    124  *
    125  * See: Black and Rogaway (2002), "Ciphers with arbitrary finite domains."
    126  *
    127  * Note that this has been adjusted from the reference paper in order to
    128  * support some legacy behaviour.
    129  *
    130  * @param {Number, String, BN}  arg
    131  * @return  {BN}
    132  */
    133 const tail = arg =>
    134   Fen(4, u_65535, u_65536, ux_ffff_ffff, F, new BN(arg))
    135 
    136 const Fen = (r, a, b, k, f, m) => {
    137   const c = fen(r, a, b, f, m)
    138   return (
    139       c.lt(k)
    140     ? c
    141     : fen(r, a, b, f, c)
    142   )
    143 }
    144 
    145 const fen = (r, a, b, f, m) => {
    146   const loop = (j, ell, arr) => {
    147     if (j < 1) {
    148       return a.mul(arr).add(ell)
    149     } else {
    150       const eff = f(j - 1, ell)
    151 
    152       // NB (jtobin):
    153       //
    154       // Slight deviation from B&R (2002) here to prevent negative values.  We
    155       // add 'a' or 'b' to arr as appropriate and reduce 'eff' modulo the same
    156       // number before performing subtraction.
    157       //
    158       const tmp =
    159           j % 2 !== 0
    160         ? arr.add(a).sub(eff.mod(a)).mod(a)
    161         : arr.add(b).sub(eff.mod(b)).mod(b)
    162 
    163       return loop(j - 1, tmp, ell)
    164     }
    165   }
    166 
    167   const ahh =
    168       r % 2 !== 0
    169     ? m.div(a)
    170     : m.mod(a)
    171 
    172   const ale =
    173       r % 2 !== 0
    174     ? m.mod(a)
    175     : m.div(a)
    176 
    177   const L =
    178       ale.eq(a)
    179     ? ahh
    180     : ale
    181 
    182   const R =
    183       ale.eq(a)
    184     ? ale
    185     : ahh
    186 
    187   return loop(r, L, R)
    188 }
    189 
    190 module.exports = {
    191   F,
    192 
    193   fe,
    194   Fe,
    195   feis,
    196   fein,
    197 
    198   fen,
    199   Fen,
    200   tail,
    201   fynd
    202 }