Last active
July 22, 2024 20:00
-
-
Save dchest/4582374 to your computer and use it in GitHub Desktop.
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
/* | |
IMPORTANT!!! DO NOT USE THIS. It works, but you'll probably get it wrong, | |
because it must be keyed with at least 128 bits of entropy, and where | |
do you get this entropy, huh? | |
- In a browser, you get it from window.crypto.getRandomValues(). | |
- In Node, you get it from crypto.randomBytes() | |
Now LOOK AT YOU! You already have secure ways to generate random bytes, | |
WHY THE HELL YOU NEED THIS Salsa20 generator? USE window.crypto.getRandomValues() | |
or crypto.randomBytes() DIRECTLY, OK?! | |
(AND NO, DO NOT, I REPEAT, DO **NOT** USE IT WITH Math.random OR PEOPLE WILL DIE) | |
*/ | |
var Salsa20 = (function () { | |
function Salsa20(key, nonce) { | |
// Constants. | |
this.rounds = 20; // number of Salsa rounds | |
this.sigmaWords = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]; | |
// State. | |
this.keyWords = []; // key words | |
this.nonceWords = [0, 0]; // nonce words | |
this.counterWords = [0, 0]; // block counter words | |
// Output buffer. | |
this.block = []; // output block of 64 bytes | |
this.blockUsed = 64; // number of block bytes used | |
this.setKey(key); | |
this.setNonce(nonce); | |
} | |
// setKey sets the key to the given 32-byte array. | |
Salsa20.prototype.setKey = function(key) { | |
for (var i = 0, j = 0; i < 8; i++, j += 4) { | |
this.keyWords[i] = (key[j] & 0xff) | | |
((key[j+1] & 0xff)<<8) | | |
((key[j+2] & 0xff)<<16) | | |
((key[j+3] & 0xff)<<24); | |
} | |
this._reset(); | |
}; | |
// setNonce sets the nonce to the given 8-byte array. | |
Salsa20.prototype.setNonce = function(nonce) { | |
this.nonceWords[0] = (nonce[0] & 0xff) | | |
((nonce[1] & 0xff)<<8) | | |
((nonce[2] & 0xff)<<16) | | |
((nonce[3] & 0xff)<<24); | |
this.nonceWords[1] = (nonce[4] & 0xff) | | |
((nonce[5] & 0xff)<<8) | | |
((nonce[6] & 0xff)<<16) | | |
((nonce[7] & 0xff)<<24); | |
this._reset(); | |
}; | |
// getBytes returns the next numberOfBytes bytes of stream. | |
Salsa20.prototype.getBytes = function(numberOfBytes) { | |
var out = new Array(numberOfBytes); | |
for (var i = 0; i < numberOfBytes; i++) { | |
if (this.blockUsed == 64) { | |
this._generateBlock(); | |
this._incrementCounter(); | |
this.blockUsed = 0; | |
} | |
out[i] = this.block[this.blockUsed]; | |
this.blockUsed++; | |
} | |
return out; | |
}; | |
Salsa20.prototype.getHexString = function(numberOfBytes) { | |
var hex=['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']; | |
var out = []; | |
var bytes = this.getBytes(numberOfBytes); | |
for(var i = 0; i < bytes.length; i++) { | |
out.push(hex[(bytes[i] >> 4) & 15]); | |
out.push(hex[bytes[i] & 15]); | |
} | |
return out.join(''); | |
}; | |
// Private methods. | |
Salsa20.prototype._reset = function() { | |
this.counterWords[0] = 0; | |
this.counterWords[1] = 0; | |
this.blockUsed = 64; | |
}; | |
// _incrementCounter increments block counter. | |
Salsa20.prototype._incrementCounter = function() { | |
// Note: maximum 2^64 blocks. | |
this.counterWords[0] = (this.counterWords[0] + 1) & 0xffffffff; | |
if (this.counterWords[0] == 0) { | |
this.counterWords[1] = (this.counterWords[1] + 1) & 0xffffffff; | |
} | |
}; | |
// _generateBlock generates 64 bytes from key, nonce, and counter, | |
// and puts the result into this.block. | |
Salsa20.prototype._generateBlock = function() { | |
var j0 = this.sigmaWords[0], | |
j1 = this.keyWords[0], | |
j2 = this.keyWords[1], | |
j3 = this.keyWords[2], | |
j4 = this.keyWords[3], | |
j5 = this.sigmaWords[1], | |
j6 = this.nonceWords[0], | |
j7 = this.nonceWords[1], | |
j8 = this.counterWords[0], | |
j9 = this.counterWords[1], | |
j10 = this.sigmaWords[2], | |
j11 = this.keyWords[4], | |
j12 = this.keyWords[5], | |
j13 = this.keyWords[6], | |
j14 = this.keyWords[7], | |
j15 = this.sigmaWords[3]; | |
var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, | |
x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, x15 = j15; | |
var u; | |
for (var i = 0; i < this.rounds; i += 2) { | |
u = x0 + x12; | |
x4 ^= (u<<7) | (u>>>(32-7)); | |
u = x4 + x0; | |
x8 ^= (u<<9) | (u>>>(32-9)); | |
u = x8 + x4; | |
x12 ^= (u<<13) | (u>>>(32-13)); | |
u = x12 + x8; | |
x0 ^= (u<<18) | (u>>>(32-18)); | |
u = x5 + x1; | |
x9 ^= (u<<7) | (u>>>(32-7)); | |
u = x9 + x5; | |
x13 ^= (u<<9) | (u>>>(32-9)); | |
u = x13 + x9; | |
x1 ^= (u<<13) | (u>>>(32-13)); | |
u = x1 + x13; | |
x5 ^= (u<<18) | (u>>>(32-18)); | |
u = x10 + x6; | |
x14 ^= (u<<7) | (u>>>(32-7)); | |
u = x14 + x10; | |
x2 ^= (u<<9) | (u>>>(32-9)); | |
u = x2 + x14; | |
x6 ^= (u<<13) | (u>>>(32-13)); | |
u = x6 + x2; | |
x10 ^= (u<<18) | (u>>>(32-18)); | |
u = x15 + x11; | |
x3 ^= (u<<7) | (u>>>(32-7)); | |
u = x3 + x15; | |
x7 ^= (u<<9) | (u>>>(32-9)); | |
u = x7 + x3; | |
x11 ^= (u<<13) | (u>>>(32-13)); | |
u = x11 + x7; | |
x15 ^= (u<<18) | (u>>>(32-18)); | |
u = x0 + x3; | |
x1 ^= (u<<7) | (u>>>(32-7)); | |
u = x1 + x0; | |
x2 ^= (u<<9) | (u>>>(32-9)); | |
u = x2 + x1; | |
x3 ^= (u<<13) | (u>>>(32-13)); | |
u = x3 + x2; | |
x0 ^= (u<<18) | (u>>>(32-18)); | |
u = x5 + x4; | |
x6 ^= (u<<7) | (u>>>(32-7)); | |
u = x6 + x5; | |
x7 ^= (u<<9) | (u>>>(32-9)); | |
u = x7 + x6; | |
x4 ^= (u<<13) | (u>>>(32-13)); | |
u = x4 + x7; | |
x5 ^= (u<<18) | (u>>>(32-18)); | |
u = x10 + x9; | |
x11 ^= (u<<7) | (u>>>(32-7)); | |
u = x11 + x10; | |
x8 ^= (u<<9) | (u>>>(32-9)); | |
u = x8 + x11; | |
x9 ^= (u<<13) | (u>>>(32-13)); | |
u = x9 + x8; | |
x10 ^= (u<<18) | (u>>>(32-18)); | |
u = x15 + x14; | |
x12 ^= (u<<7) | (u>>>(32-7)); | |
u = x12 + x15; | |
x13 ^= (u<<9) | (u>>>(32-9)); | |
u = x13 + x12; | |
x14 ^= (u<<13) | (u>>>(32-13)); | |
u = x14 + x13; | |
x15 ^= (u<<18) | (u>>>(32-18)); | |
} | |
x0 += j0; | |
x1 += j1; | |
x2 += j2; | |
x3 += j3; | |
x4 += j4; | |
x5 += j5; | |
x6 += j6; | |
x7 += j7; | |
x8 += j8; | |
x9 += j9; | |
x10 += j10; | |
x11 += j11; | |
x12 += j12; | |
x13 += j13; | |
x14 += j14; | |
x15 += j15; | |
this.block[ 0] = ( x0 >>> 0) & 0xff; this.block[ 1] = ( x0 >>> 8) & 0xff; | |
this.block[ 2] = ( x0 >>> 16) & 0xff; this.block[ 3] = ( x0 >>> 24) & 0xff; | |
this.block[ 4] = ( x1 >>> 0) & 0xff; this.block[ 5] = ( x1 >>> 8) & 0xff; | |
this.block[ 6] = ( x1 >>> 16) & 0xff; this.block[ 7] = ( x1 >>> 24) & 0xff; | |
this.block[ 8] = ( x2 >>> 0) & 0xff; this.block[ 9] = ( x2 >>> 8) & 0xff; | |
this.block[10] = ( x2 >>> 16) & 0xff; this.block[11] = ( x2 >>> 24) & 0xff; | |
this.block[12] = ( x3 >>> 0) & 0xff; this.block[13] = ( x3 >>> 8) & 0xff; | |
this.block[14] = ( x3 >>> 16) & 0xff; this.block[15] = ( x3 >>> 24) & 0xff; | |
this.block[16] = ( x4 >>> 0) & 0xff; this.block[17] = ( x4 >>> 8) & 0xff; | |
this.block[18] = ( x4 >>> 16) & 0xff; this.block[19] = ( x4 >>> 24) & 0xff; | |
this.block[20] = ( x5 >>> 0) & 0xff; this.block[21] = ( x5 >>> 8) & 0xff; | |
this.block[22] = ( x5 >>> 16) & 0xff; this.block[23] = ( x5 >>> 24) & 0xff; | |
this.block[24] = ( x6 >>> 0) & 0xff; this.block[25] = ( x6 >>> 8) & 0xff; | |
this.block[26] = ( x6 >>> 16) & 0xff; this.block[27] = ( x6 >>> 24) & 0xff; | |
this.block[28] = ( x7 >>> 0) & 0xff; this.block[29] = ( x7 >>> 8) & 0xff; | |
this.block[30] = ( x7 >>> 16) & 0xff; this.block[31] = ( x7 >>> 24) & 0xff; | |
this.block[32] = ( x8 >>> 0) & 0xff; this.block[33] = ( x8 >>> 8) & 0xff; | |
this.block[34] = ( x8 >>> 16) & 0xff; this.block[35] = ( x8 >>> 24) & 0xff; | |
this.block[36] = ( x9 >>> 0) & 0xff; this.block[37] = ( x9 >>> 8) & 0xff; | |
this.block[38] = ( x9 >>> 16) & 0xff; this.block[39] = ( x9 >>> 24) & 0xff; | |
this.block[40] = (x10 >>> 0) & 0xff; this.block[41] = (x10 >>> 8) & 0xff; | |
this.block[42] = (x10 >>> 16) & 0xff; this.block[43] = (x10 >>> 24) & 0xff; | |
this.block[44] = (x11 >>> 0) & 0xff; this.block[45] = (x11 >>> 8) & 0xff; | |
this.block[46] = (x11 >>> 16) & 0xff; this.block[47] = (x11 >>> 24) & 0xff; | |
this.block[48] = (x12 >>> 0) & 0xff; this.block[49] = (x12 >>> 8) & 0xff; | |
this.block[50] = (x12 >>> 16) & 0xff; this.block[51] = (x12 >>> 24) & 0xff; | |
this.block[52] = (x13 >>> 0) & 0xff; this.block[53] = (x13 >>> 8) & 0xff; | |
this.block[54] = (x13 >>> 16) & 0xff; this.block[55] = (x13 >>> 24) & 0xff; | |
this.block[56] = (x14 >>> 0) & 0xff; this.block[57] = (x14 >>> 8) & 0xff; | |
this.block[58] = (x14 >>> 16) & 0xff; this.block[59] = (x14 >>> 24) & 0xff; | |
this.block[60] = (x15 >>> 0) & 0xff; this.block[61] = (x15 >>> 8) & 0xff; | |
this.block[62] = (x15 >>> 16) & 0xff; this.block[63] = (x15 >>> 24) & 0xff; | |
}; | |
return Salsa20; | |
})(); | |
// ---------- Test ------------- | |
var key = [0x80]; for (i = 1; i < 32; i++) key[i] = 0; | |
var nonce = []; for (i = 0; i < 8; i++) nonce[i] = 0; | |
var good = [ | |
// 0..63 | |
"e3be8fdd8beca2e3ea8ef9475b29a6e7" + | |
"003951e1097a5c38d23b7a5fad9f6844" + | |
"b22c97559e2723c7cbbd3fe4fc8d9a07" + | |
"44652a83e72a9c461876af4d7ef1a117", | |
// 192..255 | |
"57be81f47b17d9ae7c4ff15429a73e10" + | |
"acf250ed3a90a93c711308a74c6216a9" + | |
"ed84cd126da7f28e8abf8bb63517e1ca" + | |
"98e712f4fb2e1a6aed9fdc73291faa17", | |
// 256..319 | |
"958211c4ba2ebd5838c635edb81f513a" + | |
"91a294e194f1c039aeec657dce40aa7e" + | |
"7c0af57cacefa40c9f14b71a4b3456a6" + | |
"3e162ec7d8d10b8ffb1810d71001b618", | |
// 448..511 | |
"696afcfd0cddcc83c7e77f11a649d79a" + | |
"cdc3354e9635ff137e929933a0bd6f53" + | |
"77efa105a3a4266b7c0d089d08f1e855" + | |
"cc32b15b93784a36e56a76cc64bc8477" | |
]; | |
var state = new Salsa20(key, nonce); | |
// compare 0..63 | |
if (state.getHexString(64) != good[0]) | |
console.log("BAD 0..63"); | |
// discard 64..191 | |
state.getBytes(128); | |
// compare 192..255 | |
if (state.getHexString(64) != good[1]) | |
console.log("BAD 192..255"); | |
// compare 256..319 | |
if (state.getHexString(64) != good[2]) | |
console.log("BAD 256..319"); | |
// discard 320..447 | |
state.getBytes(128); | |
// compare 448..511 | |
if (state.getHexString(64) != good[3]) | |
console.log("BAD 448..511"); | |
console.log("done"); |
Hi @DukeyToo can you tell me how to use Salsa20.encrypt()
? I have tried Salsa20.encrypt("hello")
but console outputs TypeError: Salsa20.encrypt is not a function
. Thanks
@dchest What is the license for this? Is it public domain?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If someone wants to use this to decrypt/encrypt data, the following might help: