diff --git a/examples/pwd-to-signing.test.ts b/examples/pwd-to-signing.test.ts index b66ce87..86b7ce2 100644 --- a/examples/pwd-to-signing.test.ts +++ b/examples/pwd-to-signing.test.ts @@ -1,15 +1,15 @@ import {expect} from 'bun:test' import {hkdf, pbkdf, Usage} from '../src/kdf' -import {get_pubkey, sign, verify} from '../src/signature' +import {derive_keypair, sign, verify} from '../src/signature' const salt = new Uint8Array(32) const pwd = "test" const message = new TextEncoder().encode("Yeet") const kd = await pbkdf(salt, pwd) -const privk = await hkdf(kd, Usage.sign) as Uint8Array -const pubk = await get_pubkey(privk) +const source = await hkdf(kd, Usage.sign) as Uint8Array +const [privk, pubk] = await derive_keypair(source) const sig = await sign(message, privk) const verification = await verify(message, pubk, sig) diff --git a/package.json b/package.json index 7d8d413..95638b9 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "dependencies": { - "@noble/ed25519": "^2.1.0", + "@noble/curves": "^1.4.0", "jose": "^5.3.0", "log": "git+https://git.pband.ch/typescript/log.git", "misc": "git+https://git.pband.ch/typescript/misc.git" diff --git a/src/kdf.ts b/src/kdf.ts index 7d6a2c2..929bacc 100644 --- a/src/kdf.ts +++ b/src/kdf.ts @@ -39,7 +39,7 @@ export async function hkdf(key: Uint8Array, usage: Usage) : Promise { + const derived = hkdf(sha512, source, undefined, 'sign', 512) + const validPrivateKey = mod.mapHashToField(derived, p521.CURVE.n) // Dodge modulus bias :) (kinda) + const d = a2bg(validPrivateKey) + const point = get_pubkey(d) + + const pubkey = await crypto_pubkey(point) + const privkey = await crypto_privkey(d, point) + + return [privkey, pubkey] +} +function get_pubkey(d: bigint) : ProjPointType { + return p521.ProjectivePoint.BASE.multiply(d) +} +function crypto_privkey(d: bigint, point: ProjPointType) : Promise { + const jwk = { + crv: "P-521", + d: bg2b64url(d), + ext: false, + key_ops: [ "sign" ], + kty: "EC", + x: bg2b64url(point.x), + y: bg2b64url(point.y), + } + return crypto.subtle.importKey( + "jwk", + jwk, + { + name: "ECDSA", + namedCurve: "P-521" + }, + false, + ["sign"] + ) +} +function crypto_pubkey(point: ProjPointType) : Promise { + const jwk = { + crv: "P-521", + ext: true, + key_ops: [ "verify" ], + kty: "EC", + x: bg2b64url(point.x), + y: bg2b64url(point.y), + } + return crypto.subtle.importKey( + "jwk", + jwk, + { + name: "ECDSA", + namedCurve: "P-521" + }, + true, + ["verify"] + ) +} +function bg2b64url(n: bigint) : string { + return encode(btoa(String.fromCharCode(0) + String.fromCharCode(...hexToBytes(numberToHexUnpadded(n))))) +} +function encode(input: string) : string { + // Replace non-url compatible chars with base64 standard chars + input = input + .replace(/\+/g, '-') + .replace(/\//g, '_') + + const last = input.lastIndexOf("=") + if (last > 0) return input.substring(0, last) + return input +} +function a2bg(data: Uint8Array) : bigint { + return hexToNumber(bytesToHex(data)) +} \ No newline at end of file diff --git a/src/signature.ts b/src/signature.ts index 50a73b5..6e0fadf 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -1,26 +1,38 @@ import logger from 'log' -import * as ed from '@noble/ed25519' - const log = logger('crypto:signature') -export async function gen_privkey() : Promise { - log.trace("generate keypair") - return ed.utils.randomPrivateKey() +export async function gen_keypair() : Promise { + return crypto.subtle.generateKey( + { + name: "ECDSA", + namedCurve: "P-521", + }, + true, + ["sign", "verify"] + ) } -export async function gen_keypair() : Promise<[Uint8Array, Uint8Array]> { - const privkey = await gen_privkey() - const pubkey = await get_pubkey(privkey) - return [privkey, pubkey] -} -export async function get_pubkey(privkey: Uint8Array) : Promise { - log.trace("derive pubkey") - return ed.getPublicKeyAsync(privkey) -} -export async function sign(message: Uint8Array, privkey: Uint8Array) : Promise { +export {derive_keypair} from './signature.derive' +export async function sign(message: Uint8Array, privkey: CryptoKey) : Promise { log.trace("sign") - return ed.signAsync(message, privkey) + const buffer = await crypto.subtle.sign( + { + name: "ECDSA", + hash: "SHA-512" + }, + privkey, + message + ) + return new Uint8Array(buffer) } -export async function verify(message: Uint8Array, pubkey: Uint8Array, signature: Uint8Array) : Promise { +export async function verify(message: Uint8Array, pubkey: CryptoKey, signature: Uint8Array) : Promise { log.trace("verify") - return ed.verifyAsync(signature, message, pubkey) + return crypto.subtle.verify( + { + name: "ECDSA", + hash: "SHA-512" + }, + pubkey, + signature, + message, + ) }