Created
August 12, 2025 04:17
-
-
Save mikedotexe/acb29901d032bf77f566b9f950ba9438 to your computer and use it in GitHub Desktop.
noble and crypto stuff.js
This file contains hidden or 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
| // crypto-verify.js | |
| // Usage: | |
| // import { verifyEd25519, verifySecp256k1 } from './crypto-verify.js' | |
| // const ok1 = await verifyEd25519(msgBytes, sig64, pub32); | |
| // const ok2 = await verifySecp256k1(msgBytes, sig64, pub33or65); | |
| import * as secp from '@noble/secp256k1'; | |
| import * as ed from '@noble/ed25519'; | |
| export async function verifyEd25519(message, signature, publicKey) { | |
| const msg = toBytes(message); | |
| const sig = toBytes(signature); | |
| const pub = toBytes(publicKey); | |
| if (await ed25519Available()) { | |
| const key = await crypto.subtle.importKey( | |
| 'raw', | |
| pub, // 32 bytes | |
| { name: 'Ed25519' }, | |
| false, | |
| ['verify'], | |
| ); | |
| return crypto.subtle.verify({ name: 'Ed25519' }, key, sig, msg); | |
| } | |
| // Fallback: noble-ed25519 | |
| return ed.verify(sig, msg, pub); | |
| } | |
| export async function verifySecp256k1(message, signature, publicKey, opts = {}) { | |
| // Web Crypto does NOT support secp256k1; use noble. | |
| // By default we prehash with SHA-256 (change if your protocol uses keccak256, etc.) | |
| const { hash = 'SHA-256' } = opts; | |
| const msg = toBytes(message); | |
| const sig = toBytes(signature); | |
| const pub = toBytes(publicKey); // 33 (compressed) or 65 (uncompressed) bytes | |
| const digest = await hashBytes(msg, hash); // 32 bytes | |
| return secp.verify(sig, digest, pub, { lowS: true }); | |
| } | |
| // --- helpers --- | |
| async function ed25519Available() { | |
| try { | |
| if (!globalThis.crypto?.subtle) return false; | |
| await crypto.subtle.generateKey({ name: 'Ed25519' }, false, ['sign', 'verify']); | |
| return true; | |
| } catch { | |
| return false; | |
| } | |
| } | |
| async function hashBytes(bytes, algo) { | |
| if (!globalThis.crypto?.subtle) throw new Error('Web Crypto not available'); | |
| const out = await crypto.subtle.digest(algo, toBytes(bytes)); | |
| return new Uint8Array(out); | |
| } | |
| function toBytes(x) { | |
| if (x instanceof Uint8Array) return x; | |
| if (typeof x === 'string') { | |
| const s = x.trim(); | |
| if (/^[0-9a-fA-F]+$/.test(s)) return hexToBytes(s); | |
| if (/^[A-Za-z0-9+/=]+$/.test(s)) return base64ToBytes(s); | |
| } | |
| throw new TypeError('Expected Uint8Array, hex, or base64 string'); | |
| } | |
| function hexToBytes(hex) { | |
| if (hex.length % 2) throw new Error('hex length must be even'); | |
| const out = new Uint8Array(hex.length / 2); | |
| for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.substr(i * 2, 2), 16); | |
| return out; | |
| } | |
| function base64ToBytes(b64) { | |
| if (typeof atob === 'function') { | |
| const bin = atob(b64); | |
| const out = new Uint8Array(bin.length); | |
| for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i); | |
| return out; | |
| } else { | |
| // Node | |
| return Uint8Array.from(Buffer.from(b64, 'base64')); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment