Skip to content

Commit

Permalink
Revert "remove js implementations"
Browse files Browse the repository at this point in the history
This reverts commit cbf7500.
  • Loading branch information
robbiecarlton committed Sep 9, 2024
1 parent cbf7500 commit 81618a4
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 0 deletions.
41 changes: 41 additions & 0 deletions src/deriveSeedFrom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// See opening comment in `keyManager.js`

const { sha512 } = require("js-sha512")
const argon2 = require("argon2-browser")

const ARGON2_ADDITIONAL_DATA = "holo chaperone web user ed25519 key v1" // probably need to turn into bytes

function deriveSeedFrom(hha_id, email, password) {
throw new Error("javascript deriveSeedFrom is not implemented yet")

// return new Uint8Array([
// 224, 186, 208, 19, 196, 26, 72, 30,
// 72, 91, 170, 129, 169, 229, 53, 112,
// 216, 149, 4, 192, 1, 114, 148, 173,
// 14, 68, 215, 72, 242, 209, 155, 196
// ])

let salt = sha512.digest(email)

// const seed = argon2.hash({
// pass: password,
// salt,
// type: argon2.ArgonType.Argon2id
// })

// {
// // optional
// time: 1, // the number of iterations
// mem: 1024, // used memory, in KiB
// hashLen: 24, // desired hash length
// parallelism: 1, // desired parallelism (it won't be computed in parallel, however)
// secret: new Uint8Array([...]), // optional secret data <- hha_id
// ad: new Uint8Array([...]), // optional associated data
// }

return seed
}

module.exports = {
deriveSeedFrom,
};
6 changes: 6 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const {
deriveSeedFrom
} = require("./deriveSeedFrom")
const KeyManager = require("./keyManager")
const blake = require("blakejs");
const multihash = require("multihashes");
const SerializeJSON = require("json-stable-stringify");
Expand Down Expand Up @@ -147,6 +151,8 @@ const Codec = {
};

module.exports = {
KeyManager,
deriveSeedFrom,
Codec,
HHT,
};
75 changes: 75 additions & 0 deletions src/keyManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// This class (and the function in `deriveSeedFrom.js`) are here as partial implementations of the same functionality
// currently exported in @holo-host/wasm-key-manager . In the case of this class, `KeyManager`, all the functionality
// is in place, but the generated signatures don't match those generated by wasm-key-manager. Most likely the difference
// happens at the level of converting the string to bytes, but that still needs to be fully investigated.
// In the case of `deriveSeedFrom`, the functionality is not fully implemented yet. The tests in `test_key_manager.js`
// should be correct, meaning: if they pass, then we're getting the same bytes we actually want in the signatures.

class KeyManager {
#private_key
#public_key
#ed

constructor (seed) {
throw new Error("javascript KeyManager is not implemented yet. Please use @holo-host/wasm-key-manager for this functionality")
// as per the rust implementation, private_key is just the seed
this.#private_key = seed

// TODO: update this repo to use ESM then we won't have to do this, or the associated ugliness below

import("@noble/ed25519").then(ed => {
import("@noble/hashes/sha512")
.then(({ sha512 }) => {
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
this.#ed = ed
this.#public_key = ed.getPublicKey(this.#private_key)
})
})
}

publicKey () {
if(!this.#public_key) {
throw new Error ("Attempt to access pubkey before initialized")
}
return this.#public_key
}

sign (message) {
const message_bytes = stringToByteArray(message)

if(!this.#ed) {
throw new Error ("Attempt to use ed before initialized")
}

return this.#ed.sign(message_bytes, this.#private_key)
}

verify (message, signature) {
return KeyManager.verifyWithPublicKey(message, signature, this.publicKey())
}
/**
* Returns true if the signature was generated by signing the message using the keypair of the pubkey
*
* @param {string} message - The first number.
* @param {Uint8Array} signature - The second number.
* @param {Uint8Array} pubkey - The second number.
* @returns {bool} The sum of the two numbers.
*/
static async verifyWithPublicKey (message, signature, pubkey) {
const message_bytes = stringToByteArray(message)

const ed = await import("@noble/ed25519")

return ed.verify(signature, message_bytes, pubkey)
}
}

function stringToByteArray(str) {
const byteArray = [];
for (let i = 0; i < str.length; i++) {
byteArray.push(str.charCodeAt(i));
}
return new Uint8Array(byteArray);
}

module.exports = KeyManager
77 changes: 77 additions & 0 deletions tests/test_key_manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const expect = require('chai').expect
const crypto = require('crypto')

const { KeyManager, deriveSeedFrom } = require('../src/index.js')

const wait = ms => new Promise(resolve => setTimeout(resolve, ms))

const hha_id = new Uint8Array([
66, 123, 133, 136, 133, 6, 247, 116,
4, 59, 43, 206, 131, 168, 123, 44,
54, 52, 3, 53, 134, 75, 137, 43,
63, 26, 216, 191, 67, 117, 38, 142
])

// See opening comment in `src/keyManager.js` for explanation as to why these are skipped

describe.skip("Key Manager", () => {
it("should create KeyManager instance with random bytes", async () => {
const seed = crypto.randomBytes( 32 )
const keys = new KeyManager( seed )

await wait(100) // wait for pubkey to load

expect( keys.publicKey() ).to.be.a("uint8array")
})

it("should derive seed from input", async () => {
const expectedSeed = new Uint8Array([
225, 186, 208, 19, 196, 26, 72, 30,
72, 91, 170, 129, 169, 229, 53, 112,
216, 149, 4, 192, 1, 114, 148, 173,
14, 68, 215, 72, 242, 209, 155, 196
])

const seed = deriveSeedFrom(hha_id, "example@holo.host", "password")

expect( seed ).to.be.an("uint8array")
expect( seed ).to.deep.equal( expectedSeed )
})

it("should sign and verify using derived seed", async () => {
const seed = deriveSeedFrom(hha_id, "example2@holo.host", "password")
const keys = new KeyManager( seed )

await wait(100) // wait for pubkey to load

const expectedPubkey = new Uint8Array([
253, 163, 6, 143, 70, 91, 132, 195,
250, 73, 221, 250, 186, 8, 83, 172,
77, 56, 95, 189, 150, 20, 188, 161,
40, 226, 241, 43, 45, 119, 221, 134,
])

expect ( keys.publicKey() ).to.deep.equal(expectedPubkey)

const message = "Hello, world!"

const signature = keys.sign( message )


const isGenuine = await keys.verify( message, signature )

expect( isGenuine ).to.be.true

const isGenuineStatic = await KeyManager.verifyWithPublicKey( message, signature, keys.publicKey() )

expect( isGenuineStatic ).to.be.true

const expectedSignature = new Uint8Array([
121, 105, 219, 165, 125, 230, 134, 244, 134, 164, 10, 240, 125, 89, 255, 226, 115, 5, 130, 19, 184, 226, 212, 2, 104, 13, 217, 222, 84, 54, 80, 103, 205, 34, 46, 215, 30, 68, 130, 60, 147, 207, 7, 46, 54, 238, 19, 255, 28, 209, 186, 5, 247, 198, 204, 84, 189, 233, 90, 230, 65, 24, 67, 5
])

expect( signature ).to.be.an("uint8array")
expect( signature ).to.deep.equal( expectedSignature )

})
})

0 comments on commit 81618a4

Please sign in to comment.