add chacha20; align API with PR#21
This commit is contained in:
parent
311905883a
commit
30b8e83417
@ -21,24 +21,24 @@ module.exports = {
|
|||||||
crypto_secretbox_MACBYTES
|
crypto_secretbox_MACBYTES
|
||||||
}
|
}
|
||||||
|
|
||||||
function crypto_secretbox(c,m,d,n,k) {
|
function crypto_secretbox (c, m, d, n, k) {
|
||||||
var i;
|
var i
|
||||||
if (d < 32) return -1;
|
if (d < 32) return -1
|
||||||
crypto_stream_xor(c,0,m,0,d,n,k);
|
crypto_stream_xor(c, m, n, k)
|
||||||
crypto_onetimeauth(c, 16, c, 32, d - 32, c);
|
crypto_onetimeauth(c, 16, c, 32, d - 32, c)
|
||||||
for (i = 0; i < 16; i++) c[i] = 0;
|
for (i = 0; i < 16; i++) c[i] = 0
|
||||||
return 0;
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function crypto_secretbox_open(m,c,d,n,k) {
|
function crypto_secretbox_open (m, c, d, n, k) {
|
||||||
var i;
|
var i
|
||||||
var x = new Uint8Array(32);
|
var x = new Uint8Array(32)
|
||||||
if (d < 32) return -1;
|
if (d < 32) return -1
|
||||||
crypto_stream(x,0,32,n,k);
|
crypto_stream(x, n, k)
|
||||||
if (crypto_onetimeauth_verify(c, 16,c, 32,d - 32,x) !== 0) return -1;
|
if (crypto_onetimeauth_verify(c, 16, c, 32, d - 32, x) !== 0) return -1
|
||||||
crypto_stream_xor(m,0,c,0,d,n,k);
|
crypto_stream_xor(m, c, n, k)
|
||||||
for (i = 0; i < 32; i++) m[i] = 0;
|
for (i = 0; i < 32; i++) m[i] = 0
|
||||||
return 0;
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function crypto_secretbox_detached (o, mac, msg, n, k) {
|
function crypto_secretbox_detached (o, mac, msg, n, k) {
|
||||||
@ -64,10 +64,10 @@ function crypto_secretbox_easy(o, msg, n, k) {
|
|||||||
check(k, crypto_secretbox_KEYBYTES)
|
check(k, crypto_secretbox_KEYBYTES)
|
||||||
|
|
||||||
var i
|
var i
|
||||||
var m = new Uint8Array(crypto_secretbox_ZEROBYTES + msg.length);
|
var m = new Uint8Array(crypto_secretbox_ZEROBYTES + msg.length)
|
||||||
var c = new Uint8Array(m.length);
|
var c = new Uint8Array(m.length)
|
||||||
for (i = 0; i < msg.length; i++) m[i+crypto_secretbox_ZEROBYTES] = msg[i];
|
for (i = 0; i < msg.length; i++) m[i+crypto_secretbox_ZEROBYTES] = msg[i]
|
||||||
crypto_secretbox(c, m, m.length, n, k);
|
crypto_secretbox(c, m, m.length, n, k)
|
||||||
for (i = crypto_secretbox_BOXZEROBYTES; i < c.length; i++) o[i - crypto_secretbox_BOXZEROBYTES] = c[i]
|
for (i = crypto_secretbox_BOXZEROBYTES; i < c.length; i++) o[i - crypto_secretbox_BOXZEROBYTES] = c[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,11 +78,11 @@ function crypto_secretbox_open_easy(msg, box, n, k) {
|
|||||||
check(k, crypto_secretbox_KEYBYTES)
|
check(k, crypto_secretbox_KEYBYTES)
|
||||||
|
|
||||||
var i
|
var i
|
||||||
var c = new Uint8Array(crypto_secretbox_BOXZEROBYTES + box.length);
|
var c = new Uint8Array(crypto_secretbox_BOXZEROBYTES + box.length)
|
||||||
var m = new Uint8Array(c.length);
|
var m = new Uint8Array(c.length);
|
||||||
for (i = 0; i < box.length; i++) c[i+crypto_secretbox_BOXZEROBYTES] = box[i];
|
for (i = 0; i < box.length; i++) c[i+crypto_secretbox_BOXZEROBYTES] = box[i]
|
||||||
if (c.length < 32) return false;
|
if (c.length < 32) return false
|
||||||
if (crypto_secretbox_open(m, c, c.length, n, k) !== 0) return false;
|
if (crypto_secretbox_open(m, c, c.length, n, k) !== 0) return false
|
||||||
|
|
||||||
for (i = crypto_secretbox_ZEROBYTES; i < m.length; i++) msg[i - crypto_secretbox_ZEROBYTES] = m[i]
|
for (i = crypto_secretbox_ZEROBYTES; i < m.length; i++) msg[i - crypto_secretbox_ZEROBYTES] = m[i]
|
||||||
return true
|
return true
|
||||||
|
@ -4,12 +4,12 @@ exports.crypto_stream_KEYBYTES = 32
|
|||||||
exports.crypto_stream_NONCEBYTES = 24
|
exports.crypto_stream_NONCEBYTES = 24
|
||||||
exports.crypto_stream_PRIMITIVE = 'xsalsa20'
|
exports.crypto_stream_PRIMITIVE = 'xsalsa20'
|
||||||
|
|
||||||
exports.crypto_stream = function (c, cpos, clen, nonce, key) {
|
exports.crypto_stream = function (c, nonce, key) {
|
||||||
c.fill(0)
|
c.fill(0)
|
||||||
exports.crypto_stream_xor(c, 0, c, 0, 0, nonce, key)
|
exports.crypto_stream_xor(c, c, nonce, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.crypto_stream_xor = function (c, cpos, m, mpos, clen, nonce, key) {
|
exports.crypto_stream_xor = function (c, m, nonce, key) {
|
||||||
var xor = xsalsa20(nonce, key)
|
var xor = xsalsa20(nonce, key)
|
||||||
|
|
||||||
xor.update(m, c)
|
xor.update(m, c)
|
||||||
|
209
crypto_stream_chacha20.js
Normal file
209
crypto_stream_chacha20.js
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
const assert = require('nanoassert')
|
||||||
|
|
||||||
|
const constant = [1634760805, 857760878, 2036477234, 1797285236]
|
||||||
|
|
||||||
|
exports.crypto_stream_chacha20_KEYBYTES = 32
|
||||||
|
exports.crypto_stream_chacha20_NONCEBYTES = 8
|
||||||
|
exports.crypto_stream_chacha20_MESSAGEBYTES_MAX = Number.MAX_SAFE_INTEGER
|
||||||
|
|
||||||
|
exports.crypto_stream_chacha20_ietf_KEYBYTES = 32
|
||||||
|
exports.crypto_stream_chacha20_ietf_NONCEBYTES = 12
|
||||||
|
exports.crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX = 2 ** 32
|
||||||
|
|
||||||
|
exports.crypto_stream_chacha20 = function (c, n, k) {
|
||||||
|
c.fill(0)
|
||||||
|
exports.crypto_stream_chacha20_xor(c, c, n, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.crypto_stream_chacha20_xor = function (c, m, n, k) {
|
||||||
|
assert(n.byteLength === exports.crypto_stream_chacha20_NONCEBYTES,
|
||||||
|
'n should be crypto_stream_chacha20_NONCEBYTES')
|
||||||
|
assert(k.byteLength === exports.crypto_stream_chacha20_KEYBYTES,
|
||||||
|
'k should be crypto_stream_chacha20_KEYBYTES')
|
||||||
|
|
||||||
|
const xor = new Chacha20(n, k)
|
||||||
|
xor.update(c, m)
|
||||||
|
xor.final()
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.crypto_stream_chacha20_xor_ic = function (c, m, n, ic, k) {
|
||||||
|
assert(n.byteLength === exports.crypto_stream_chacha20_NONCEBYTES,
|
||||||
|
'n should be crypto_stream_chacha20_NONCEBYTES')
|
||||||
|
assert(k.byteLength === exports.crypto_stream_chacha20_KEYBYTES,
|
||||||
|
'k should be crypto_stream_chacha20_KEYBYTES')
|
||||||
|
|
||||||
|
const xor = new Chacha20(n, k, ic)
|
||||||
|
xor.update(c, m)
|
||||||
|
xor.final()
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.crypto_stream_chacha20_xor_instance = function (n, k) {
|
||||||
|
assert(n.byteLength === exports.crypto_stream_chacha20_NONCEBYTES,
|
||||||
|
'n should be crypto_stream_chacha20_NONCEBYTES')
|
||||||
|
assert(k.byteLength === exports.crypto_stream_chacha20_KEYBYTES,
|
||||||
|
'k should be crypto_stream_chacha20_KEYBYTES')
|
||||||
|
|
||||||
|
return new Chacha20(n, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.crypto_stream_chacha20_ietf = function (c, n, k) {
|
||||||
|
c.fill(0)
|
||||||
|
exports.crypto_stream_chacha20_ietf_xor(c, c, n, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.crypto_stream_chacha20_ietf_xor = function (c, m, n, k) {
|
||||||
|
assert(n.byteLength === exports.crypto_stream_chacha20_ietf_NONCEBYTES,
|
||||||
|
'n should be crypto_stream_chacha20_ietf_NONCEBYTES')
|
||||||
|
assert(k.byteLength === exports.crypto_stream_chacha20_ietf_KEYBYTES,
|
||||||
|
'k should be crypto_stream_chacha20_ietf_KEYBYTES')
|
||||||
|
|
||||||
|
const xor = new Chacha20(n, k)
|
||||||
|
xor.update(c, m)
|
||||||
|
xor.final()
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.crypto_stream_chacha20_ietf_xor_ic = function (c, m, n, ic, k) {
|
||||||
|
assert(n.byteLength === exports.crypto_stream_chacha20_ietf_NONCEBYTES,
|
||||||
|
'n should be crypto_stream_chacha20_ietf_NONCEBYTES')
|
||||||
|
assert(k.byteLength === exports.crypto_stream_chacha20_ietf_KEYBYTES,
|
||||||
|
'k should be crypto_stream_chacha20_ietf_KEYBYTES')
|
||||||
|
|
||||||
|
const xor = new Chacha20(n, k, ic)
|
||||||
|
xor.update(c, m)
|
||||||
|
xor.final()
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.crypto_stream_chacha20_ietf_xor_instance = function (n, k) {
|
||||||
|
assert(n.byteLength === exports.crypto_stream_chacha20_ietf_NONCEBYTES,
|
||||||
|
'n should be crypto_stream_chacha20_ietf_NONCEBYTES')
|
||||||
|
assert(k.byteLength === exports.crypto_stream_chacha20_ietf_KEYBYTES,
|
||||||
|
'k should be crypto_stream_chacha20_ietf_KEYBYTES')
|
||||||
|
|
||||||
|
return new Chacha20(n, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Chacha20 (n, k, counter) {
|
||||||
|
assert(k.byteLength === exports.crypto_stream_chacha20_ietf_KEYBYTES)
|
||||||
|
assert(n.byteLength === exports.crypto_stream_chacha20_NONCEBYTES ||
|
||||||
|
n.byteLength === exports.crypto_stream_chacha20_ietf_NONCEBYTES)
|
||||||
|
|
||||||
|
if (!counter) counter = 0
|
||||||
|
assert(counter < Number.MAX_SAFE_INTEGER)
|
||||||
|
|
||||||
|
this.finalized = false
|
||||||
|
this.pos = 0
|
||||||
|
this.state = new Uint32Array(16)
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; i++) this.state[i] = constant[i]
|
||||||
|
for (let i = 0; i < 8; i++) this.state[4 + i] = k.readUInt32LE(4 * i)
|
||||||
|
|
||||||
|
this.state[12] = counter & 0xffffffff
|
||||||
|
|
||||||
|
if (n.byteLength === 8) {
|
||||||
|
this.state[13] = (counter && 0xffffffff00000000) >> 32
|
||||||
|
this.state[14] = n.readUInt32LE(0)
|
||||||
|
this.state[15] = n.readUInt32LE(4)
|
||||||
|
} else {
|
||||||
|
this.state[13] = n.readUInt32LE(0)
|
||||||
|
this.state[14] = n.readUInt32LE(4)
|
||||||
|
this.state[15] = n.readUInt32LE(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
Chacha20.prototype.update = function (output, input) {
|
||||||
|
assert(!this.finalized, 'cipher finalized.')
|
||||||
|
assert(output.byteLength >= input.byteLength,
|
||||||
|
'output cannot be shorter than input.')
|
||||||
|
|
||||||
|
let len = input.length
|
||||||
|
let offset = this.pos % 64
|
||||||
|
this.pos += len
|
||||||
|
|
||||||
|
// input position
|
||||||
|
let j = 0
|
||||||
|
|
||||||
|
let keyStream = chacha20Block(this.state)
|
||||||
|
|
||||||
|
// try to finsih the current block
|
||||||
|
while (offset > 0 && len > 0) {
|
||||||
|
output[j] = input[j++] ^ keyStream[offset]
|
||||||
|
offset = (offset + 1) & 0x3f
|
||||||
|
if (!offset) this.state[12]++
|
||||||
|
len--
|
||||||
|
}
|
||||||
|
|
||||||
|
// encrypt rest block at a time
|
||||||
|
while (len > 0) {
|
||||||
|
keyStream = chacha20Block(this.state)
|
||||||
|
|
||||||
|
// less than a full block remaining
|
||||||
|
if (len < 64) {
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
output[j] = input[j++] ^ keyStream[offset++]
|
||||||
|
offset &= 0x3f
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; offset < 64;) {
|
||||||
|
output[j] = input[j++] ^ keyStream[offset++]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state[12]++
|
||||||
|
offset = 0
|
||||||
|
len -= 64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Chacha20.prototype.final = function () {
|
||||||
|
this.finalized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function chacha20Block (state) {
|
||||||
|
// working state
|
||||||
|
const ws = new Uint32Array(16)
|
||||||
|
for (let i = 16; i--;) ws[i] = state[i]
|
||||||
|
|
||||||
|
for (let i = 0; i < 20; i += 2) {
|
||||||
|
QR(ws, 0, 4, 8, 12) // column 0
|
||||||
|
QR(ws, 1, 5, 9, 13) // column 1
|
||||||
|
QR(ws, 2, 6, 10, 14) // column 2
|
||||||
|
QR(ws, 3, 7, 11, 15) // column 3
|
||||||
|
|
||||||
|
QR(ws, 0, 5, 10, 15) // diagonal 1 (main diagonal)
|
||||||
|
QR(ws, 1, 6, 11, 12) // diagonal 2
|
||||||
|
QR(ws, 2, 7, 8, 13) // diagonal 3
|
||||||
|
QR(ws, 3, 4, 9, 14) // diagonal 4
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
ws[i] += state[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return Buffer.from(ws.buffer, ws.byteOffset, ws.byteLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
function rotl (a, b) {
|
||||||
|
return ((a << b) | (a >>> (32 - b)))
|
||||||
|
}
|
||||||
|
|
||||||
|
function QR (obj, a, b, c, d) {
|
||||||
|
obj[a] += obj[b]
|
||||||
|
obj[d] ^= obj[a]
|
||||||
|
obj[d] = rotl(obj[d], 16)
|
||||||
|
|
||||||
|
obj[c] += obj[d]
|
||||||
|
obj[b] ^= obj[c]
|
||||||
|
obj[b] = rotl(obj[b], 12)
|
||||||
|
|
||||||
|
obj[a] += obj[b]
|
||||||
|
obj[d] ^= obj[a]
|
||||||
|
obj[d] = rotl(obj[d], 8)
|
||||||
|
|
||||||
|
obj[c] += obj[d]
|
||||||
|
obj[b] ^= obj[c]
|
||||||
|
obj[b] = rotl(obj[b], 7)
|
||||||
|
}
|
1
index.js
1
index.js
@ -35,6 +35,7 @@ forward(require('./crypto_secretbox'))
|
|||||||
forward(require('./crypto_shorthash'))
|
forward(require('./crypto_shorthash'))
|
||||||
forward(require('./crypto_sign'))
|
forward(require('./crypto_sign'))
|
||||||
forward(require('./crypto_stream'))
|
forward(require('./crypto_stream'))
|
||||||
|
forward(require('./crypto_stream_chacha20'))
|
||||||
forward(require('./randombytes'))
|
forward(require('./randombytes'))
|
||||||
|
|
||||||
function forward (submodule) {
|
function forward (submodule) {
|
||||||
|
Loading…
Reference in New Issue
Block a user