Crypto Training RSA algorithm
by Arnaud Buchholz
HTML
<table border="0" width="100%" cellpadding="0" cellspacing="0">
<tr>
<th class="public">🔑 Public key</th>
<th class="private">🔐 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">𝛟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 < e < 𝛟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 𝛟N = 1</td>
<td class="private-but-computed">
<div class="input">
<span class="label">𝛟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 »</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 =>...