Rework API

This commit is contained in:
2024-09-09 16:10:08 +02:00
parent 7aee1ee379
commit 57ca4bf78d
12 changed files with 355 additions and 273 deletions
+12 -8
View File
@@ -1,9 +1,13 @@
import {ecdh, hkdf, pbkdf, Usage} from "./src/kdf"
import SecretBox from './src/secret-box'
import SecretWrap from './src/secret-wrap'
import PrivateBox from './src/private-box'
import PrivateWrap from './src/private-wrap'
import PwdBox from './src/pwd-box'
import PwdWrap from './src/pwd-wrap'
export const kdf = {ecdh, hkdf, pbkdf, Usage}
export * as signature from "./src/signature"
export * as JWT from "./src/jwt"
export {SecretBox} from './src/secret-box'
export {PrivateWrap} from './src/private-wrap'
export {SecretWrap} from './src/secret-wrap'
export {PwdWrap} from './src/pwd-wrap'
export * as kdf from './src/kdf'
export * as misc from './src/misc'
export * as signature from './src/signature'
export * as JWT from './src/jwt'
export {SecretBox,SecretWrap,PrivateBox,PrivateWrap,PwdBox,PwdWrap}
+4
View File
@@ -0,0 +1,4 @@
export const ECDH = {name: 'ECDH', namedCurve: 'P-521'}
export const SIGNATURE_KEY = {name: 'ECDSA', namedCurve: 'P-521'}
export const SIGNATURE_ALGO = {name: 'ECDSA', hash: 'SHA-512'}
export const ENCRYPTION = 'AES-GCM'
+61 -36
View File
@@ -1,47 +1,72 @@
import logger from 'log'
import * as consts from './const'
const log = logger('crypto:kdf')
export enum Usage {
sign,
box,
wrap
// sign
}
export enum DHusage {
box,
wrap
}
export async function hkdf(key: Uint8Array, usage: Usage): Promise<CryptoKey | Uint8Array> {
export async function hkdf(key: Uint8Array, usage: Usage): Promise<CryptoKey> {
log.trace('HKDF')
log.trace(`usage : ${usage === Usage.sign ? 'sign' : 'wrap'}`)
log.trace(`usage : ${usage}`)
const material = await crypto.subtle.importKey('raw', key, 'HKDF', false, ['deriveKey', 'deriveBits'])
if (usage === Usage.wrap) {
return crypto.subtle.deriveKey(
let algo
let usages: KeyUsage[]
let info_txt: string
switch (usage) {
/*case Usage.sign: {
algo = 'Ed215519'
usages = ['sign', 'verify']
info_txt = 'sign'
} break;*/
case Usage.box:
{
name: 'HKDF',
hash: 'SHA-512',
salt: new Uint8Array(32),
info: new TextEncoder().encode('wrap')
},
material,
{name: 'AES-GCM', length: 256},
false,
['wrapKey', 'unwrapKey']
)
} else if (usage === Usage.sign) {
const buffer = await crypto.subtle.deriveBits(
algo = {
name: consts.ENCRYPTION,
length: 256
}
usages = ['encrypt', 'decrypt']
info_txt = 'box'
}
break
case Usage.wrap:
{
name: 'HKDF',
hash: 'SHA-512',
salt: new Uint8Array(32),
info: new TextEncoder().encode('sign')
},
material,
512
)
return new Uint8Array(buffer)
} else {
log.warn(`Called HKDF with unknown enum value : ${usage}`)
throw "I don't even know what to say."
algo = {
name: consts.ENCRYPTION,
length: 256
}
usages = ['wrapKey', 'unwrapKey']
info_txt = 'wrap'
}
break
default: {
log.warn(`Unknown usage '${usage}'`)
throw 'Unknown usage for HDKF'
}
}
}
const info = new TextEncoder().encode(info_txt)
return crypto.subtle.deriveKey(
{
name: 'HKDF',
hash: 'SHA-512',
salt: new Uint8Array(32),
info
},
material,
algo,
false,
usages
)
}
export async function pbkdf(salt: Uint8Array, password: string): Promise<Uint8Array> {
log.trace('PBKDF')
const material = await crypto.subtle.importKey('raw', new TextEncoder().encode(password), 'PBKDF2', false, [
@@ -61,20 +86,20 @@ export async function pbkdf(salt: Uint8Array, password: string): Promise<Uint8Ar
return new Uint8Array(buffer)
}
export function ecdh(privkey: CryptoKey, pubkey: CryptoKey): Promise<CryptoKey> {
log.trace('ECDH')
export function ecdh(privkey: CryptoKey, pubkey: CryptoKey, usage: DHusage): Promise<CryptoKey> {
log.trace("ecdh")
const outputUsage: KeyUsage[] = usage === DHusage.box ? ['encrypt', 'decrypt'] : ['wrapKey', 'unwrapKey']
return crypto.subtle.deriveKey(
{
name: 'ECDH',
name: consts.ECDH.name,
public: pubkey
},
privkey,
{
name: 'AES-GCM',
name: consts.ENCRYPTION,
length: 256
},
false,
['wrapKey', 'unwrapKey']
outputUsage
)
}
+36
View File
@@ -0,0 +1,36 @@
import logger from 'log'
import {a2b64, b642a} from 'misc'
import * as consts from './const'
const log = logger('misc')
export enum Usage {
sign,
ecdh,
}
export async function pubkey_toString(pubkey: CryptoKey): Promise<string> {
log.trace('pubkey_toString')
const pubkey_buff = await crypto.subtle.exportKey('spki', pubkey)
return a2b64(new Uint8Array(pubkey_buff))
}
export async function pubkey_fromString(pubkey_str: string, usage: Usage): Promise<CryptoKey | null> {
log.trace('pubkey_fromString')
const pubkey = b642a(pubkey_str)
if (pubkey === null) return null
try {
return await crypto.subtle.importKey(
'spki',
pubkey,
usage === Usage.ecdh ? consts.ECDH : consts.SIGNATURE_KEY,
true,
usage === Usage.ecdh ? [] : ['verify']
)
} catch (e) {
log.warn("Failed to import public key")
log.debug("Error :", e)
return null
}
}
+59
View File
@@ -0,0 +1,59 @@
import logger from 'log'
import SecretBox from './secret-box'
import {DHusage, ecdh} from './kdf'
import {pubkey_fromString, pubkey_toString, Usage} from './misc'
import * as consts from './const'
const log = logger('crypto:private-box')
export default class PrivateBox {
private constructor(
private readonly pubkey: CryptoKey,
private readonly box: SecretBox
) {}
public static gen(extractable: boolean = true): Promise<CryptoKeyPair> {
log.trace('generate keypair')
log.trace('Extractable :', extractable)
try {
return crypto.subtle.generateKey(consts.ECDH, extractable, ['deriveKey']) as Promise<CryptoKeyPair>
} catch(e) {
log.warn("Failed to generate a key")
log.debug("Error :", e)
throw e
}
}
public static async encrypt(data: Uint8Array, pubkey: CryptoKey): Promise<PrivateBox> {
log.trace('encrypt')
const tmp_pair = await PrivateBox.gen(false)
const key = await ecdh(tmp_pair.privateKey, pubkey, DHusage.box)
const box = await SecretBox.encrypt(data, key)
return new this(tmp_pair.publicKey, box)
}
public async decrypt(privkey: CryptoKey): Promise<Uint8Array | null> {
log.trace('decrypt')
const key = await ecdh(privkey, this.pubkey, DHusage.box)
return this.box.decrypt(key)
}
public async toString(): Promise<string> {
log.trace('toString')
const pubkey = await pubkey_toString(this.pubkey)
const box = this.box.toString()
return `${pubkey}.${box}`
}
public static async fromString(data: string): Promise<PrivateBox | null> {
log.trace('fromString')
const parts = data.split('.')
if (parts.length < 2) return null
const pubkey = await pubkey_fromString(parts[0], Usage.ecdh)
if (pubkey === null) return null
const box = SecretBox.fromString(parts.slice(1).join("."))
if (box === null) return null
return new PrivateBox(pubkey, box)
}
}
+21 -37
View File
@@ -1,68 +1,52 @@
import {a2b64, b642a} from 'misc'
import logger from 'log'
import {SecretWrap} from './secret-wrap'
import {ecdh} from './kdf'
import {DHusage, ecdh} from './kdf'
import SecretWrap from './secret-wrap'
import {pubkey_fromString, pubkey_toString, Usage} from './misc'
import * as consts from './const'
const log = logger('crypto:private-wrap')
const algorithm = {
name: 'ECDH',
namedCurve: 'P-521'
}
export default class PrivateWrap {
private constructor(
private readonly box: SecretWrap,
private readonly pubkey: CryptoKey
) {}
export class PrivateWrap {
private constructor(private readonly box: SecretWrap, private readonly pubkey: CryptoKey) {}
public static gen_keypair(extractable: boolean = true): Promise<CryptoKeyPair> {
public static gen(extractable: boolean = true): Promise<CryptoKeyPair> {
log.trace('generate keypair')
return crypto.subtle.generateKey(algorithm, extractable, ['deriveKey'])
return crypto.subtle.generateKey(consts.ECDH, extractable, ['deriveKey']) as Promise<CryptoKeyPair>
}
public static async encrypt(data: CryptoKey, pubkey: CryptoKey): Promise<PrivateWrap> {
log.trace('encrypt')
const k = await this.gen_keypair()
const kd = await ecdh(k.privateKey, pubkey)
const tmp_keypair = await PrivateWrap.gen()
const kd = await ecdh(tmp_keypair.privateKey, pubkey, DHusage.wrap)
const box = await SecretWrap.encrypt(data, kd)
return new this(box, k.publicKey)
return new this(box, tmp_keypair.publicKey)
}
public async decrypt(privkey: CryptoKey): Promise<CryptoKey> {
public async decrypt(privkey: CryptoKey): Promise<CryptoKey | null> {
log.trace('decrypt')
const kd = await ecdh(privkey, this.pubkey)
const kd = await ecdh(privkey, this.pubkey, DHusage.wrap)
return this.box.decrypt(kd)
}
public async toString(): Promise<string> {
log.trace('toString')
const pubkey = await PrivateWrap.publicKey_toString(this.pubkey)
const pubkey = await pubkey_toString(this.pubkey)
const box = this.box.toString()
return `${pubkey}.${box}`
}
public static async fromString(data: string): Promise<PrivateWrap | null> {
log.trace('fromString')
const parts = data.split('.', 2)
if (parts.length !== 2) return null
const parts = data.split('.')
if (parts.length < 2) return null
const pubkey = await this.publicKey_fromString(parts[0])
const pubkey = await pubkey_fromString(parts[0], Usage.ecdh)
if (pubkey === null) return null
const box = SecretWrap.fromString(parts[1])
const box = SecretWrap.fromString(parts.slice(1).join("."))
if (box === null) return null
return new PrivateWrap(box, pubkey)
}
public static async publicKey_toString(publicKey: CryptoKey): Promise<string> {
const pubkey_spki = await crypto.subtle.exportKey('spki', publicKey)
return a2b64(new Uint8Array(pubkey_spki))
}
public static async publicKey_fromString(data: string): Promise<CryptoKey | null> {
const pubkey_str = b642a(data)
if (pubkey_str.is_err()) return null
try {
return crypto.subtle.importKey('spki', pubkey_str.unwrap(), algorithm, true, [])
} catch(e) {
return null
}
}
}
+51
View File
@@ -0,0 +1,51 @@
import logger from 'log'
import {hkdf, pbkdf, Usage} from './kdf'
import SecretBox from './secret-box'
import {a2b64, b642a} from 'misc'
const log = logger('crypto:pwd-box')
export default class PwdBox {
private constructor(
private readonly box: SecretBox,
private readonly salt: Uint8Array
) {}
private static async derive(pwd: string, salt: Uint8Array): Promise<CryptoKey> {
const k = await pbkdf(salt, pwd)
return (await hkdf(k, Usage.box)) as CryptoKey
}
public static async encrypt(data: Uint8Array, pwd: string): Promise<PwdBox> {
log.trace('encrypt')
const salt = crypto.getRandomValues(new Uint8Array(16))
const k = await PwdBox.derive(pwd, salt)
const box = await SecretBox.encrypt(data, k)
return new PwdBox(box, salt)
}
public async decrypt(pwd: string): Promise<Uint8Array | null> {
log.trace('decrypt')
const k = await PwdBox.derive(pwd, this.salt)
return this.box.decrypt(k)
}
public toString(): string {
log.trace('toString')
const salt = a2b64(this.salt)
const box = this.box.toString()
return `${salt}.${box}`
}
public static fromString(data: string): PwdBox | null {
log.trace('fromString')
const parts = data.split('.')
if (parts.length < 2) return null
const salt = b642a(parts[0])
if (salt === null) return null
const box = SecretBox.fromString(parts.slice(1).join("."))
if (box === null) return null
return new PwdBox(box, salt)
}
}
+39 -36
View File
@@ -1,49 +1,52 @@
import {hkdf, pbkdf, Usage} from './kdf'
import {SecretWrap} from './secret-wrap'
import SecretWrap from './secret-wrap'
import logger from 'log'
import {a2b64, b642a} from "misc";
import {a2b64, b642a} from 'misc'
const log = logger('crypto:pwd-wrap')
export class PwdWrap {
private constructor(private readonly box: SecretWrap, private readonly salt: Uint8Array) {}
export default class PwdWrap {
private constructor(
private readonly box: SecretWrap,
private readonly salt: Uint8Array
) {}
private static async derive(pwd: string, salt: Uint8Array) : Promise<CryptoKey> {
const k = await pbkdf(salt, pwd)
return await hkdf(k, Usage.wrap) as CryptoKey
}
private static async derive(pwd: string, salt: Uint8Array): Promise<CryptoKey> {
const k = await pbkdf(salt, pwd)
return (await hkdf(k, Usage.wrap)) as CryptoKey
}
public static async encrypt(data: CryptoKey, pwd: string, salt?: Uint8Array) : Promise<PwdWrap> {
log.trace('encrypt')
salt = salt ?? crypto.getRandomValues(new Uint8Array(16))
const k = await PwdWrap.derive(pwd, salt)
const box = await SecretWrap.encrypt(data, k)
return new PwdWrap(box, salt)
}
public async decrypt(pwd: string) : Promise<CryptoKey> {
log.trace('decrypt')
const k = await PwdWrap.derive(pwd, this.salt)
return this.box.decrypt(k)
}
public static async encrypt(data: CryptoKey, pwd: string, salt?: Uint8Array): Promise<PwdWrap> {
log.trace('encrypt')
salt = salt ?? crypto.getRandomValues(new Uint8Array(16))
const k = await PwdWrap.derive(pwd, salt)
const box = await SecretWrap.encrypt(data, k)
return new PwdWrap(box, salt)
}
public async decrypt(pwd: string): Promise<CryptoKey | null> {
log.trace('decrypt')
const k = await PwdWrap.derive(pwd, this.salt)
return this.box.decrypt(k)
}
public toString(): String {
log.trace('toString')
const salt = a2b64(this.salt)
const box = this.box.toString()
return `${salt}.${box}`
}
public toString(): string {
log.trace('toString')
const salt = a2b64(this.salt)
const box = this.box.toString()
return `${salt}.${box}`
}
public fromString(data: string): PwdWrap | null {
log.trace('fromString')
public static fromString(data: string): PwdWrap | null {
log.trace('fromString')
const parts = data.split('.', 2)
if (parts.length !== 2) return null
const parts = data.split('.')
if (parts.length < 2) return null
const salt = b642a(parts[0])
if (salt.is_err()) return null
const box = SecretWrap.fromString(parts[1])
if (box === null) return null
const salt = b642a(parts[0])
if (salt === null) return null
const box = SecretWrap.fromString(parts.slice(1).join("."))
if (box === null) return null
return new PwdWrap(box, salt.unwrap())
}
return new PwdWrap(box, salt)
}
}
+41 -20
View File
@@ -1,16 +1,20 @@
import logger from 'log'
import {a2b64, b642a} from 'misc'
import * as consts from './const'
const log = logger('crypto:secret-box')
export class SecretBox {
private constructor(private readonly iv: Uint8Array, private readonly cipher: Uint8Array) {}
export default class SecretBox {
private constructor(
private readonly iv: Uint8Array,
private readonly cipher: Uint8Array
) {}
public static gen_key(extractable: boolean = true): Promise<CryptoKey> {
public static gen(extractable: boolean = true): Promise<CryptoKey> {
log.trace('generate key')
return crypto.subtle.generateKey(
{
name: 'AES-GCM',
name: consts.ENCRYPTION,
length: 256
},
extractable,
@@ -23,7 +27,7 @@ export class SecretBox {
const iv = crypto.getRandomValues(new Uint8Array(11))
const cipher = await crypto.subtle.encrypt(
{
name: 'AES-GCM',
name: consts.ENCRYPTION,
iv
},
key,
@@ -31,30 +35,47 @@ export class SecretBox {
)
return new SecretBox(iv, new Uint8Array(cipher))
}
public async decrypt(key: CryptoKey): Promise<Uint8Array> {
public async decrypt(key: CryptoKey): Promise<Uint8Array | null> {
log.trace('decrypt')
const buffer = await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: this.iv
},
key,
this.cipher
)
return new Uint8Array(buffer)
try {
const buffer = await crypto.subtle.decrypt(
{
name: consts.ENCRYPTION,
iv: this.iv
},
key,
this.cipher
)
return new Uint8Array(buffer)
} catch(e) {
log.warn("Failed to decrypt")
log.debug("Error :", e)
return null
}
}
public toString(): string {
log.trace('toString')
const iv = a2b64(this.iv)
const cipher = a2b64(this.iv)
const cipher = a2b64(this.cipher)
return `${iv}.${cipher}`
}
public static fromString(data: string): SecretBox {
public static fromString(data: string): SecretBox | null {
log.trace('fromString')
const parts = data.split('.', 2)
const iv = b642a(parts[0]).expect('Failed decode IV')
const cipher = b642a(parts[1]).expect('Failed to decode cipher')
const parts = data.split('.')
if (parts.length < 2) {
log.warn("Invalid parts count")
return null
}
const iv = b642a(parts[0])
if (iv === null) return null
const cipher = b642a(parts[1])
if (cipher === null) return null
return new SecretBox(iv, cipher)
}
}
+26 -29
View File
@@ -1,9 +1,10 @@
import logger from 'log'
import {a2b64, b642a} from 'misc'
import * as consts from './const'
const log = logger('crypto:secret-wrap')
export class SecretWrap {
export default class SecretWrap {
constructor(
private readonly cipher: Uint8Array,
private readonly algorithm: KeyAlgorithm,
@@ -12,11 +13,11 @@ export class SecretWrap {
private readonly iv: Uint8Array
) {}
public static gen_key(extractable: boolean = true): Promise<CryptoKey> {
public static gen(extractable: boolean = true): Promise<CryptoKey> {
log.trace('generate key')
return crypto.subtle.generateKey(
{
name: 'AES-GCM',
name: consts.ENCRYPTION,
length: 256
},
extractable,
@@ -37,55 +38,51 @@ export class SecretWrap {
public static async encrypt(data: CryptoKey, key: CryptoKey): Promise<SecretWrap> {
log.trace('encrypt')
const format = this.format(data.type)
const format = SecretWrap.format(data.type)
const iv = crypto.getRandomValues(new Uint8Array(12))
const box = await crypto.subtle.wrapKey(format, data, key, {
name: 'AES-GCM',
iv
})
const box = await crypto.subtle.wrapKey(format, data, key, {name: consts.ENCRYPTION, iv})
return new SecretWrap(new Uint8Array(box), data.algorithm, data.usages, format, iv)
}
public async decrypt(key: CryptoKey): Promise<CryptoKey> {
public async decrypt(key: CryptoKey): Promise<CryptoKey | null> {
log.trace('decrypt')
return crypto.subtle.unwrapKey(
this.type,
this.cipher,
key,
{
name: 'AES-GCM',
iv: this.iv
},
this.algorithm,
true,
this.usages
)
try {
return await crypto.subtle.unwrapKey(this.type, this.cipher, key, {name: consts.ENCRYPTION, iv: this.iv}, this.algorithm, true, this.usages)
} catch(e) {
log.warn("Failed to unwrap")
log.debug("Error :", e)
return null
}
}
public toString(): string {
log.trace('toString')
const iv = a2b64(this.iv)
return JSON.stringify({
cipher: a2b64(this.cipher),
iv: a2b64(this.iv),
algorithm: this.algorithm,
usages: this.usages,
type: this.type
type: this.type,
iv,
})
}
public static fromString(data: string): SecretWrap | null {
log.trace('fromString')
const obj = this.parseJSON(data)
const obj = SecretWrap.parseJSON(data)
if (obj === null) return null
const cipher = b642a(obj.cipher)
if (cipher.is_err()) return null
const iv = b642a(obj.iv)
if (iv.is_err()) return null
if (cipher === null) return null
return new SecretWrap(cipher.unwrap(), obj.algorithm, obj.usages, obj.type, iv.unwrap())
const iv = b642a(obj.iv)
if (iv === null) return null
return new SecretWrap(cipher, obj.algorithm, obj.usages, obj.type, iv)
}
private static parseJSON(data: string): {cipher: string, iv: string, algorithm: KeyAlgorithm, usages: KeyUsage[], type: 'raw' | 'pkcs8'} | null {
private static parseJSON(
data: string
): {cipher: string; iv: string; algorithm: KeyAlgorithm; usages: KeyUsage[]; type: 'raw' | 'pkcs8'} | null {
log.trace('parseJSON')
try {
// TODO : Check the fields
-80
View File
@@ -1,80 +0,0 @@
import {p521} from '@noble/curves/p521'
import {hexToNumber, bytesToHex, hexToBytes, numberToHexUnpadded} from '@noble/curves/abstract/utils'
import {sha512} from '@noble/hashes/sha512'
import {hkdf} from '@noble/hashes/hkdf'
import * as mod from '@noble/curves/abstract/modular'
import type {ProjPointType} from '@noble/curves/abstract/weierstrass'
/** Derive a keypair from random data with sufficient entropy
* @param source entropy source, 64 bytes
* @returns [PrivKey, PubKey]
*/
export async function derive_keypair(source: Uint8Array): Promise<[CryptoKey, CryptoKey]> {
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<bigint> {
return p521.ProjectivePoint.BASE.multiply(d)
}
function crypto_privkey(d: bigint, point: ProjPointType<bigint>): Promise<CryptoKey> {
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<bigint>): Promise<CryptoKey> {
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(...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))
}
+5 -27
View File
@@ -1,38 +1,16 @@
import logger from 'log'
const log = logger('crypto:signature')
import * as consts from './const'
export async function gen_keypair(extractable: boolean = true): Promise<CryptoKeyPair> {
return crypto.subtle.generateKey(
{
name: 'ECDSA',
namedCurve: 'P-521'
},
extractable,
['sign', 'verify']
)
export async function gen(extractable: boolean = true): Promise<CryptoKeyPair> {
return crypto.subtle.generateKey(consts.SIGNATURE_KEY, extractable, ['sign', 'verify'])
}
export {derive_keypair} from './signature.derive'
export async function sign(message: Uint8Array, privkey: CryptoKey): Promise<Uint8Array> {
log.trace('sign')
const buffer = await crypto.subtle.sign(
{
name: 'ECDSA',
hash: 'SHA-512'
},
privkey,
message
)
const buffer = await crypto.subtle.sign(consts.SIGNATURE_ALGO, privkey, message)
return new Uint8Array(buffer)
}
export async function verify(message: Uint8Array, pubkey: CryptoKey, signature: Uint8Array): Promise<boolean> {
log.trace('verify')
return crypto.subtle.verify(
{
name: 'ECDSA',
hash: 'SHA-512'
},
pubkey,
signature,
message
)
return crypto.subtle.verify(consts.SIGNATURE_ALGO, pubkey, signature, message)
}