@notabene/verify-proof 1.4.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notabene/verify-proof",
3
- "version": "1.4.1",
3
+ "version": "1.7.0",
4
4
  "description": "Verify ownership proofs",
5
5
  "source": "src/index.ts",
6
6
  "type": "module",
@@ -44,8 +44,10 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@cardano-foundation/cardano-verify-datasignature": "^1.0.11",
47
+ "@concordium/web-sdk": "^9.1.1",
48
+ "@grpc/grpc-js": "^1.13.4",
47
49
  "@noble/curves": "^1.7.0",
48
- "@notabene/javascript-sdk": "2.9.0",
50
+ "@notabene/javascript-sdk": "2.11.0",
49
51
  "@scure/base": "^1.2.1",
50
52
  "@stellar/stellar-sdk": "^13.1.0",
51
53
  "bip322-js": "^2.0.0",
package/src/bitcoin.ts CHANGED
@@ -18,8 +18,8 @@ enum SEGWIT_TYPES {
18
18
 
19
19
  interface ChainConfig {
20
20
  messagePrefix: string;
21
- pubKeyHashVersion: number;
22
- scriptHashVersion: number;
21
+ pubKeyHashVersion: number | Uint8Array;
22
+ scriptHashVersion: number | Uint8Array;
23
23
  bech32Prefix?: string;
24
24
  isTestnet?: boolean;
25
25
  }
@@ -58,6 +58,13 @@ const CHAIN_CONFIGS: Record<string, ChainConfig> = {
58
58
  scriptHashVersion: 0x10, // 7...
59
59
  isTestnet: false,
60
60
  },
61
+ zcash: {
62
+ messagePrefix: "\u0018Zcash Signed Message:\n",
63
+ pubKeyHashVersion: Uint8Array.from([0x1C, 0xB8]), // <-- FIXED
64
+ scriptHashVersion: Uint8Array.from([0x1C, 0xBD]),
65
+ isTestnet: false,
66
+ },
67
+
61
68
  testnet: {
62
69
  messagePrefix: "\u0018Bitcoin Signed Message:\n",
63
70
  pubKeyHashVersion: 0x6f, // m or n
@@ -87,7 +94,12 @@ export async function verifyBTCSignature(
87
94
  // Map chainId to our chain configuration
88
95
  const chainConfig = getChainConfig(address);
89
96
  if (!chainConfig) return { ...proof, status: ProofStatus.FAILED };
90
-
97
+
98
+ const isZcash = address.startsWith("t1") || address.startsWith("t3");
99
+ if (isZcash) {
100
+ return verifyBIP137(address, proof, chainConfig);
101
+ }
102
+
91
103
  // Use BIP322 for testnet addresses
92
104
  if (chainConfig.isTestnet) {
93
105
  return verifyBIP322(address, proof);
@@ -120,6 +132,9 @@ function getChainConfig(address: string): ChainConfig {
120
132
  if (address.startsWith("1") || address.startsWith("3") || address.startsWith("bc1")) {
121
133
  return CHAIN_CONFIGS["bitcoin"];
122
134
  }
135
+ if (address.startsWith("t1") || address.startsWith("t3")) {
136
+ return CHAIN_CONFIGS["zcash"];
137
+ }
123
138
  if (address.startsWith("L") || address.startsWith("M") || address.startsWith("ltc1")) {
124
139
  return CHAIN_CONFIGS["litecoin"];
125
140
  }
@@ -275,11 +290,19 @@ function verify(
275
290
 
276
291
  const base58check = createBase58check(Hash.sha256);
277
292
 
278
- function encodeBase58AddressFormat(version: number, publicKeyHash: Uint8Array) {
279
- const payload = new Uint8Array([version, ...publicKeyHash]);
293
+ function encodeBase58AddressFormat(version: number | Uint8Array, publicKeyHash: Uint8Array) {
294
+ const prefixBytes =
295
+ typeof version === "number"
296
+ ? Uint8Array.of(version)
297
+ : version; // Accept raw Uint8Array for Zcash
298
+
299
+ const payload = new Uint8Array(prefixBytes.length + publicKeyHash.length);
300
+ payload.set(prefixBytes);
301
+ payload.set(publicKeyHash, prefixBytes.length);
280
302
  return base58check.encode(payload);
281
303
  }
282
304
 
305
+
283
306
  function magicHash(attestation: string, messagePrefix: string) {
284
307
  const prefix = new TextEncoder().encode(messagePrefix);
285
308
  const message = new TextEncoder().encode(attestation);
package/src/cardano.ts CHANGED
@@ -5,17 +5,25 @@ export async function verifyCIP8Signature(
5
5
  proof: SignatureProof
6
6
  ): Promise<SignatureProof> {
7
7
  const [ns, , address] = proof.address.split(/:/);
8
- const key = proof.chainSpecificData?.cardanoCoseKey;
9
-
8
+ const key =
9
+ proof.chainSpecificData && "cardanoCoseKey" in proof.chainSpecificData
10
+ ? proof.chainSpecificData.cardanoCoseKey
11
+ : null;
12
+
10
13
  if (ns !== "cardano" || !key) {
11
14
  return { ...proof, status: ProofStatus.FAILED };
12
15
  }
13
-
16
+
14
17
  try {
15
- const verified = verifyDataSignature(proof.proof, key, proof.attestation, address);
18
+ const verified = verifyDataSignature(
19
+ proof.proof,
20
+ key,
21
+ proof.attestation,
22
+ address
23
+ );
16
24
  return {
17
25
  ...proof,
18
- status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED
26
+ status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,
19
27
  };
20
28
  } catch {
21
29
  return { ...proof, status: ProofStatus.FAILED };
@@ -0,0 +1,33 @@
1
+ import { ProofStatus, SignatureProof } from "@notabene/javascript-sdk";
2
+ import { AccountAddress, AccountTransactionSignature, verifyMessageSignature } from "@concordium/web-sdk";
3
+ import { ConcordiumGRPCNodeClient } from "@concordium/web-sdk/nodejs";
4
+ import { credentials } from "@grpc/grpc-js";
5
+
6
+ export const verifyConcordiumSignature = async (
7
+ proof: SignatureProof
8
+ ): Promise<SignatureProof> => {
9
+ const [ns, , address] = proof.address.split(/:/);
10
+ if (ns !== "ccd") return { ...proof, status: ProofStatus.FAILED };
11
+
12
+ const client = new ConcordiumGRPCNodeClient(
13
+ "grpc.testnet.concordium.com",
14
+ 20000,
15
+ credentials.createSsl(),
16
+ {
17
+ timeout: 15000,
18
+ }
19
+ );
20
+
21
+ const accountInfo = await client.getAccountInfo(
22
+ AccountAddress.fromBase58(address)
23
+ );
24
+
25
+ const signature = JSON.parse(proof.proof) as AccountTransactionSignature;
26
+
27
+ const verified = await verifyMessageSignature(proof.attestation, signature, accountInfo);
28
+
29
+ return {
30
+ ...proof,
31
+ status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,
32
+ };
33
+ };
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ import { verifyPersonalSignTIP191 } from "./tron";
13
13
  import { verifyCIP8Signature } from "./cardano";
14
14
  import { verifyPersonalSignXRPL } from "./xrpl";
15
15
  import { verifyStellarSignature } from "./xlm";
16
+ import { verifyConcordiumSignature } from "./concordium";
16
17
 
17
18
  export async function verifyProof(
18
19
  proof: OwnershipProof,
@@ -43,6 +44,8 @@ export async function verifyProof(
43
44
  return verifyPersonalSignXRPL(proof as SignatureProof, publicKey);
44
45
  case ProofTypes.XLM_ED25519:
45
46
  return verifyStellarSignature(proof as SignatureProof);
47
+ case ProofTypes.CONCORDIUM:
48
+ return verifyConcordiumSignature(proof as SignatureProof);
46
49
  case ProofTypes.EIP712:
47
50
  case ProofTypes.BIP137:
48
51
  case ProofTypes.BIP322:
@@ -78,6 +78,16 @@ const dogeProof: SignatureProof = {
78
78
  wallet_provider: "Doge",
79
79
  };
80
80
 
81
+ const zcashProof: SignatureProof = {
82
+ type: ProofTypes.BIP137,
83
+ address: "bip122:000000000019d6689c085ae165831e93:t1NmCz1oRS3e84NrUHsbHPJnz8a6KgZvbHL",
84
+ did: "did:pkh:bip122:000000000019d6689c085ae165831e93:t1NmCz1oRS3e84NrUHsbHPJnz8a6KgZvbHL",
85
+ attestation: "I certify that the blockchain address t1NmCz1oRS3e84NrUHsbHPJnz8a6KgZvbHL belongs to did:pkh:bip122:000000000019d6689c085ae165831e93:t1NmCz1oRS3e84NrUHsbHPJnz8a6KgZvbHL on Mon, 30 Jun 2025 08:41:29 GMT",
86
+ proof: "HzPW+q7EXixhtV/rBQ0sOtoXCDOml7C6P6rtIxLkBy3tenP978po7tuwj997DvJRiakL65Qw7xADK5hHs1Vu7es=",
87
+ status: ProofStatus.PENDING,
88
+ wallet_provider: "Manual Wallet Signature",
89
+ };
90
+
81
91
  describe("verifyBTCSignature", () => {
82
92
  it("handles bip322 segwit testnet addresses", async () => {
83
93
  const result = await verifyBTCSignature(bip322SegwitTestnetProof);
@@ -109,6 +119,11 @@ describe("verifyBTCSignature", () => {
109
119
  expect(result).toEqual({ ...dogeProof, status: ProofStatus.VERIFIED });
110
120
  });
111
121
 
122
+ it("verifies zcash address signature", async () => {
123
+ const result = await verifyBTCSignature(zcashProof);
124
+ expect(result).toEqual({ ...zcashProof, status: ProofStatus.VERIFIED });
125
+ });
126
+
112
127
  it("fails for invalid bitcoin signature", async () => {
113
128
  const proof: SignatureProof = { ...legacyProof, proof: "invalitd" };
114
129
 
@@ -0,0 +1,45 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { verifyConcordiumSignature } from "../concordium";
3
+ import {
4
+ ProofStatus,
5
+ ProofTypes,
6
+ SignatureProof,
7
+ } from "@notabene/javascript-sdk";
8
+
9
+ describe("verifyConcordiumSignature", () => {
10
+ it("returns verified proof when valid message and address", async () => {
11
+ const proof: SignatureProof = {
12
+ address:
13
+ "ccd:9dd9ca4d19e9393877d2c44b70f89acb:3thVBVWVwz1oHb4ob2VgehUGF7zyAJHkm5UDW8NdQKFKCvFnoF",
14
+ attestation:
15
+ "I certify that\n\nccd:9dd9ca4d19e9393877d2c44b70f89acb account 3thVBVWVwz1oHb4ob2VgehUGF7zyAJHkm5UDW8NdQKFKCvFnoF\n\nbelonged to did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5\n\non Thu, 12 Jun 2025 09:20:01 GMT",
16
+ type: ProofTypes.CONCORDIUM,
17
+ did: "did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5",
18
+ proof:
19
+ '{"0":{"0":"9b7263696072d4c5a0e060fec77bee5cb62ca9c8b674be864893f9d1dab75d28bf435efba9d4651ea1effdbd5b000f44d0d5be0fb10a11b3626a9a7be52e0d0d"}}',
20
+ status: ProofStatus.PENDING,
21
+ wallet_provider: "Concordium Wallet",
22
+ };
23
+ const result = await verifyConcordiumSignature(proof);
24
+
25
+ expect(result.status).toBe(ProofStatus.VERIFIED);
26
+ });
27
+
28
+ it("returns failed proof when invalid message and address", async () => {
29
+ const proof: SignatureProof = {
30
+ address:
31
+ "ccd:9dd9ca4d19e9393877d2c44b70f89acb:3thVBVWVwz1oHb4ob2VgehUGF7zyAJHkm5UDW8NdQKFKCvFnoF",
32
+ attestation:
33
+ "I certify that\n\nccd:9dd9ca4d19e9393877d2c44b70f89acb account 3thVBVWVwz1oHb4ob2VgehUGF7zyAJHkm5UDW8NdQKFKCvFnoF\n\nbelonged to did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5\n\non Thu, 12 Jun 2025 09:20:01 GMT",
34
+ proof: '{}',
35
+ did: "did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5",
36
+ status: ProofStatus.PENDING,
37
+ wallet_provider: "Concordium Wallet",
38
+ type: ProofTypes.CONCORDIUM,
39
+ };
40
+
41
+ const result = await verifyConcordiumSignature(proof);
42
+
43
+ expect(result.status).toBe(ProofStatus.FAILED);
44
+ });
45
+ });
@@ -298,4 +298,48 @@ describe("verifyProof", () => {
298
298
  expect(result.status).toBe(ProofStatus.FAILED);
299
299
  });
300
300
  });
301
+
302
+ describe("SIWE", () => {
303
+ const proof: SignatureProof = {
304
+ address: "eip155:1:0xb5f28405d09746a20c7aa0399e5109f0c295b098",
305
+ attestation:
306
+ "localhost wants you to sign in with your **blockchain** account:\n0xb5f28405d09746a20c7aa0399e5109f0c295b098\n\nPlease sign this message to confirm your identity\n\nURI: http://localhost:8000\nVersion: 1\nChain ID: eip155:1\nNonce: 5389\nIssued At: 2025-05-22T09:28:38.992Z",
307
+ proof:
308
+ "0xf88efca56019b58d4523fa5b480fe549afd41d2d92a90e7b54126ca1d8a933fe4e5b3a507d1a2e9f28e6b9fab92d3fb288e946712903bbdafff4556694fc50f51b",
309
+ did: "did:pkh:eip155:1:0xb5f28405d09746a20c7aa0399e5109f0c295b098",
310
+ status: ProofStatus.PENDING,
311
+ type: ProofTypes.EIP191,
312
+ wallet_provider: "reown",
313
+ };
314
+
315
+ it("should verify proof", async () => {
316
+ const result = await verifyProof(proof);
317
+ expect(result.status).toBe(ProofStatus.VERIFIED);
318
+ });
319
+
320
+ it("should fail if not valid proof", async () => {
321
+ const result = await verifyProof({
322
+ ...proof,
323
+ proof: "0x",
324
+ } as SignatureProof);
325
+ expect(result.status).toBe(ProofStatus.FAILED);
326
+ });
327
+ });
328
+
329
+ describe("Concordium", () => {
330
+ const proof: SignatureProof = {
331
+ type: ProofTypes.CONCORDIUM,
332
+ address: "ccd:9dd9ca4d19e9393877d2c44b70f89acb:3thVBVWVwz1oHb4ob2VgehUGF7zyAJHkm5UDW8NdQKFKCvFnoF",
333
+ attestation: "I certify that\n\nccd:9dd9ca4d19e9393877d2c44b70f89acb account 3thVBVWVwz1oHb4ob2VgehUGF7zyAJHkm5UDW8NdQKFKCvFnoF\n\nbelonged to did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5\n\non Thu, 12 Jun 2025 09:20:01 GMT",
334
+ proof: '{"0":{"0":"9b7263696072d4c5a0e060fec77bee5cb62ca9c8b674be864893f9d1dab75d28bf435efba9d4651ea1effdbd5b000f44d0d5be0fb10a11b3626a9a7be52e0d0d"}}',
335
+ did: "did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5",
336
+ status: ProofStatus.PENDING,
337
+ wallet_provider: "Concordium Wallet",
338
+ };
339
+
340
+ it("should verify proof", async () => {
341
+ const result = await verifyProof(proof);
342
+ expect(result.status).toBe(ProofStatus.VERIFIED);
343
+ });
344
+ })
301
345
  });