Crypto Training RSA algorithm

by Arnaud Buchholz

HTML

<table border="0" width="100%" cellpadding="0" cellspacing="0">
  <tr>
    <th class="public">&#128273; Public key</th>
    <th class="private">&#128272; Private key</th>
  </tr>
  <tr>
    <td class="public">
      <div class="input">
        <span class="label">N</span>
        <input type="number" id="N" value="1355"><br />
      </div>
    </td>
    <td class="private">
      <div class="input">
        <span class="label">N</span>
        <input type="number" id="Nro" value="1355" readonly><br />
      </div>
    </td>
  </tr>
  <tr>
    <td class="public">
      <div class="input">
        <span class="label">e</span>
        <input type="number" id="e" value="347">
      </div>
    </td>
    <td  class="private">
      <div class="input">
        <span class="label">d</span>
        <input type="number" id="d" readonly><br />
      </div>
    </td>
  </tr>
  <tr>
    <td class="algorithm">&#120543;N = (P - 1) * (Q - 1)</td>
    <td class="private-but-computed">
      <div class="input">
        <span class="label">P</span>
        <input type="number" id="P" readonly><br />
      </div>
    </td>
  </tr>
  <tr>
    <td class="algorithm">1 &lt; e &lt; &#120543;N</td>
    <td class="private-but-computed">
      <div class="input">
        <span class="label">Q</span>
        <input type="number" id="Q" readonly><br />
      </div>
    </td>
  </tr>
  <tr>
    <td class="algorithm">e * d mod &#120543;N = 1</td>
    <td class="private-but-computed">
      <div class="input">
        <span class="label">&#120543;N</span>
        <input type="number" id="phiN" readonly><br />
      </div>
    </td>
  </tr>
  <tr>
    <td colspan="2"><hr></td>
  </tr>
  <tr>
    <th>M <i>message</i></th>
    <th>C <i>crypted</i></th>
  </tr>
  <tr>
    <td>
      <textarea id="M">Hello World !</textarea>
    </td>
    <td>
      <textarea id="C"></textarea>
    </td>
  </tr>
  <tr>
    <td>
      <button id="encode">Encode &raquo;</button>
      <input type="checkbox" id="asText"...

CSS

th {
  text-align: left;
}

input[readonly] {
  border: 1px gray dotted;
}

.public {
  background-color: #bdf0cb;
}

.private {
  background-color: #f0bdbd;
}

.private-but-computed {
  background-color: #f5dada;
}

.algorithm {
  background-color: #dfdfdf;
}

td.algorithm {
  text-align: center;
}

.input {
  margin: 4px;
}

.label {
  display: inline-block;
  width: 10%;
}

input.error {
  border: 1px red solid;
}

textarea {
  width: 90%;
  resize: none;
  height: 5rem;
}

JavaScript

const byId = id => document.getElementById(id)

byId('decode').addEventListener('click', () => {
  const {d, N} = refresh()
  const C = readNumbers('C')
  const M = C.map(c => Number(pow(c, d) % BigInt(N)))
  if (byId('asText').checked) {
    writeTextMessage(M)
  } else {
  	set('M', M.join(' '))
  }
})

byId('encode').addEventListener('click', () => {
  const {e, N} = refresh()
  let M
  if (byId('asText').checked) {
    M = readTextMessage()
  } else {
    M = readNumbers('M')
  }
  const C = M.map(m => Number(pow(m, e) % BigInt(N)))
	set('C', C.join(' '))
})

byId('N').addEventListener('change', refresh)
byId('e').addEventListener('change', refresh)
byId('asText').addEventListener('click', function () {
  if (this.checked) {
    writeTextMessage(readNumbers('M'))
  } else {
    set('M', readTextMessage().join(' '))
  }
})

const get = id => byId(id).value
const getNumber = id => parseInt(get(id), 10)

function set (id, value) {
  document.getElementById(id).value = value
}

function setState (id, error, message) {
  const control = byId(id)
  const classList = control.classList
  if (error) {
    classList.add('error')
    control.title = `\u26a0\ufe0f ${message}`
  } else {
    classList.remove('error')
    control.title = ''
  }
}

function refresh () {
  const N = get('N')
  set('Nro', N)
  const [P, Q, ...others] = prime(N)
  set('P', P)
  set('Q', Q)
  const invalidPrimes = others.length || !Q
  setState('N', invalidPrimes, 'Invalid prime number combination')
  const phiN = (P - 1) * (Q - 1)
  set('phiN', phiN)
  const e = getNumber('e')
  setState('e', !(1 < e && e < phiN), '1 < e < \ud835\udedfN')
  let d
  for (d = 1; d < phiN; ++d) {
    if ((d * e) % phiN === 1) {
      break
    }
  }
  set('d', d)
  return {d, e, N}
}

const readNumbers = id => get(id).split(' ').map(s => parseInt(s, 10))
const readTextMessage = () => get('M').split('').map(m => m.charCodeAt(0))
const writeTextMessage = M => set('M', M.map(m =>...