UNPKG

@didtools/pkh-tezos

Version:

Implements support to authenticate, authorize and verify with Tezos accounts as a did:pkh with SIWE(X) and CACAO. Primarly used with `did-session` and `@didtools/cacao`.

105 lines (104 loc) 3.53 kB
import { SiwTezosMessage, verifyTimeChecks, assertSigned } from '@didtools/cacao'; import { AccountId } from 'caip'; import * as u8a from 'uint8arrays'; import { blake2b } from '@noble/hashes/blake2b'; import { sha256 } from '@noble/hashes/sha256'; import { ed25519 } from '@noble/curves/ed25519'; // ED const TZ1Prefix = new Uint8Array([ 6, 161, 159 ]); const TZ1Length = 20; const EDPKPrefix = new Uint8Array([ 13, 15, 37, 217 ]); const EDSIGPrefix = new Uint8Array([ 9, 245, 205, 134, 18 ]); const SIGPrefix = new Uint8Array([ 4, 130, 43 ]); const Base58CheckSumLength = 4; export function getTezosVerifier() { return { // eslint-disable-next-line @typescript-eslint/require-await 'tezos:ed25519': async (cacao, opts)=>{ verifyTezosSignature(cacao, opts); } }; } export function getPkhfromPk(publicKey) { const pkPrefix = publicKey.substring(0, 4); if (pkPrefix !== 'edpk') throw new Error('Tezos Signature type not supported, only type tezos:ed25519'); const decoded = b58cdecode(publicKey, EDPKPrefix); const hashed = blake2b(decoded, { dkLen: TZ1Length }); const result = b58cencode(hashed, TZ1Prefix); return result; } function verifyEdSignature(decodedSig, bytesHash, decodedPublicKey) { try { return ed25519.verify(decodedSig, bytesHash, decodedPublicKey); } catch (e) { return false; } } //bs58btc decoding, bs58check - checksum function b58cdecode(enc, prefixArg) { const u8akey = u8a.fromString(enc, 'base58btc'); return u8akey.slice(prefixArg.length, u8akey.length - Base58CheckSumLength); } //bs58check encoding, bs58btc + checksum function b58cencode(value, prefix) { const n = new Uint8Array(prefix.length + value.length); n.set(prefix); n.set(value, prefix.length); const checksum = getCheckSum(n); const nc = new Uint8Array(n.length + 4); nc.set(n); nc.set(checksum, prefix.length + value.length); return u8a.toString(nc, 'base58btc'); } function getCheckSum(u8a) { const hashed = sha256(sha256(u8a)); return hashed.slice(0, 4); } export function verifySignature(payload, publicKey, signature) { const pkPrefix = publicKey.substring(0, 4); const sigPrefix = signature.startsWith('sig') ? signature.substr(0, 3) : signature.substr(0, 5); if (pkPrefix !== 'edpk' || !(sigPrefix === 'edsig' || sigPrefix === 'sig')) throw new Error('Tezos Signature type not supported, only type tezos:ed25519'); const decodedPublicKey = b58cdecode(publicKey, EDPKPrefix); const decodedSig = b58cdecode(signature, sigPrefix === 'edsig' ? EDSIGPrefix : SIGPrefix); const bytesHash = blake2b(u8a.fromString(payload, 'base16'), { dkLen: 32 }); return verifyEdSignature(decodedSig, bytesHash, decodedPublicKey); } export function verifyTezosSignature(cacao, options) { assertSigned(cacao); verifyTimeChecks(cacao, options); const msg = SiwTezosMessage.fromCacao(cacao); let signature = cacao.s.s; const publicKey = signature.slice(99); signature = signature.slice(0, 99); const issuerAddress = AccountId.parse(cacao.p.iss.replace('did:pkh:', '')).address; if (issuerAddress !== getPkhfromPk(publicKey)) { throw new Error(`address does not belong to publicKey`); } const payload = msg.signMessage(); if (!verifySignature(payload, publicKey, signature)) { throw new Error(`Signature does not belong to issuer`); } }