Created
June 27, 2021 05:25
-
-
Save TransparentLC/a528c9122f1e356ba202892461cdce90 to your computer and use it in GitHub Desktop.
4 KB 左右的 ChaCha20Poly1305 认证加密算法实现,参考 https://github.com/thesimj/js-chacha20 和 https://github.com/devi/chacha20poly1305 进行了少量修改。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { ChaCha20Poly1305 } = require('./chacha20poly1305.min.js'); | |
const hexToBytes = e => new Uint8Array(e.match(/[0-9a-f]{2}/gi).map(e => parseInt(e, 16))); | |
const bytesToHex = e => Array.from(e).map(e => e.toString(16).padStart(2, 0)).join(''); | |
// Test vectors from: | |
// https://datatracker.ietf.org/doc/html/rfc7539#section-2.8.2 | |
const keyC = hexToBytes('808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f') | |
const nonceC = hexToBytes('070000004041424344454647'); | |
const authC = hexToBytes('50515253c0c1c2c3c4c5c6c7'); | |
const plainC1 = hexToBytes('' | |
+ '4c616469657320616e642047656e746c' | |
+ '656d656e206f662074686520636c6173' | |
+ '73206f66202739393a20496620492063' | |
+ '6f756c64206f6666657220796f75206f' | |
); | |
const plainC2 = hexToBytes('' | |
+ '6e6c79206f6e652074697020666f7220' | |
+ '746865206675747572652c2073756e73' | |
+ '637265656e20776f756c642062652069' | |
+ '742e' | |
); | |
const contextC1 = new ChaCha20Poly1305(keyC, nonceC, authC); | |
const cipherC1 = contextC1.encrypt(plainC1); | |
const cipherC2 = contextC1.encrypt(plainC2); | |
console.assert( | |
bytesToHex(cipherC1) === '' | |
+ 'd31a8d34648e60db7b86afbc53ef7ec2' | |
+ 'a4aded51296e08fea9e2b5a736ee62d6' | |
+ '3dbea45e8ca9671282fafb69da92728b' | |
+ '1a71de0a9e060b2905d6a5b67ecd3b36', | |
'Cipher C1' | |
); | |
console.assert( | |
bytesToHex(cipherC2) === '' | |
+ '92ddbd7f2d778b8c9803aee328091b58' | |
+ 'fab324e4fad675945585808b4831d7bc' | |
+ '3ff4def08e4b7a9de576d26586cec64b' | |
+ '6116', | |
'Cipher C2' | |
); | |
const macC = contextC1.mac(); | |
console.assert(bytesToHex(macC) === '1ae10b594f09e26a7e902ecbd0600691', 'Mac C1'); | |
const contextC2 = new ChaCha20Poly1305(keyC, nonceC, authC); | |
const decryptC1 = contextC2.decrypt(cipherC1); | |
const decryptC2 = contextC2.decrypt(cipherC2); | |
console.assert(bytesToHex(plainC1) === bytesToHex(decryptC1), 'Decrypt C1'); | |
console.assert(bytesToHex(plainC2) === bytesToHex(decryptC2), 'Decrypt C2'); | |
console.assert(contextC2.verify(macC), 'Verify C'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2017, Bubelich Mykola | |
* https://www.bubelich.com | |
* | |
* (。◕‿‿◕。) | |
* | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met, 0x | |
* | |
* Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the following disclaimer. | |
* | |
* Redistributions in binary form must reproduce the above copyright notice, | |
* this list of conditions and the following disclaimer in the documentation | |
* and/or other materials provided with the distribution. | |
* | |
* Neither the name of the copyright holder nor the names of its contributors | |
* may be used to endorse or promote products derived from this software without | |
* specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" | |
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
* POSSIBILITY OF SUCH DAMAGE. | |
* | |
* ChaCha20 is a stream cipher designed by D. J. Bernstein. | |
* It is a refinement of the Salsa20 algorithm, and it uses a 256-bit key. | |
* | |
* ChaCha20 successively calls the ChaCha20 block function, with the same key and nonce, and with successively increasing block counter parameters. | |
* ChaCha20 then serializes the resulting state by writing the numbers in little-endian order, creating a keystream block. | |
* | |
* Concatenating the keystream blocks from the successive blocks forms a keystream. | |
* The ChaCha20 function then performs an XOR of this keystream with the plaintext. | |
* Alternatively, each keystream block can be XORed with a plaintext block before proceeding to create the next block, saving some memory. | |
* There is no requirement for the plaintext to be an integral multiple of 512 bits. If there is extra keystream from the last block, it is discarded. | |
* | |
* The inputs to ChaCha20 are | |
* - 256-bit key | |
* - 32-bit initial counter | |
* - 96-bit nonce. In some protocols, this is known as the Initialization Vector | |
* - Arbitrary-length plaintext | |
* | |
* Implementation derived from chacha-ref.c version 20080118 | |
* See for details, 0x http, 0x//cr.yp.to/chacha/chacha-20080128.pdf | |
*/ | |
/* poly1305 | |
* | |
* Written in 2014 by Devi Mandiri. Public domain. | |
* Implementation derived from poly1305-donna-16.h | |
* See for details: https://github.com/floodyberry/poly1305-donna | |
*/ | |
/* | |
Terser option: | |
{ | |
module: true, | |
compress: { | |
passes: 2, | |
}, | |
mangle: { | |
properties: { | |
keep_quoted: 'strict', | |
}, | |
}, | |
output: {}, | |
parse: {}, | |
rename: {}, | |
} | |
*/ | |
(() => { | |
/** @type {globalThis} */ | |
const GLOBAL = typeof globalThis !== 'undefined' ? globalThis : (global || self); | |
const { | |
Error, | |
Uint8Array, | |
Uint16Array, | |
Uint32Array, | |
} = GLOBAL; | |
/** | |
* The basic operation of the ChaCha algorithm is the quarter round. | |
* It operates on four 32-bit unsigned integers, denoted a, b, c, and d. | |
* | |
* @param {Uint32Array} output | |
* @param {Number} a | |
* @param {Number} b | |
* @param {Number} c | |
* @param {Number} d | |
*/ | |
const _quarterround = (output, a, b, c, d) => { | |
output[d] = _rotl(output[d] ^ (output[a] += output[b]), 16) | |
output[b] = _rotl(output[b] ^ (output[c] += output[d]), 12) | |
output[d] = _rotl(output[d] ^ (output[a] += output[b]), 8) | |
output[b] = _rotl(output[b] ^ (output[c] += output[d]), 7) | |
// JavaScript hack to make UINT32 :) // | |
// output[a] >>>= 0 | |
// output[b] >>>= 0 | |
// output[c] >>>= 0 | |
// output[d] >>>= 0 | |
} | |
/** | |
* Little-endian to uint 32 bytes | |
* | |
* @param {Uint8Array} data | |
* @param {Number} index | |
* @return {Number} | |
*/ | |
const _get32 = (data, index) => data[index++] | (data[index++] << 8) | (data[index++] << 16) | (data[index] << 24); | |
/** | |
* Little-endian to uint 16 bytes | |
* | |
* @param {Uint8Array} data | |
* @param {Number} index | |
* @return {Number} | |
*/ | |
const _get16 = (data, index) => data[index++] | (data[index] << 8); | |
/** | |
* Cyclic left rotation | |
* | |
* @param {Number} data | |
* @param {Number} shift | |
* @return {Number} | |
*/ | |
const _rotl = (data, shift) => ((data << shift) | (data >>> (32 - shift))); | |
class ChaCha20 { | |
/** | |
* @param {Uint8Array} key | |
* @param {Uint8Array} nonce | |
* @param {Number} [counter] | |
*/ | |
constructor(key, nonce, counter = 0) { | |
if (!(key instanceof Uint8Array) || key.length !== 32) { | |
throw new Error('Key should be 32 byte array!') | |
} | |
if (!(nonce instanceof Uint8Array) || nonce.length !== 12) { | |
throw new Error('Nonce should be 12 byte array!') | |
} | |
this._rounds = 20 | |
// param construction | |
const _param = new Uint32Array(16); | |
_param.set(new Uint32Array([0x61707865, 0x3320646E, 0x79622D32, 0x6B206574])); | |
_param[12] = counter; | |
let i = 8; | |
while (i--) { | |
_param[i + 4] = _get32(key, i << 2); | |
} | |
i = 3; | |
while (i--) { | |
_param[i + 13] = _get32(nonce, i << 2); | |
} | |
this._param = _param; | |
// init 64 byte keystream block // | |
this._keystream = new Uint8Array(64) | |
// internal byte counter // | |
this._byteCounter = 0 | |
} | |
_chacha() { | |
// copy param array to mix // | |
const mix = new Uint32Array(this._param) | |
let i = 0 | |
let j = 0 | |
// mix rounds // | |
for (i = 0; i < this._rounds; i += 2) { | |
for (j = 0; j < 4; j++) { | |
_quarterround(mix, j, j | 0x4, j | 0x8, j | 0xC); | |
// _quarterround(mix, j, j + 4, j + 8, j + 12); | |
} | |
// this._quarterround(mix, 0, 4, 8, 12) | |
// this._quarterround(mix, 1, 5, 9, 13) | |
// this._quarterround(mix, 2, 6, 10, 14) | |
// this._quarterround(mix, 3, 7, 11, 15) | |
for (j = 0; j < 4; j++) { | |
_quarterround(mix, j, ((j + 1) & 3) | 0x4, ((j + 2) & 3) | 0x8, ((j + 3) & 3) | 0xC) | |
// _quarterround(mix, j, ((j + 1) & 3) + 4, ((j + 2) & 3) + 8, ((j + 3) & 3) + 12) | |
} | |
// _quarterround(mix, 0, 5, 10, 15) | |
// _quarterround(mix, 1, 6, 11, 12) | |
// _quarterround(mix, 2, 7, 8, 13) | |
// _quarterround(mix, 3, 4, 9, 14) | |
} | |
let b = 0 | |
for (i = 0; i < 16; i++) { | |
// add | |
mix[i] += this._param[i] | |
// store keystream | |
for (j = 0; j < 4; j++) { | |
this._keystream[b++] = (mix[i] >>> (j << 3)) // & 0xFF | |
} | |
// this._keystream[b++] = mix[i] & 0xFF | |
// this._keystream[b++] = (mix[i] >>> 8) & 0xFF | |
// this._keystream[b++] = (mix[i] >>> 16) & 0xFF | |
// this._keystream[b++] = (mix[i] >>> 24) & 0xFF | |
} | |
} | |
/** | |
* Encrypt or Decrypt data with key and nonce | |
* | |
* @param {Uint8Array} data | |
* @return {Uint8Array} | |
*/ | |
_update(data) { | |
if (!(data instanceof Uint8Array) || data.length === 0) { | |
throw new Error('Data should be Uint8Array and not empty!') | |
} | |
const output = new Uint8Array(data.length) | |
// core function, build block and xor with input data // | |
for (let i = 0; i < data.length; i++) { | |
if (this._byteCounter === 0 || this._byteCounter === 64) { | |
// generate new block // | |
this._chacha() | |
// counter increment // | |
this._param[12]++ | |
// reset internal counter // | |
this._byteCounter = 0 | |
} | |
output[i] = data[i] ^ this._keystream[this._byteCounter++] | |
} | |
return output | |
} | |
/** | |
* Encrypt data with key and nonce | |
* | |
* @param {Uint8Array} data | |
* @return {Uint8Array} | |
*/ | |
'encrypt'(data) { | |
return this._update(data); | |
} | |
/** | |
* Decrypt data with key and nonce | |
* | |
* @param {Uint8Array} data | |
* @return {Uint8Array} | |
*/ | |
'decrypt'(data) { | |
return this._update(data); | |
} | |
} | |
class Poly1305 { | |
/** | |
* @param {Uint8Array} key | |
*/ | |
constructor(key) { | |
this.b = new Uint8Array(16); | |
this.l = 0; | |
this.h = new Uint16Array(10); | |
this.p = new Uint16Array(8); | |
this.f = false; | |
const t = new Uint16Array(8); | |
let i; | |
for (i = 8; i--;) t[i] = _get16(key, i << 1); | |
this.r = new Uint16Array([ | |
t[0] & 0x1fff, | |
((t[0] >>> 13) | (t[1] << 3)) & 0x1fff, | |
((t[1] >>> 10) | (t[2] << 6)) & 0x1f03, | |
((t[2] >>> 7) | (t[3] << 9)) & 0x1fff, | |
((t[3] >>> 4) | (t[4] << 12)) & 0x00ff, | |
(t[4] >>> 1) & 0x1ffe, | |
((t[4] >>> 14) | (t[5] << 2)) & 0x1fff, | |
((t[5] >>> 11) | (t[6] << 5)) & 0x1f81, | |
((t[6] >>> 8) | (t[7] << 8)) & 0x1fff, | |
(t[7] >>> 5) & 0x007f, | |
]); | |
for (i = 8; i--;) { | |
// this.p[i] = _get16(key, 16 + (i << 1)); | |
this.p[i] = _get16(key, (i << 1) | 16); | |
} | |
this.l = 0; | |
} | |
/** | |
* @param {Uint8Array} m | |
* @param {Number} mpos | |
* @param {Number} bytes | |
*/ | |
blocks(m, mpos, bytes) { | |
let hibit = this.f ? 0 : (1 << 11); | |
const t = new Uint16Array(8); | |
const d = new Uint32Array(10); | |
const {h, r} = this; | |
let c = 0, i = 0, j = 0; | |
while (bytes >= 16) { | |
for (i = 8; i--;) t[i] = _get16(m, (i << 1) + mpos); | |
h[0] += t[0] & 0x1FFF; | |
h[1] += ((t[0] >>> 13) | (t[1] << 3)) & 0x1FFF; | |
h[2] += ((t[1] >>> 10) | (t[2] << 6)) & 0x1FFF; | |
h[3] += ((t[2] >>> 7) | (t[3] << 9)) & 0x1FFF; | |
h[4] += ((t[3] >>> 4) | (t[4] << 12)) & 0x1FFF; | |
h[5] += (t[4] >>> 1) & 0x1FFF; | |
h[6] += ((t[4] >>> 14) | (t[5] << 2)) & 0x1FFF; | |
h[7] += ((t[5] >>> 11) | (t[6] << 5)) & 0x1FFF; | |
h[8] += ((t[6] >>> 8) | (t[7] << 8)) & 0x1FFF; | |
h[9] += (t[7] >>> 5) | hibit; | |
for (i = 0, c = 0; i < 10; i++) { | |
d[i] = c; | |
for (j = 0; j < 10; j++) { | |
d[i] += (h[j] & 0xFFFFFFFF) * ((j <= i) ? r[i - j] : (5 * r[i + 10 - j])); | |
if (j === 4) { | |
c = d[i] >>> 13; | |
d[i] &= 0x1FFF; | |
} | |
} | |
c += d[i] >>> 13; | |
d[i] &= 0x1FFF; | |
} | |
c = (c << 2) + c; | |
c += d[0]; | |
d[0] = c & 0x1FFF; | |
c >>>= 13; | |
d[1] += c; | |
h.set(d); | |
// for (i = 10; i--;) h[i] = d[i]; | |
mpos += 16; | |
bytes -= 16; | |
} | |
} | |
/** | |
* @param {Uint8Array} m | |
*/ | |
'update'(m) { | |
let want = 0, i = 0, mpos = 0, bytes = m.length; | |
const {b} = this; | |
if (this.l) { | |
want = 16 - this.l; | |
if (want > bytes) | |
want = bytes; | |
for (i = want; i--;) { | |
b[this.l+i] = m[i+mpos]; | |
} | |
bytes -= want; | |
mpos += want; | |
this.l += want; | |
if (this.l < 16) | |
return; | |
this.blocks(b, 0, 16); | |
this.l = 0; | |
} | |
if (bytes >= 16) { | |
want = (bytes & -16); | |
this.blocks(m, mpos, want); | |
mpos += want; | |
bytes -= want; | |
} | |
if (bytes) { | |
b.set(m.subarray(mpos, mpos + bytes), this.l); | |
// for (i = bytes; i--;) { | |
// b[this.l+i] = m[i+mpos]; | |
// } | |
this.l += bytes; | |
} | |
} | |
/** | |
* @returns {Uint8Array} | |
*/ | |
'finish'() { | |
const mac = new Uint8Array(16); | |
const g = new Uint16Array(10); | |
const {b, h} = this; | |
let c = 0, mask = 0, f = 0, i = 0; | |
if (this.l) { | |
i = this.l; | |
b[i++] = 1; | |
for (; i < 16; i++) { | |
b[i] = 0; | |
} | |
this.f = true; | |
this.blocks(b, 0, 16); | |
} | |
c = h[1] >>> 13; | |
h[1] &= 0x1FFF; | |
for (i = 2; i < 10; i++) { | |
h[i] += c; | |
c = h[i] >>> 13; | |
h[i] &= 0x1FFF; | |
} | |
h[0] += c * 5; | |
c = h[0] >>> 13; | |
h[0] &= 0x1FFF; | |
h[1] += c; | |
c = h[1] >>> 13; | |
h[1] &= 0x1FFF; | |
h[2] += c; | |
g[0] = h[0] + 5; | |
c = g[0] >>> 13; | |
g[0] &= 0x1FFF; | |
for (i = 1; i < 10; i++) { | |
g[i] = h[i] + c; | |
c = g[i] >>> 13; | |
g[i] &= 0x1FFF; | |
} | |
g[9] -= 1 << 13; | |
mask = (g[9] >>> 15) - 1; | |
for (i = 10; i--;) g[i] &= mask; | |
mask = ~mask; | |
for (i = 10; i--;) { | |
h[i] = (h[i] & mask) | g[i]; | |
} | |
h[0] = (h[0] ) | (h[1] << 13); | |
h[1] = (h[1] >> 3) | (h[2] << 10); | |
h[2] = (h[2] >> 6) | (h[3] << 7); | |
h[3] = (h[3] >> 9) | (h[4] << 4); | |
h[4] = (h[4] >> 12) | (h[5] << 1) | (h[6] << 14); | |
h[5] = (h[6] >> 2) | (h[7] << 11); | |
h[6] = (h[7] >> 5) | (h[8] << 8); | |
h[7] = (h[8] >> 8) | (h[9] << 5); | |
f = (h[0] & 0xFFFFFFFF) + this.p[0]; | |
h[0] = f; | |
for (i = 1; i < 8; i++) { | |
f = (h[i] & 0xFFFFFFFF) + this.p[i] + (f >>> 16); | |
h[i] = f; | |
} | |
for (i = 8; i--;) { | |
mac[i << 1] = h[i]; | |
mac[(i << 1) + 1] = h[i] >>> 8; | |
} | |
return mac; | |
} | |
} | |
class ChaCha20Poly1305 { | |
/** | |
* @param {Uint8Array} key | |
* @param {Uint8Array} nonce | |
* @param {Uint8Array} auth | |
*/ | |
constructor(key, nonce, auth) { | |
if (!(auth instanceof Uint8Array) || auth.length === 0) { | |
throw new Error('Auth should be Uint8Array and not empty!'); | |
} | |
this.chacha20 = new ChaCha20(key, nonce); | |
this.poly1305 = new Poly1305(this.chacha20['encrypt'](new Uint8Array(64))); | |
this.authLength = auth.length; | |
this.dataLength = 0; | |
this.poly1305['update'](auth); | |
if (16 - auth.length & 15) { | |
this.poly1305['update'](new Uint8Array(16 - auth.length & 15)); | |
} | |
} | |
/** | |
* @param {Uint8Array} data | |
* @returns {Uint8Array} | |
*/ | |
'encrypt'(data) { | |
const cipher = this.chacha20['encrypt'](data); | |
this.dataLength += cipher.length; | |
this.poly1305['update'](cipher); | |
return cipher; | |
} | |
/** | |
* @param {Uint8Array} data | |
* @returns {Uint8Array} | |
*/ | |
'decrypt'(data) { | |
this.poly1305['update'](data); | |
const plain = this.chacha20['decrypt'](data); | |
this.dataLength += plain.length; | |
return plain; | |
} | |
/** | |
* @returns {Uint8Array} | |
*/ | |
'mac'() { | |
if (16 - this.dataLength & 15) { | |
this.poly1305['update'](new Uint8Array(16 - this.dataLength & 15)); | |
} | |
// Uint32 only. Javascript cannot handle Uint64. | |
this.poly1305['update'](new Uint8Array(new Uint32Array([this.authLength, 0, this.dataLength, 0]).buffer)); | |
return this.poly1305['finish'](); | |
} | |
/** | |
* @param {Uint8Array} mac | |
* @returns {Boolean} | |
*/ | |
'verify'(mac) { | |
const m = this['mac'](); | |
let i = 16; | |
let result = 0; | |
while (i--) result |= m[i] ^ mac[i]; | |
return !result; | |
} | |
} | |
// EXPORT // | |
if (typeof module !== 'undefined') { | |
module.exports = { | |
'ChaCha20': ChaCha20, | |
'ChaCha20Poly1305': ChaCha20Poly1305, | |
}; | |
} else { | |
GLOBAL['ChaCha20'] = ChaCha20; | |
GLOBAL['ChaCha20Poly1305'] = ChaCha20Poly1305; | |
} | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(()=>{const t="undefined"!=typeof globalThis?globalThis:global||self,{Error:s,Uint8Array:i,Uint16Array:h,Uint32Array:e}=t,r=(t,s,i,h,e)=>{t[e]=f(t[e]^(t[s]+=t[i]),16),t[i]=f(t[i]^(t[h]+=t[e]),12),t[e]=f(t[e]^(t[s]+=t[i]),8),t[i]=f(t[i]^(t[h]+=t[e]),7)},n=(t,s)=>t[s++]|t[s++]<<8|t[s++]<<16|t[s]<<24,o=(t,s)=>t[s++]|t[s]<<8,f=(t,s)=>t<<s|t>>>32-s;class c{constructor(t,h,r=0){if(!(t instanceof i)||32!==t.length)throw new s("Key should be 32 byte array!");if(!(h instanceof i)||12!==h.length)throw new s("Nonce should be 12 byte array!");this.t=20;const o=new e(16);o.set(new e([1634760805,857760878,2036477234,1797285236])),o[12]=r;let f=8;for(;f--;)o[f+4]=n(t,f<<2);for(f=3;f--;)o[f+13]=n(h,f<<2);this.i=o,this.h=new i(64),this.o=0}l(){const t=new e(this.i);let s=0,i=0;for(s=0;s<this.t;s+=2){for(i=0;i<4;i++)r(t,i,4|i,8|i,12|i);for(i=0;i<4;i++)r(t,i,i+1&3|4,i+2&3|8,i+3&3|12)}let h=0;for(s=0;s<16;s++)for(t[s]+=this.i[s],i=0;i<4;i++)this.h[h++]=t[s]>>>(i<<3)}u(t){if(!(t instanceof i)||0===t.length)throw new s("Data should be Uint8Array and not empty!");const h=new i(t.length);for(let s=0;s<t.length;s++)0!==this.o&&64!==this.o||(this.l(),this.i[12]++,this.o=0),h[s]=t[s]^this.h[this.o++];return h}encrypt(t){return this.u(t)}decrypt(t){return this.u(t)}}class a{constructor(t){this.b=new i(16),this.p=0,this.A=new h(10),this.m=new h(8),this.f=!1;const s=new h(8);let e;for(e=8;e--;)s[e]=o(t,e<<1);for(this.r=new h([8191&s[0],8191&(s[0]>>>13|s[1]<<3),7939&(s[1]>>>10|s[2]<<6),8191&(s[2]>>>7|s[3]<<9),255&(s[3]>>>4|s[4]<<12),s[4]>>>1&8190,8191&(s[4]>>>14|s[5]<<2),8065&(s[5]>>>11|s[6]<<5),8191&(s[6]>>>8|s[7]<<8),s[7]>>>5&127]),e=8;e--;)this.m[e]=o(t,e<<1|16);this.p=0}U(t,s,i){let r=this.f?0:2048;const n=new h(8),f=new e(10),{A:c,r:a}=this;let w=0,l=0,u=0;for(;i>=16;){for(l=8;l--;)n[l]=o(t,(l<<1)+s);for(c[0]+=8191&n[0],c[1]+=8191&(n[0]>>>13|n[1]<<3),c[2]+=8191&(n[1]>>>10|n[2]<<6),c[3]+=8191&(n[2]>>>7|n[3]<<9),c[4]+=8191&(n[3]>>>4|n[4]<<12),c[5]+=n[4]>>>1&8191,c[6]+=8191&(n[4]>>>14|n[5]<<2),c[7]+=8191&(n[5]>>>11|n[6]<<5),c[8]+=8191&(n[6]>>>8|n[7]<<8),c[9]+=n[7]>>>5|r,l=0,w=0;l<10;l++){for(f[l]=w,u=0;u<10;u++)f[l]+=(4294967295&c[u])*(u<=l?a[l-u]:5*a[l+10-u]),4===u&&(w=f[l]>>>13,f[l]&=8191);w+=f[l]>>>13,f[l]&=8191}w=(w<<2)+w,w+=f[0],f[0]=8191&w,w>>>=13,f[1]+=w,c.set(f),s+=16,i-=16}}update(t){let s=0,i=0,h=0,e=t.length;const{b:r}=this;if(this.p){for(s=16-this.p,s>e&&(s=e),i=s;i--;)r[this.p+i]=t[i+h];if(e-=s,h+=s,this.p+=s,this.p<16)return;this.U(r,0,16),this.p=0}e>=16&&(s=-16&e,this.U(t,h,s),h+=s,e-=s),e&&(r.set(t.subarray(h,h+e),this.p),this.p+=e)}finish(){const t=new i(16),s=new h(10),{b:e,A:r}=this;let n=0,o=0,f=0,c=0;if(this.p){for(c=this.p,e[c++]=1;c<16;c++)e[c]=0;this.f=!0,this.U(e,0,16)}for(n=r[1]>>>13,r[1]&=8191,c=2;c<10;c++)r[c]+=n,n=r[c]>>>13,r[c]&=8191;for(r[0]+=5*n,n=r[0]>>>13,r[0]&=8191,r[1]+=n,n=r[1]>>>13,r[1]&=8191,r[2]+=n,s[0]=r[0]+5,n=s[0]>>>13,s[0]&=8191,c=1;c<10;c++)s[c]=r[c]+n,n=s[c]>>>13,s[c]&=8191;for(s[9]-=8192,o=(s[9]>>>15)-1,c=10;c--;)s[c]&=o;for(o=~o,c=10;c--;)r[c]=r[c]&o|s[c];for(r[0]=r[0]|r[1]<<13,r[1]=r[1]>>3|r[2]<<10,r[2]=r[2]>>6|r[3]<<7,r[3]=r[3]>>9|r[4]<<4,r[4]=r[4]>>12|r[5]<<1|r[6]<<14,r[5]=r[6]>>2|r[7]<<11,r[6]=r[7]>>5|r[8]<<8,r[7]=r[8]>>8|r[9]<<5,f=(4294967295&r[0])+this.m[0],r[0]=f,c=1;c<8;c++)f=(4294967295&r[c])+this.m[c]+(f>>>16),r[c]=f;for(c=8;c--;)t[c<<1]=r[c],t[1+(c<<1)]=r[c]>>>8;return t}}class w{constructor(t,h,e){if(!(e instanceof i)||0===e.length)throw new s("Auth should be Uint8Array and not empty!");this.C=new c(t,h),this.g=new a(this.C.encrypt(new i(64))),this.T=e.length,this._=0,this.g.update(e),16-e.length&15&&this.g.update(new i(16-e.length&15))}encrypt(t){const s=this.C.encrypt(t);return this._+=s.length,this.g.update(s),s}decrypt(t){this.g.update(t);const s=this.C.decrypt(t);return this._+=s.length,s}mac(){return 16-this._&15&&this.g.update(new i(16-this._&15)),this.g.update(new i(new e([this.T,0,this._,0]).buffer)),this.g.finish()}verify(t){const s=this.mac();let i=16,h=0;for(;i--;)h|=s[i]^t[i];return!h}}"undefined"!=typeof module?module.exports={ChaCha20:c,ChaCha20Poly1305:w}:(t.ChaCha20=c,t.ChaCha20Poly1305=w)})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment