@notabene/verify-proof 1.8.1 → 1.9.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.
- package/dist/cosmos.d.ts +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.modern.js +1 -1
- package/dist/index.modern.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/tests/cosmos.test.d.ts +1 -0
- package/package.json +3 -2
- package/src/cosmos.ts +88 -0
- package/src/index.ts +4 -2
- package/src/tests/cosmos.test.ts +51 -0
- package/src/tests/index.test.ts +23 -0
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@notabene/verify-proof",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.9.0",
|
4
4
|
"description": "Verify ownership proofs",
|
5
5
|
"source": "src/index.ts",
|
6
6
|
"type": "module",
|
@@ -44,8 +44,9 @@
|
|
44
44
|
},
|
45
45
|
"dependencies": {
|
46
46
|
"@cardano-foundation/cardano-verify-datasignature": "^1.0.11",
|
47
|
+
"@cosmjs/amino": "^0.34.0",
|
47
48
|
"@noble/curves": "^1.7.0",
|
48
|
-
"@notabene/javascript-sdk": "
|
49
|
+
"@notabene/javascript-sdk": "2.11.0-next.2",
|
49
50
|
"@scure/base": "^1.2.1",
|
50
51
|
"@stellar/stellar-sdk": "^13.1.0",
|
51
52
|
"bip322-js": "^2.0.0",
|
package/src/cosmos.ts
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
import {
|
2
|
+
CosmosMetadata,
|
3
|
+
ProofStatus,
|
4
|
+
SignatureProof,
|
5
|
+
} from "@notabene/javascript-sdk";
|
6
|
+
import { bech32 } from "bech32";
|
7
|
+
import { StdSignDoc, serializeSignDoc } from "@cosmjs/amino";
|
8
|
+
import { secp256k1 } from "@noble/curves/secp256k1";
|
9
|
+
import { ripemd160 } from "@noble/hashes/legacy";
|
10
|
+
import { sha256 } from "@noble/hashes/sha2";
|
11
|
+
|
12
|
+
// Base64 decode
|
13
|
+
function base64ToUint8Array(base64: string): Uint8Array {
|
14
|
+
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
15
|
+
}
|
16
|
+
|
17
|
+
// Base64 encode UTF-8 bytes
|
18
|
+
function toBase64Bytes(str: string): string {
|
19
|
+
const utf8 = new TextEncoder().encode(str);
|
20
|
+
let binary = "";
|
21
|
+
for (let i = 0; i < utf8.length; i++) {
|
22
|
+
binary += String.fromCharCode(utf8[i]);
|
23
|
+
}
|
24
|
+
return btoa(binary);
|
25
|
+
}
|
26
|
+
|
27
|
+
// Cosmos address derivation (ADR-36)
|
28
|
+
// https://github.com/cosmos/cosmos-sdk/blob/214b11dcbaa129f7b4c0013b2103db9d54b85e9e/docs/architecture/adr-036-arbitrary-signature.md
|
29
|
+
function deriveCosmosAddress(pubkey: Uint8Array, prefix = "cosmos"): string {
|
30
|
+
const sha = sha256(pubkey);
|
31
|
+
const rip = ripemd160(sha);
|
32
|
+
return bech32.encode(prefix, bech32.toWords(rip));
|
33
|
+
}
|
34
|
+
|
35
|
+
export async function verifyCosmosSignature(
|
36
|
+
proof: SignatureProof
|
37
|
+
): Promise<SignatureProof> {
|
38
|
+
try {
|
39
|
+
const [ns, , address] = proof.address.split(/:/);
|
40
|
+
if (ns !== "cosmos") {
|
41
|
+
return { ...proof, status: ProofStatus.FAILED };
|
42
|
+
}
|
43
|
+
|
44
|
+
// Parse the proof JSON
|
45
|
+
const pubKeyBytes = base64ToUint8Array(
|
46
|
+
(proof.chainSpecificData as CosmosMetadata).pub_key.value
|
47
|
+
);
|
48
|
+
const signatureBytes = base64ToUint8Array(proof.proof);
|
49
|
+
|
50
|
+
// Step 1: Derive address from pubkey
|
51
|
+
const derivedAddress = deriveCosmosAddress(pubKeyBytes);
|
52
|
+
if (derivedAddress !== address) {
|
53
|
+
return { ...proof, status: ProofStatus.FAILED };
|
54
|
+
}
|
55
|
+
|
56
|
+
// Step 2: Build MsgSignData + StdSignDoc
|
57
|
+
const signDoc: StdSignDoc = {
|
58
|
+
chain_id: "", // must match the signing process
|
59
|
+
account_number: "0",
|
60
|
+
sequence: "0",
|
61
|
+
fee: { amount: [], gas: "0" },
|
62
|
+
msgs: [
|
63
|
+
{
|
64
|
+
type: "sign/MsgSignData",
|
65
|
+
value: {
|
66
|
+
signer: address,
|
67
|
+
data: toBase64Bytes(proof.attestation),
|
68
|
+
},
|
69
|
+
},
|
70
|
+
],
|
71
|
+
memo: "",
|
72
|
+
};
|
73
|
+
|
74
|
+
// Step 3: Serialize + hash
|
75
|
+
const signBytes = await serializeSignDoc(signDoc);
|
76
|
+
const digest = sha256(signBytes);
|
77
|
+
|
78
|
+
// Step 4: Verify using secp256k1
|
79
|
+
const verified = secp256k1.verify(signatureBytes, digest, pubKeyBytes);
|
80
|
+
|
81
|
+
return {
|
82
|
+
...proof,
|
83
|
+
status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,
|
84
|
+
};
|
85
|
+
} catch {
|
86
|
+
return { ...proof, status: ProofStatus.FAILED };
|
87
|
+
}
|
88
|
+
}
|
package/src/index.ts
CHANGED
@@ -14,6 +14,7 @@ import { verifyCIP8Signature } from "./cardano";
|
|
14
14
|
import { verifyPersonalSignXRPL } from "./xrpl";
|
15
15
|
import { verifyStellarSignature } from "./xlm";
|
16
16
|
import { verifyConcordiumSignature } from "./concordium";
|
17
|
+
import { verifyCosmosSignature } from "./cosmos";
|
17
18
|
|
18
19
|
export async function verifyProof(
|
19
20
|
proof: OwnershipProof,
|
@@ -46,9 +47,10 @@ export async function verifyProof(
|
|
46
47
|
return verifyPersonalSignXRPL(proof as SignatureProof, publicKey);
|
47
48
|
case ProofTypes.XLM_ED25519:
|
48
49
|
return verifyStellarSignature(proof as SignatureProof);
|
49
|
-
case ProofTypes.CONCORDIUM:
|
50
|
+
case ProofTypes.CONCORDIUM:
|
50
51
|
return verifyConcordiumSignature(proof as SignatureProof);
|
51
|
-
|
52
|
+
case ProofTypes.COSMOS:
|
53
|
+
return verifyCosmosSignature(proof as SignatureProof);
|
52
54
|
case ProofTypes.EIP712:
|
53
55
|
case ProofTypes.BIP137:
|
54
56
|
case ProofTypes.BIP322:
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
2
|
+
import {
|
3
|
+
ProofStatus,
|
4
|
+
ProofTypes,
|
5
|
+
SignatureProof,
|
6
|
+
} from "@notabene/javascript-sdk";
|
7
|
+
import { verifyCosmosSignature } from "../cosmos";
|
8
|
+
|
9
|
+
describe("verifyCosmosSignature", () => {
|
10
|
+
it("returns verified proof when valid message and address", async () => {
|
11
|
+
const proof: SignatureProof = {
|
12
|
+
type: ProofTypes.COSMOS,
|
13
|
+
did: "did:pkh:cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
|
14
|
+
status: ProofStatus.PENDING,
|
15
|
+
attestation:
|
16
|
+
"I certify that\n\ncosmos:cosmoshub-4 account cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8\n\nbelonged to did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5\n\non Mon, 14 Jul 2025 15:09:56 GMT",
|
17
|
+
address:
|
18
|
+
"cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
|
19
|
+
proof:
|
20
|
+
"r0L+4864I3p2jPRBt8IAnhhvYmemMkKGg9fcKM0GOBIScgIpmyX/+ObPT/09VMSCzxlzZkH6LQGLNiD2sudAZw==",
|
21
|
+
wallet_provider: "Keplr (Wallet Connect)",
|
22
|
+
chainSpecificData: {
|
23
|
+
pub_key: {
|
24
|
+
type: "tendermint/PubKeySecp256k1",
|
25
|
+
value: "Ak8k3jYgH4srYi1ygG0GWLrdbF7VwQgjiLqjifuJSsmU",
|
26
|
+
},
|
27
|
+
},
|
28
|
+
};
|
29
|
+
const result = await verifyCosmosSignature(proof);
|
30
|
+
|
31
|
+
expect(result.status).toBe(ProofStatus.VERIFIED);
|
32
|
+
});
|
33
|
+
|
34
|
+
it("returns failed proof when invalid message and address", async () => {
|
35
|
+
const proof: SignatureProof = {
|
36
|
+
type: ProofTypes.COSMOS,
|
37
|
+
did: "did:pkh:cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
|
38
|
+
status: ProofStatus.PENDING,
|
39
|
+
attestation:
|
40
|
+
"I certify that\n\ncosmos:cosmoshub-4 account cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8\n\nbelonged to did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5\n\non Mon, 14 Jul 2025 15:09:56 GMT",
|
41
|
+
address:
|
42
|
+
"cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
|
43
|
+
proof:
|
44
|
+
'{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Ak8k3jYgH4srYi1ygG0GWLrdbF7VwQgjiLqjifuJSsmU"},"signature":"invalid_signature_that_will_fail_verification"}',
|
45
|
+
wallet_provider: "Keplr (Wallet Connect)",
|
46
|
+
};
|
47
|
+
const result = await verifyCosmosSignature(proof);
|
48
|
+
|
49
|
+
expect(result.status).toBe(ProofStatus.FAILED);
|
50
|
+
});
|
51
|
+
});
|
package/src/tests/index.test.ts
CHANGED
@@ -342,4 +342,27 @@ describe("verifyProof", () => {
|
|
342
342
|
expect(result.status).toBe(ProofStatus.VERIFIED);
|
343
343
|
});
|
344
344
|
})
|
345
|
+
|
346
|
+
describe("Cosmos", () => {
|
347
|
+
const proof: SignatureProof = {
|
348
|
+
type: ProofTypes.COSMOS,
|
349
|
+
did: "did:pkh:cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
|
350
|
+
status: ProofStatus.PENDING,
|
351
|
+
attestation: "I certify that\n\ncosmos:cosmoshub-4 account cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8\n\nbelonged to did:key:z6MksamQq4oVRktkeSDxs6pbixYFUaUca8xgCvnTVLQA5PC5\n\non Mon, 14 Jul 2025 15:09:56 GMT",
|
352
|
+
address: "cosmos:cosmoshub-4:cosmos1tzxpcdsszyfuj852msx85c24u37jd7rly8skc8",
|
353
|
+
proof: "r0L+4864I3p2jPRBt8IAnhhvYmemMkKGg9fcKM0GOBIScgIpmyX/+ObPT/09VMSCzxlzZkH6LQGLNiD2sudAZw==",
|
354
|
+
wallet_provider: "Keplr (Wallet Connect)",
|
355
|
+
chainSpecificData: {
|
356
|
+
pub_key: {
|
357
|
+
type: "tendermint/PubKeySecp256k1",
|
358
|
+
value: "Ak8k3jYgH4srYi1ygG0GWLrdbF7VwQgjiLqjifuJSsmU",
|
359
|
+
},
|
360
|
+
}
|
361
|
+
};
|
362
|
+
|
363
|
+
it("should verify proof", async () => {
|
364
|
+
const result = await verifyProof(proof);
|
365
|
+
expect(result.status).toBe(ProofStatus.VERIFIED);
|
366
|
+
});
|
367
|
+
});
|
345
368
|
});
|