passes += +test
failures += +!test
- // // test both strings and bytes as input
- // for (const { privateKey, publicKey, message, signature } of PYTHON_ED25519_BLAKE2B_VECTORS) {
- // result = nano25519.derive(privateKey)
- // test = result.toLowerCase() === publicKey
- // check(`derive from ${privateKey}`, test)
- // passes += +test
- // failures += +!test
-
- // result = nano25519.sign(message, privateKey + publicKey)
- // test = result.toLowerCase() === signature.slice(0, 128)
- // check(`sign message ${message}`, test)
- // passes += +test
- // failures += +!test
-
- // result = nano25519.verify(signature.slice(0, 128), message, publicKey)
- // test = result === true
- // check(`verify signature ${signature.slice(0, 128)}`, test)
- // passes += +test
- // failures += +!test
-
- // const privateKeyBytes = new Uint8Array(privateKey.match(/.{2}/g)?.map(b => parseInt(b, 16)) ?? [])
- // const publicKeyBytes = new Uint8Array(publicKey.match(/.{2}/g)?.map(b => parseInt(b, 16)) ?? [])
- // const secretKeyBytes = new Uint8Array([...privateKeyBytes, ...publicKeyBytes])
- // const messageBytes = new Uint8Array(message.match(/.{2}/g)?.map(b => parseInt(b, 16)) ?? [])
- // const signatureBytes = new Uint8Array(signature.match(/.{2}/g)?.slice(0, 64).map(b => parseInt(b, 16)) ?? [])
-
- // result = nano25519.derive(privateKeyBytes)
- // test = [...result].map(b => b.toString(16).padStart(2, '0')).join('').toLowerCase() === publicKey
- // check(`derive from ${privateKey}`, test)
- // passes += +test
- // failures += +!test
-
- // result = nano25519.sign(messageBytes, secretKeyBytes)
- // test = [...result].map(b => b.toString(16).padStart(2, '0')).join('').toLowerCase() === signature.slice(0, 128)
- // check(`sign message ${message}`, test)
- // passes += +test
- // failures += +!test
-
- // result = nano25519.verify(signatureBytes, messageBytes, publicKeyBytes)
- // test = result === true
- // check(`verify signature ${signature.slice(0, 128)}`, test)
- // passes += +test
- // failures += +!test
- // }
+ // test both strings and bytes as input
+ for (const { privateKey, publicKey, message, signature } of PYTHON_ED25519_BLAKE2B_VECTORS) {
+ result = nano25519.derive(privateKey)
+ test = result.toLowerCase() === publicKey
+ check(`derive from ${privateKey}`, test)
+ passes += +test
+ failures += +!test
+
+ result = nano25519.sign(message, privateKey + publicKey)
+ test = result.toLowerCase() === signature.slice(0, 128)
+ check(`sign message ${message}`, test)
+ passes += +test
+ failures += +!test
+
+ result = nano25519.verify(signature.slice(0, 128), message, publicKey)
+ test = result === true
+ check(`verify signature ${signature.slice(0, 128)}`, test)
+ passes += +test
+ failures += +!test
+
+ const privateKeyBytes = new Uint8Array(privateKey.match(/.{2}/g)?.map(b => parseInt(b, 16)) ?? [])
+ const publicKeyBytes = new Uint8Array(publicKey.match(/.{2}/g)?.map(b => parseInt(b, 16)) ?? [])
+ const secretKeyBytes = new Uint8Array([...privateKeyBytes, ...publicKeyBytes])
+ const messageBytes = new Uint8Array(message.match(/.{2}/g)?.map(b => parseInt(b, 16)) ?? [])
+ const signatureBytes = new Uint8Array(signature.match(/.{2}/g)?.slice(0, 64).map(b => parseInt(b, 16)) ?? [])
+
+ result = nano25519.derive(privateKeyBytes)
+ test = [...result].map(b => b.toString(16).padStart(2, '0')).join('').toLowerCase() === publicKey
+ check(`derive from ${privateKey}`, test)
+ passes += +test
+ failures += +!test
+
+ result = nano25519.sign(messageBytes, secretKeyBytes)
+ test = [...result].map(b => b.toString(16).padStart(2, '0')).join('').toLowerCase() === signature.slice(0, 128)
+ check(`sign message ${message}`, test)
+ passes += +test
+ failures += +!test
+
+ result = nano25519.verify(signatureBytes, messageBytes, publicKeyBytes)
+ test = result === true
+ check(`verify signature ${signature.slice(0, 128)}`, test)
+ passes += +test
+ failures += +!test
+ }
// XFAIL
try {
import nano25519_wasm from '../../build/nano25519.wasm'
import { nano25519_init } from './nano25519'
-type Action = 'derive' | 'sign' | 'start' | 'stop' | 'verify'
+type Action = 'derive' | 'sign' | 'start' | 'verify'
type Data = {
- id: string
+ id: UUID
action: string
message?: string | ArrayBuffer
privateKey?: string
}
const nano25519_worker_init = ({ derive, sign, verify }: ReturnType<typeof nano25519_init>) => {
- let isListening = false
let host: NodeMessagePort | null = null
/**
|| typeof message.data.action !== 'string'
) return
let result: undefined | boolean | string | Uint8Array<ArrayBuffer>
- let id: undefined | string
+ let id: undefined | UUID
try {
- const data: Data = message.data as object & { id: string, action: string }
+ const data: Data = message.data as object & { id: UUID, action: Action }
id = data.id
- if (data.action === 'start') {
- isListening = true
- result = 'started'
- } else if (data.action === 'stop') {
- isListening = false
- result = 'stopped'
- } else if (isListening) {
- const { action } = data
- if (action === 'derive') {
+ switch (data.action) {
+ case 'start': {
+ result = 'listening'
+ break
+ }
+ case 'derive': {
const { privateKey } = data
const publicKey = derive(privateKey)
if (publicKey == null) {
throw new TypeError('Invalid public key from WASM derive()')
}
result = publicKey
- } else if (action === 'sign') {
+ break
+ }
+ case 'sign': {
const { message, secretKey } = data
const signature = sign(message, secretKey)
if (signature == null) {
throw new TypeError('Invalid signature from WASM sign()')
}
result = signature
- } else if (action === 'verify') {
+ break
+ }
+ case 'verify': {
const { message, publicKey, signature } = data
const verification = verify(signature, message, publicKey)
if (verification == null) {
throw new TypeError('Invalid verification from WASM verify()')
}
result = verification
+ break
+ }
+ default: {
+ throw new TypeError(`Invalid action '${data.action}'`)
}
}
} catch (err: unknown) {
function init (): void {
try {
BROWSER: {
+ if (url) URL.revokeObjectURL(url)
url = URL.createObjectURL(new Blob([nano25519_worker], { type: 'text/javascript' }))
worker = new Worker(url, { type: 'module' })
worker.onmessage = report
const { result } = data
console.log('received result from worker')
if (typeof result !== 'boolean' && typeof result !== 'string' && !isBytes(result)) {
+ console.error(result)
return err('Invalid return type')
}
return ok(result)
// Reconstruct worker when errors occur
function reset (): void {
console.warn(`nano25519 encountered an error. Reinitializing...`)
- isWorkerReady = false
worker.terminate()
init()
}
// Check that the worker is running and listening before sending messages
async function start (): Promise<unknown> {
return starting ??= new Promise((resolve, reject): void => {
+ console.log('starting worker')
if (!isWorkerReady) init()
const id = crypto.randomUUID()
tasks.set(id, [resolve, reject])
})
}
-// Request that the worker stop listening without terminating
-async function stop (): Promise<void> {
- console.log('stopping worker')
- const result = await dispatch({ action: 'stop' })
- if (result === 'stopped') {
- console.log('worker stopped successfully')
- } else if (typeof result === 'string') {
- throw new Error(result)
- } else {
- throw new Error('unknown error while stopping worker')
+class Nano25519ResultError extends Error {
+ constructor (action: string, cause?: unknown) {
+ super(`${action} result invalid`, { cause })
}
}
export async function run (data: Record<'action', 'verify'> & Record<string, string | Uint8Array<ArrayBuffer>>): Promise<boolean>
export async function run (data: Record<'action', 'derive' | 'sign' | 'verify'> & Record<string, string | Uint8Array<ArrayBuffer>>): Promise<boolean | string | Uint8Array<ArrayBuffer>> {
try {
- await start()
- if (url) URL.revokeObjectURL(url)
- } catch (err: any) {
- throw new Error('Error initializing worker')
+ const result = await start()
+ if (result === 'listening') {
+ console.log('worker listening')
+ } else if (typeof result === 'string') {
+ throw new Error(result)
+ } else {
+ throw new Error('unknown error')
+ }
+ } catch (e: any) {
+ throw new Error('failed to start worker', { cause: e })
}
- try {
- const result = await dispatch(data)
- switch (data.action) {
- case 'derive': {
- if ((isBytes(result) && result.byteLength === 32) || (typeof result === 'string' && /^[0-9a-f]{64}$/i.test(result))) {
- return result
- } else {
- throw new Error('derive result invalid')
- }
- }
- case 'sign': {
- if ((isBytes(result) && result.byteLength === 64) || (typeof result === 'string' && /^[0-9a-f]{128}$/i.test(result))) {
- return result
- } else {
- throw new Error('sign result invalid')
- }
+
+ const result = await dispatch(data)
+ switch (data.action) {
+ case 'derive': {
+ if ((isBytes(result) && result.byteLength === 32) || (typeof result === 'string' && /^[0-9a-f]{64}$/i.test(result))) {
+ return result
}
- case 'verify': {
- if (typeof result === 'boolean') {
- return result
- } else {
- throw new Error('verify result invalid')
- }
+ break
+ }
+ case 'sign': {
+ if ((isBytes(result) && result.byteLength === 64) || (typeof result === 'string' && /^[0-9a-f]{128}$/i.test(result))) {
+ return result
}
+ break
}
- } catch (err: any) {
- try {
- await stop()
- } catch (e: any) {
- console.error('failed to stop worker')
- reset()
- } finally {
- throw new Error('Error dispatching async request')
+ case 'verify': {
+ if (typeof result === 'boolean') {
+ return result
+ }
+ break
}
}
+ throw new Nano25519ResultError(data.action, result)
}