Skip to content

Commit

Permalink
Consume AES-GCM from HACL* WASM. Remove old API.
Browse files Browse the repository at this point in the history
  • Loading branch information
msprotz committed Jun 27, 2024
1 parent 59f2b3e commit a710ef5
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 199 deletions.
4 changes: 4 additions & 0 deletions js/import.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ HACL_MODULES=(
Hacl_Chacha20_Vec32
Hacl_MAC_Poly1305
Hacl_AEAD_Chacha20Poly1305
Hacl_Lib
Hacl_AES_128_CTR32_BitSlice
Hacl_Gf128_CT64
Hacl_AES_128_GCM_CT64
)

for m in ${HACL_MODULES[@]}; do
Expand Down
167 changes: 48 additions & 119 deletions js/index.js
Original file line number Diff line number Diff line change
@@ -1,103 +1,14 @@
var debug = true;

////////////////////////////////////////////////////////////////////////////////
// OLD MLS Lib //
////////////////////////////////////////////////////////////////////////////////

let freshKeyPairDebug = 0;

function freshKeyPair() {
let dummy32s = [
new Uint8Array([32, 253, 20, 170, 202, 227, 238, 210, 27, 101, 42, 60, 111, 102, 230, 67, 215, 226, 241, 122, 209, 115, 47, 6, 56, 238, 190, 255, 224, 93, 78, 19]),
new Uint8Array([35, 90, 128, 221, 81, 223, 92, 59, 75, 242, 32, 175, 171, 153, 103, 118, 79, 18, 173, 160, 6, 102, 242, 54, 173, 120, 38, 90, 24, 142, 151, 198]),
new Uint8Array([156, 11, 245, 228, 136, 5, 116, 12, 190, 194, 163, 35, 133, 176, 85, 85, 181, 16, 7, 77, 225, 131, 43, 71, 252, 151, 14, 126, 17, 132, 152, 31])
];
let random32 = crypto.getRandomValues(new Uint8Array(32));
return MLS.freshKeyPair1(dummy32s[freshKeyPairDebug++%3]);
}

function freshKeyPackage(cred, priv) {
let dummy64 = new Uint8Array([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
]);
let random64 = crypto.getRandomValues(new Uint8Array(64));
return MLS.freshKeyPackage1(dummy64, cred, priv);
}

function create(cred, priv, groupId) {
let dummy96 = new Uint8Array([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
]);
let random96 = crypto.getRandomValues(new Uint8Array(96));
return MLS.create1(dummy96, cred, priv, groupId);
}

function add(state, keyPackage) {
let dummy36 = new Uint8Array([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3
]);
let random36 = crypto.getRandomValues(new Uint8Array(36));
return MLS.add1(state, keyPackage, dummy36);
}

function send(state, data) {
let dummy4 = new Uint8Array([
0, 1, 2, 3
]);
let random4 = crypto.getRandomValues(new Uint8Array(4));
return MLS.send1(state, dummy4, data);
}

function remove(state, identity) {
let dummy36 = new Uint8Array([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3
]);
let random36 = crypto.getRandomValues(new Uint8Array(36));
return MLS.remove1(state, identity, dummy36);
}


function processGroupMessage(state, payload) {
return MLS.processGroupMessage1(state, payload);
}

function processWelcomeMessage(payload, keyPair, lookup) {
return MLS.processWelcomeMessage1(payload, keyPair, lookup);
}

////////////////////////////////////////////////////////////////////////////////
// Crypto Lib //
////////////////////////////////////////////////////////////////////////////////

if (typeof module !== "undefined") {
var MLS = require("./js/MLS_JS.bc.js");
var node_crypto = require("crypto");
// for getRandomValues, hardcoded above
var crypto = node_crypto.webcrypto;
// UNCOMMENT THIS TO GET AES-GCM VIA NODE-CRYPTO
// var node_crypto = require("crypto");
// var crypto = node_crypto.webcrypto;
}

// Implementation of the (fast) crypto. Currently calling our own verified HACL-WASM
Expand Down Expand Up @@ -129,7 +40,11 @@ var hacl_modules = [
"Hacl_Chacha20",
"Hacl_Chacha20_Vec32",
"Hacl_MAC_Poly1305",
"Hacl_AEAD_Chacha20Poly1305"
"Hacl_AEAD_Chacha20Poly1305",
"Hacl_Lib",
"Hacl_AES_128_CTR32_BitSlice",
"Hacl_Gf128_CT64",
"Hacl_AES_128_GCM_CT64"
];


Expand Down Expand Up @@ -162,41 +77,55 @@ function HaclCrypto(Hacl) {

// AES128-GCM implementation relying on OpenSSL via node-crypto to get the
// benefits of hardware acceleration with AESNI
aes128gcm_encrypt: (key, iv, ad, pt) => {
let cipher = node_crypto.createCipheriv("id-aes128-GCM", key, iv, { authTagLength: 16 });
cipher.setAAD(ad);
let ct = cipher.update(pt);
cipher.final();
return [ new Uint8Array(ct.buffer), new Uint8Array(cipher.getAuthTag().buffer) ];
// aes128gcm_encrypt: (key, iv, ad, pt) => {
// let cipher = node_crypto.createCipheriv("id-aes128-GCM", key, iv, { authTagLength: 16 });
// cipher.setAAD(ad);
// let ct = cipher.update(pt);
// cipher.final();
// return [ new Uint8Array(ct.buffer), new Uint8Array(cipher.getAuthTag().buffer) ];
// },

// aes128gcm_decrypt: (key, iv, ad, ct, tag) => {
// let decipher = node_crypto.createDecipheriv("id-aes128-GCM", key, iv, { authTagLength: 16 });
// decipher.setAAD(ad);
// let pt = decipher.update(ct);
// decipher.setAuthTag(tag);
// try {
// decipher.final();
// return pt;
// } catch (e) {
// return null;
// }
// }

aes128gcm_encrypt: (key, iv, aad, plain) => {
let [ ctx ] = Hacl.AES128_GCM.expand(key);
let [ cipher_and_tag ] = Hacl.AES128_GCM.encrypt(ctx, plain, aad, iv);
let cipher = cipher_and_tag.subarray(0, plain.byteLength);
let tag = cipher_and_tag.subarray(plain.byteLength);
if (tag.byteLength != 16)
throw new Error("incorrect tag length");
return [ cipher, tag ];
},

aes128gcm_decrypt: (key, iv, ad, ct, tag) => {
let decipher = node_crypto.createDecipheriv("id-aes128-GCM", key, iv, { authTagLength: 16 });
decipher.setAAD(ad);
let pt = decipher.update(ct);
decipher.setAuthTag(tag);
try {
decipher.final();
return pt;
} catch (e) {
aes128gcm_decrypt: (key, iv, aad, cipher, tag) => {
if (tag.byteLength != 16)
throw new Error("incorrect tag length");
let cipher_and_tag = new Uint8Array(cipher.length + 16);
cipher_and_tag.set(cipher, 0);
cipher_and_tag.set(tag, cipher.byteLength);
let [ ctx ] = Hacl.AES128_GCM.expand(key);
let [ ok, plain ] = Hacl.AES128_GCM.decrypt(ctx, cipher_and_tag, aad, iv);
if (ok)
return plain;
else
return null;
}
}
}
}

if (typeof module !== undefined)
module.exports = {
// OLD MLS API
freshKeyPair,
freshKeyPackage,
create,
add,
send,
remove,
processGroupMessage,
processWelcomeMessage,
currentEpoch: MLS.currentEpoch,
// NEW MLS API
setEntropy: MLS.setEntropy,
setCiphersuite: MLS.setCiphersuite,
Expand Down
82 changes: 2 additions & 80 deletions js/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,82 +6,12 @@ var HaclWasm = require("./wasm/api.js");
////////////////////////////////////////////////////////////////////////////////

var test_main = () => {
// A demo of how to drive the (OLD) high-level API.
console.log("Page loaded");

// A self-test mostly for my own debugging.
const t0 = performance.now();
MLS.test();
const t1 = performance.now();

// Sample usage of the API -- an integration test.

// Fresh public & private *signing* keys. The application remembers the
// private signing key.
let signKeyPair_A = MLS.freshKeyPair();
console.log("generated a signing key pair for A", signKeyPair_A);

// Fresh credentials: a key package to be pushed to the directory, and a
// private HPKE key. Here, the application remembers that the private key
// corresponds to this specific package by way of the provided hash.
let cred_A = { identity: "jonathan", signPubKey: signKeyPair_A.pubKey };
let packageAndKey_A = MLS.freshKeyPackage(cred_A, signKeyPair_A.privKey);
console.log("generated a key package and a private key for A", packageAndKey_A);

// Create a new state for a group id.
let state_A = MLS.create(cred_A, signKeyPair_A.privKey, "[email protected]");

// We are at epoch 0 right now.
console.log("current epoch of A", MLS.currentEpoch(state_A));

// Let's introduce a new user: B
let signKeyPair_B = MLS.freshKeyPair();
let cred_B = { identity: "juraj", signPubKey: signKeyPair_B.pubKey };
let packageAndKey_B = MLS.freshKeyPackage(cred_B, signKeyPair_B.privKey);

// We publish key packages to the server. For each key package, we remember
// locally the private for each package hash.
let store_B = {};
console.log("adding to B's store", packageAndKey_B.hash);
store_B[packageAndKey_B.hash] = packageAndKey_B.privKey;

// A adds B to the group
({ state: state_A, welcomeMessage, groupMessage } = MLS.add(state_A, packageAndKey_B.keyPackage));
console.log("welcome message for B", welcomeMessage);
console.log("group message", groupMessage);

// A processes the echo of the add
({ state: state_A, outcome } = MLS.processGroupMessage(state_A, groupMessage.payload));

// B creates its fresh state via the welcome message
({ state: state_B, groupId } = MLS.processWelcomeMessage(welcomeMessage.payload, signKeyPair_B,
(hash) => {
console.log("looking into B's store", hash);
let v = store_B[hash];
if (!v)
console.log("hash not found in B's store");
else
console.log("hash found in B's store, value is", v);
return (store_B[hash] || null)
}));
console.log("B joined the group", groupId);

// B says hello
({ state: state_B, groupMessage } = MLS.send(state_B, "hello!"));

// B processes the echo of the message
({ state: state_B, outcome } = MLS.processGroupMessage(state_B, groupMessage.payload));
console.log("B received a message", outcome.payload);
console.log("current epoch of B", MLS.currentEpoch(state_B));

// A receives the message
({ state: state_A, outcome } = MLS.processGroupMessage(state_A, groupMessage.payload));
console.log("A received a message", outcome.payload);
console.log("current epoch of A", MLS.currentEpoch(state_A));

const t2 = performance.now();
console.log(`Internal self-test took ${t1 - t0} milliseconds.`);
console.log(`JS-driven test took ${t2 - t1} milliseconds.`);
};

var test_new = () => {
Expand Down Expand Up @@ -149,7 +79,7 @@ var test_new = () => {
let out = MLS.startJoinGroup(welcome, (k) => (k in store_B) ? store_B[k] : null);
out = MLS.continueJoinGroup(out, null);
group_B = MLS.finalizeJoinGroup(out);
console.log("B joined the group", groupId);
console.log("B joined the group", MLS.groupId(group_B));

// B says hello
({ message, group: group_B } = MLS.sendApplicationMessage(group_B, framingParams, Buffer.from("hello", "ascii")));
Expand All @@ -167,16 +97,8 @@ var test_new = () => {

if (typeof module !== "undefined") {
(async () => {
// Load the WASM modules, and instruct the MLS node module to use NodeCrypto
// for primitives.
// Load the WASM modules, this is the HACL-WASM initialization dance.
let h = await HaclWasm.getInitializedHaclModule(MLS.hacl_modules);
// The line below doesn't work because for some reason that's beyond my
// understanding, the global scope of JSOO and this global scope are not the
// same. Maybe they live in different modules or something?
// MyCrypto = HaclCrypto(h);

// HACL has slightly better performance, actually.
// MLS.setCrypto(NodeCrypto(h));
MLS.setCrypto(MLS.HaclCrypto(h));

console.log("Test starting");
Expand Down

0 comments on commit a710ef5

Please sign in to comment.