-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
4 changed files
with
199 additions
and
0 deletions.
There are no files selected for viewing
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
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, | ||
}; |
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
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
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 |
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
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 ) | ||
|
||
}) | ||
}) |