I've spent an embarrasing number of hours trying to get Libsodium.js to work.
See my fiddle demo (and code pasted below too).
I keep getting Error: wrong secret key for the given ciphertext
.
What I would prefer is to replicate this PHP example of function simpleEncrypt($message, $key)
into Libsodium.js.
But as a starter, I'd be happy even getting the basic sample from the Libsodium.js repo to work.
Any hints?
Here is the code (also shown in the working fiddle):
const _sodium = require("libsodium-wrappers");
const concatTypedArray = require("concat-typed-array");
(async () => {
await _sodium.ready;
const sodium = _sodium;
const utf8 = "utf-8";
const td = new TextDecoder(utf8);
const te = new TextEncoder(utf8);
const nonceBytes = sodium.crypto_secretbox_NONCEBYTES;
const macBytes = sodium.crypto_secretbox_MACBYTES;
let key = sodium.from_hex("724b092810ec86d7e35c9d067702b31ef90bc43a7b598626749914d6a3e033ed");
function encrypt_and_prepend_nonce(message, key) {
let nonce = sodium.randombytes_buf(nonceBytes);
var encrypted = sodium.crypto_secretbox_easy(message, nonce, key);
var combined2 = concatTypedArray(Uint8Array, nonce, encrypted);
return combined2;
}
function decrypt_after_extracting_nonce(nonce_and_ciphertext, key) {
if (nonce_and_ciphertext.length < nonceBytes + macBytes) {
throw "Short message";
}
let nonce = nonce_and_ciphertext.slice(0, nonceBytes);
let ciphertext = nonce_and_ciphertext.slice(nonceBytes);
return sodium.crypto_secretbox_open_easy(ciphertext, nonce, key);
}
function encrypt(message, key) {
var x = encrypt_and_prepend_nonce(message, key);
return td.decode(x);
}
function decrypt(nonce_and_ciphertext_str, key) {
var nonce_and_ciphertext = te.encode(nonce_and_ciphertext_str);
return decrypt_after_extracting_nonce(nonce_and_ciphertext, key);
}
var inputStr = "shhh this is a secret";
var garbledStr = encrypt(inputStr, key);
try {
var decryptedStr = decrypt(garbledStr, key);
console.log("Recovered input string:", decryptedStr);
console.log("Check whether the following text matches the original:", decryptedStr === inputStr);
} catch (e) {
console.error(e);
}
})();
The parts that really helped me were:
function u_atob(ascii)
for Uint8Array)const concatTypedArray = require("concat-typed-array");
require("babel-core/register");
and require("babel-polyfill");
, which I still don't understand (https://stackoverflow.com/a/33527883/470749)Here is the working fiddle sandbox.
And in case that ever disappears, here are the important parts:
const nonceBytes = sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES;
let key = sodium.from_hex("724b092810ec86d7e35c9d067702b31ef90bc43a7b598626749914d6a3e033ed");
var nonceTest;
/**
* @param {string} message
* @param {string} key
* @returns {Uint8Array}
*/
function encrypt_and_prepend_nonce(message, key) {
let nonce = sodium.randombytes_buf(nonceBytes);
nonceTest = nonce.toString();
var encrypted = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(message, null, nonce, nonce, key);
var nonce_and_ciphertext = concatTypedArray(Uint8Array, nonce, encrypted); //https://github.com/jedisct1/libsodium.js/issues/130#issuecomment-361399594
return nonce_and_ciphertext;
}
/**
* @param {Uint8Array} nonce_and_ciphertext
* @param {string} key
* @returns {string}
*/
function decrypt_after_extracting_nonce(nonce_and_ciphertext, key) {
let nonce = nonce_and_ciphertext.slice(0, nonceBytes); //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/slice
let ciphertext = nonce_and_ciphertext.slice(nonceBytes);
var result = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(nonce, ciphertext, null, nonce, key, "text");
return result;
}
/**
* @param {string} message
* @param {string} key
* @returns {string}
*/
function encrypt(message, key) {
var uint8ArrayMsg = encrypt_and_prepend_nonce(message, key);
return u_btoa(uint8ArrayMsg); //returns ascii string of garbled text
}
/**
* @param {string} nonce_and_ciphertext_str
* @param {string} key
* @returns {string}
*/
function decrypt(nonce_and_ciphertext_str, key) {
var nonce_and_ciphertext = u_atob(nonce_and_ciphertext_str); //converts ascii string of garbled text into binary
return decrypt_after_extracting_nonce(nonce_and_ciphertext, key);
}
function u_atob(ascii) { //https://stackoverflow.com/a/43271130/
return Uint8Array.from(atob(ascii), c => c.charCodeAt(0));
}
function u_btoa(buffer) { //https://stackoverflow.com/a/43271130/
var binary = [];
var bytes = new Uint8Array(buffer);
for (var i = 0, il = bytes.byteLength; i < il; i++) {
binary.push(String.fromCharCode(bytes[i]));
}
return btoa(binary.join(""));
}