@notabene/verify-proof 1.11.0 → 1.12.0-next.12
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/README.md +21 -15
- package/dist/cjs/bitcoin-UIEQV3IM.cjs +403 -0
- package/dist/cjs/bitcoin-UIEQV3IM.cjs.map +1 -0
- package/dist/cjs/cardano-QG2S452K.cjs +36 -0
- package/dist/cjs/cardano-QG2S452K.cjs.map +1 -0
- package/dist/cjs/concordium-4B33UJJ5.cjs +44 -0
- package/dist/cjs/concordium-4B33UJJ5.cjs.map +1 -0
- package/dist/cjs/cosmos-JTT3AZCF.cjs +92 -0
- package/dist/cjs/cosmos-JTT3AZCF.cjs.map +1 -0
- package/dist/cjs/eth-BVXX47HH.cjs +36 -0
- package/dist/cjs/eth-BVXX47HH.cjs.map +1 -0
- package/dist/cjs/index.cjs +68 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +5 -0
- package/dist/cjs/solana-4WNBEQSU.cjs +348 -0
- package/dist/cjs/solana-4WNBEQSU.cjs.map +1 -0
- package/dist/cjs/tron-IM2O2VLM.cjs +57 -0
- package/dist/cjs/tron-IM2O2VLM.cjs.map +1 -0
- package/dist/cjs/xlm-CHT7RN4Z.cjs +27 -0
- package/dist/cjs/xlm-CHT7RN4Z.cjs.map +1 -0
- package/dist/cjs/xrpl-HJ2XWSOO.cjs +74 -0
- package/dist/cjs/xrpl-HJ2XWSOO.cjs.map +1 -0
- package/dist/esm/bitcoin-BTZ4TXEK.js +401 -0
- package/dist/esm/bitcoin-BTZ4TXEK.js.map +1 -0
- package/dist/esm/cardano-OMGAPH5N.js +30 -0
- package/dist/esm/cardano-OMGAPH5N.js.map +1 -0
- package/dist/esm/concordium-JJ5VEIOA.js +42 -0
- package/dist/esm/concordium-JJ5VEIOA.js.map +1 -0
- package/dist/esm/cosmos-AVIWAIX7.js +90 -0
- package/dist/esm/cosmos-AVIWAIX7.js.map +1 -0
- package/dist/esm/eth-GI4Z5V4T.js +33 -0
- package/dist/esm/eth-GI4Z5V4T.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +66 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/solana-ZARDAF6O.js +341 -0
- package/dist/esm/solana-ZARDAF6O.js.map +1 -0
- package/dist/esm/tron-FI2B6DRS.js +52 -0
- package/dist/esm/tron-FI2B6DRS.js.map +1 -0
- package/dist/esm/xlm-TNRZ5CFC.js +25 -0
- package/dist/esm/xlm-TNRZ5CFC.js.map +1 -0
- package/dist/esm/xrpl-52IBJ4EW.js +69 -0
- package/dist/esm/xrpl-52IBJ4EW.js.map +1 -0
- package/package.json +27 -6
- package/src/bitcoin.ts +43 -19
- package/src/cashaddr.ts +157 -0
- package/src/concordium.ts +34 -283
- package/src/cosmos.ts +44 -6
- package/src/index.ts +31 -20
- package/src/tests/bitcoin.test.ts +527 -16
- package/src/tests/concordium.test.ts +96 -134
- package/src/tests/cosmos.test.ts +139 -0
- package/src/tests/index.test.ts +7 -7
- package/src/tests/solana.test.ts +1 -1
- package/src/tests/xrpl.test.ts +8 -6
- package/dist/bitcoin.d.ts +0 -2
- package/dist/cardano.d.ts +0 -2
- package/dist/concordium.d.ts +0 -15
- package/dist/cosmos.d.ts +0 -2
- package/dist/eth.d.ts +0 -4
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
- package/dist/index.modern.js +0 -2
- package/dist/index.modern.js.map +0 -1
- package/dist/index.umd.js +0 -2
- package/dist/index.umd.js.map +0 -1
- package/dist/solana.d.ts +0 -17
- package/dist/tests/bitcoin.test.d.ts +0 -1
- package/dist/tests/cardano.test.d.ts +0 -1
- package/dist/tests/concordium.test.d.ts +0 -1
- package/dist/tests/cosmos.test.d.ts +0 -1
- package/dist/tests/eth.test.d.ts +0 -1
- package/dist/tests/index.test.d.ts +0 -1
- package/dist/tests/solana.test.d.ts +0 -1
- package/dist/tests/tron.test.d.ts +0 -1
- package/dist/tests/xlm.test.d.ts +0 -1
- package/dist/tests/xrpl.test.d.ts +0 -1
- package/dist/tron.d.ts +0 -6
- package/dist/xlm.d.ts +0 -2
- package/dist/xrpl.d.ts +0 -5
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import verifyDataSignature from '@cardano-foundation/cardano-verify-datasignature';
|
|
3
|
+
import { ProofStatus } from '@notabene/javascript-sdk';
|
|
4
|
+
|
|
5
|
+
if (typeof globalThis !== 'undefined' && !globalThis.Buffer) { globalThis.Buffer = Buffer; }
|
|
6
|
+
async function verifyCIP8Signature(proof) {
|
|
7
|
+
const [ns, , address] = proof.address.split(/:/);
|
|
8
|
+
const key = proof.chainSpecificData && "cardanoCoseKey" in proof.chainSpecificData ? proof.chainSpecificData.cardanoCoseKey : null;
|
|
9
|
+
if (ns !== "cardano" || !key) {
|
|
10
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const verified = verifyDataSignature(
|
|
14
|
+
proof.proof,
|
|
15
|
+
key,
|
|
16
|
+
proof.attestation,
|
|
17
|
+
address
|
|
18
|
+
);
|
|
19
|
+
return {
|
|
20
|
+
...proof,
|
|
21
|
+
status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED
|
|
22
|
+
};
|
|
23
|
+
} catch {
|
|
24
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { verifyCIP8Signature };
|
|
29
|
+
//# sourceMappingURL=cardano-OMGAPH5N.js.map
|
|
30
|
+
//# sourceMappingURL=cardano-OMGAPH5N.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cardano.ts"],"names":[],"mappings":";;;;;AAGA,eAAsB,oBACpB,KAAA,EACyB;AACzB,EAAA,MAAM,CAAC,MAAM,OAAO,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,GAAG,CAAA;AAC/C,EAAA,MAAM,GAAA,GACJ,MAAM,iBAAA,IAAqB,gBAAA,IAAoB,MAAM,iBAAA,GACjD,KAAA,CAAM,kBAAkB,cAAA,GACxB,IAAA;AAEN,EAAA,IAAI,EAAA,KAAO,SAAA,IAAa,CAAC,GAAA,EAAK;AAC5B,IAAA,OAAO,EAAE,GAAG,KAAA,EAAO,MAAA,EAAQ,YAAY,MAAA,EAAO;AAAA,EAChD;AAEA,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,mBAAA;AAAA,MACf,KAAA,CAAM,KAAA;AAAA,MACN,GAAA;AAAA,MACA,KAAA,CAAM,WAAA;AAAA,MACN;AAAA,KACF;AACA,IAAA,OAAO;AAAA,MACL,GAAG,KAAA;AAAA,MACH,MAAA,EAAQ,QAAA,GAAW,WAAA,CAAY,QAAA,GAAW,WAAA,CAAY;AAAA,KACxD;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAE,GAAG,KAAA,EAAO,MAAA,EAAQ,YAAY,MAAA,EAAO;AAAA,EAChD;AACF","file":"cardano-OMGAPH5N.js","sourcesContent":["import verifyDataSignature from \"@cardano-foundation/cardano-verify-datasignature\";\nimport { ProofStatus, SignatureProof } from \"@notabene/javascript-sdk\";\n\nexport async function verifyCIP8Signature(\n proof: SignatureProof\n): Promise<SignatureProof> {\n const [ns, , address] = proof.address.split(/:/);\n const key =\n proof.chainSpecificData && \"cardanoCoseKey\" in proof.chainSpecificData\n ? proof.chainSpecificData.cardanoCoseKey\n : null;\n\n if (ns !== \"cardano\" || !key) {\n return { ...proof, status: ProofStatus.FAILED };\n }\n\n try {\n const verified = verifyDataSignature(\n proof.proof,\n key,\n proof.attestation,\n address\n );\n return {\n ...proof,\n status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,\n };\n } catch {\n return { ...proof, status: ProofStatus.FAILED };\n }\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import { ProofStatus } from '@notabene/javascript-sdk';
|
|
3
|
+
|
|
4
|
+
if (typeof globalThis !== 'undefined' && !globalThis.Buffer) { globalThis.Buffer = Buffer; }
|
|
5
|
+
var CONCORDIUM_VERIFIER_URL = "https://web3id-verifier.mainnet.concordium.software";
|
|
6
|
+
var verifyConcordiumSignature = async (proof) => {
|
|
7
|
+
const [ns] = proof.address.split(/:/);
|
|
8
|
+
if (ns !== "ccd") {
|
|
9
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const presentationJson = JSON.parse(proof.proof);
|
|
13
|
+
if (presentationJson.presentationContext !== proof.attestation) {
|
|
14
|
+
throw new Error("Presentation context does not match attestation");
|
|
15
|
+
}
|
|
16
|
+
if (!presentationJson.verifiableCredential || presentationJson.verifiableCredential.length === 0) {
|
|
17
|
+
throw new Error("Verifiable credential is missing or empty");
|
|
18
|
+
}
|
|
19
|
+
const response = await fetch(`${CONCORDIUM_VERIFIER_URL}/v0/verify`, {
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: { "Content-Type": "application/json" },
|
|
22
|
+
body: JSON.stringify(presentationJson)
|
|
23
|
+
});
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
const errorText = await response.text();
|
|
26
|
+
throw new Error(`Verifier returned ${response.status}: ${errorText}`);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
...proof,
|
|
30
|
+
status: ProofStatus.VERIFIED
|
|
31
|
+
};
|
|
32
|
+
} catch {
|
|
33
|
+
return {
|
|
34
|
+
...proof,
|
|
35
|
+
status: ProofStatus.FAILED
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export { verifyConcordiumSignature };
|
|
41
|
+
//# sourceMappingURL=concordium-JJ5VEIOA.js.map
|
|
42
|
+
//# sourceMappingURL=concordium-JJ5VEIOA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/concordium.ts"],"names":[],"mappings":";;;;AAGA,IAAM,uBAAA,GACJ,qDAAA;AAOK,IAAM,yBAAA,GAA4B,OACvC,KAAA,KAC4B;AAC5B,EAAA,MAAM,CAAC,EAAE,CAAA,GAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,GAAG,CAAA;AAEpC,EAAA,IAAI,OAAO,KAAA,EAAO;AAChB,IAAA,OAAO,EAAE,GAAG,KAAA,EAAO,MAAA,EAAQ,YAAY,MAAA,EAAO;AAAA,EAChD;AAEA,EAAA,IAAI;AACF,IAAA,MAAM,gBAAA,GAAmB,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,KAAK,CAAA;AAG/C,IAAA,IAAI,gBAAA,CAAiB,mBAAA,KAAwB,KAAA,CAAM,WAAA,EAAa;AAC9D,MAAA,MAAM,IAAI,MAAM,iDAAiD,CAAA;AAAA,IACnE;AAGA,IAAA,IACE,CAAC,gBAAA,CAAiB,oBAAA,IAClB,gBAAA,CAAiB,oBAAA,CAAqB,WAAW,CAAA,EACjD;AACA,MAAA,MAAM,IAAI,MAAM,2CAA2C,CAAA;AAAA,IAC7D;AAGA,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,CAAA,EAAG,uBAAuB,CAAA,UAAA,CAAA,EAAc;AAAA,MACnE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,gBAAgB;AAAA,KACtC,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,IAAA,EAAK;AACtC,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kBAAA,EAAqB,SAAS,MAAM,CAAA,EAAA,EAAK,SAAS,CAAA,CAAE,CAAA;AAAA,IACtE;AAGA,IAAA,OAAO;AAAA,MACL,GAAG,KAAA;AAAA,MACH,QAAQ,WAAA,CAAY;AAAA,KACtB;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO;AAAA,MACL,GAAG,KAAA;AAAA,MACH,QAAQ,WAAA,CAAY;AAAA,KACtB;AAAA,EACF;AACF","file":"concordium-JJ5VEIOA.js","sourcesContent":["import { ProofStatus, SignatureProof } from \"@notabene/javascript-sdk\";\nimport { VerifiablePresentation } from \"@concordium/web-sdk\";\n\nconst CONCORDIUM_VERIFIER_URL =\n \"https://web3id-verifier.mainnet.concordium.software\";\n\n/**\n * Verifies a Concordium signature proof using Concordium's hosted verifier service\n * @param proof The signature proof to verify\n * @returns Promise resolving to the proof with updated status\n */\nexport const verifyConcordiumSignature = async (\n proof: SignatureProof,\n): Promise<SignatureProof> => {\n const [ns] = proof.address.split(/:/);\n\n if (ns !== \"ccd\") {\n return { ...proof, status: ProofStatus.FAILED };\n }\n\n try {\n const presentationJson = JSON.parse(proof.proof) as VerifiablePresentation;\n\n // Validate presentation context matches attestation\n if (presentationJson.presentationContext !== proof.attestation) {\n throw new Error(\"Presentation context does not match attestation\");\n }\n\n // Validate verifiableCredential exists and is not empty\n if (\n !presentationJson.verifiableCredential ||\n presentationJson.verifiableCredential.length === 0\n ) {\n throw new Error(\"Verifiable credential is missing or empty\");\n }\n\n // Use Concordium's hosted verifier instead of gRPC\n const response = await fetch(`${CONCORDIUM_VERIFIER_URL}/v0/verify`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(presentationJson),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Verifier returned ${response.status}: ${errorText}`);\n }\n\n // If we get here, the proof is valid\n return {\n ...proof,\n status: ProofStatus.VERIFIED,\n };\n } catch {\n return {\n ...proof,\n status: ProofStatus.FAILED,\n };\n }\n};\n"]}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import { ProofStatus } from '@notabene/javascript-sdk';
|
|
3
|
+
import { bech32 } from 'bech32';
|
|
4
|
+
import { serializeSignDoc } from '@cosmjs/amino';
|
|
5
|
+
import { secp256k1 } from '@noble/curves/secp256k1';
|
|
6
|
+
import { ripemd160 } from '@noble/hashes/legacy';
|
|
7
|
+
import { sha256 } from '@noble/hashes/sha2';
|
|
8
|
+
import { keccak_256 } from '@noble/hashes/sha3';
|
|
9
|
+
|
|
10
|
+
if (typeof globalThis !== 'undefined' && !globalThis.Buffer) { globalThis.Buffer = Buffer; }
|
|
11
|
+
function base64ToUint8Array(base64) {
|
|
12
|
+
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
|
13
|
+
}
|
|
14
|
+
function toBase64Bytes(str) {
|
|
15
|
+
const utf8 = new TextEncoder().encode(str);
|
|
16
|
+
let binary = "";
|
|
17
|
+
for (let i = 0; i < utf8.length; i++) {
|
|
18
|
+
binary += String.fromCharCode(utf8[i]);
|
|
19
|
+
}
|
|
20
|
+
return btoa(binary);
|
|
21
|
+
}
|
|
22
|
+
function deriveCosmosAddress(pubkey, prefix) {
|
|
23
|
+
const sha = sha256(pubkey);
|
|
24
|
+
const rip = ripemd160(sha);
|
|
25
|
+
return bech32.encode(prefix, bech32.toWords(rip));
|
|
26
|
+
}
|
|
27
|
+
function deriveEthAddress(pubkey, prefix) {
|
|
28
|
+
const uncompressed = secp256k1.ProjectivePoint.fromHex(pubkey).toRawBytes(false).slice(1);
|
|
29
|
+
const eth = keccak_256(uncompressed).slice(-20);
|
|
30
|
+
return bech32.encode(prefix, bech32.toWords(eth));
|
|
31
|
+
}
|
|
32
|
+
function keyAlgoForAddress(pubkey, address) {
|
|
33
|
+
const prefix = bech32Prefix(address);
|
|
34
|
+
if (deriveCosmosAddress(pubkey, prefix) === address) {
|
|
35
|
+
return "secp256k1";
|
|
36
|
+
}
|
|
37
|
+
if (deriveEthAddress(pubkey, prefix) === address) {
|
|
38
|
+
return "ethsecp256k1";
|
|
39
|
+
}
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
function bech32Prefix(address) {
|
|
43
|
+
return address.slice(0, address.lastIndexOf("1"));
|
|
44
|
+
}
|
|
45
|
+
async function verifyCosmosSignature(proof) {
|
|
46
|
+
try {
|
|
47
|
+
const [ns, , address] = proof.address.split(/:/);
|
|
48
|
+
if (ns !== "cosmos") {
|
|
49
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
50
|
+
}
|
|
51
|
+
const pubKeyBytes = base64ToUint8Array(
|
|
52
|
+
proof.chainSpecificData.pub_key.value
|
|
53
|
+
);
|
|
54
|
+
const signatureBytes = base64ToUint8Array(proof.proof);
|
|
55
|
+
const algo = keyAlgoForAddress(pubKeyBytes, address);
|
|
56
|
+
if (!algo) {
|
|
57
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
58
|
+
}
|
|
59
|
+
const signDoc = {
|
|
60
|
+
chain_id: "",
|
|
61
|
+
// must match the signing process
|
|
62
|
+
account_number: "0",
|
|
63
|
+
sequence: "0",
|
|
64
|
+
fee: { amount: [], gas: "0" },
|
|
65
|
+
msgs: [
|
|
66
|
+
{
|
|
67
|
+
type: "sign/MsgSignData",
|
|
68
|
+
value: {
|
|
69
|
+
signer: address,
|
|
70
|
+
data: toBase64Bytes(proof.attestation)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
memo: ""
|
|
75
|
+
};
|
|
76
|
+
const signBytes = await serializeSignDoc(signDoc);
|
|
77
|
+
const digest = algo === "ethsecp256k1" ? keccak_256(signBytes) : sha256(signBytes);
|
|
78
|
+
const verified = secp256k1.verify(signatureBytes, digest, pubKeyBytes);
|
|
79
|
+
return {
|
|
80
|
+
...proof,
|
|
81
|
+
status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED
|
|
82
|
+
};
|
|
83
|
+
} catch {
|
|
84
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { verifyCosmosSignature };
|
|
89
|
+
//# sourceMappingURL=cosmos-AVIWAIX7.js.map
|
|
90
|
+
//# sourceMappingURL=cosmos-AVIWAIX7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cosmos.ts"],"names":[],"mappings":"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","file":"cosmos-AVIWAIX7.js","sourcesContent":["import {\n CosmosMetadata,\n ProofStatus,\n SignatureProof,\n} from \"@notabene/javascript-sdk\";\nimport { bech32 } from \"bech32\";\nimport { StdSignDoc, serializeSignDoc } from \"@cosmjs/amino\";\nimport { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { ripemd160 } from \"@noble/hashes/legacy\";\nimport { sha256 } from \"@noble/hashes/sha2\";\nimport { keccak_256 } from \"@noble/hashes/sha3\";\n\n// Base64 decode\nfunction base64ToUint8Array(base64: string): Uint8Array {\n return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));\n}\n\n// Base64 encode UTF-8 bytes\nfunction toBase64Bytes(str: string): string {\n const utf8 = new TextEncoder().encode(str);\n let binary = \"\";\n for (let i = 0; i < utf8.length; i++) {\n binary += String.fromCharCode(utf8[i]);\n }\n return btoa(binary);\n}\n\n// Cosmos address derivation (ADR-36)\n// https://github.com/cosmos/cosmos-sdk/blob/214b11dcbaa129f7b4c0013b2103db9d54b85e9e/docs/architecture/adr-036-arbitrary-signature.md\nfunction deriveCosmosAddress(pubkey: Uint8Array, prefix: string): string {\n const sha = sha256(pubkey);\n const rip = ripemd160(sha);\n return bech32.encode(prefix, bech32.toWords(rip));\n}\n\n// Ethermint-style chains (Injective, Evmos, ...) use eth-secp256k1 keys: the account is\n// the Ethereum address of the key, bech32-encoded under the chain prefix.\nfunction deriveEthAddress(pubkey: Uint8Array, prefix: string): string {\n const uncompressed = secp256k1.ProjectivePoint.fromHex(pubkey)\n .toRawBytes(false)\n .slice(1);\n const eth = keccak_256(uncompressed).slice(-20);\n return bech32.encode(prefix, bech32.toWords(eth));\n}\n\n// Same selection Keplr's verifyADR36AminoSignDoc makes via its `algo` argument. The signed\n// address tells us which key algorithm the chain uses, so we do not depend on the\n// pub_key.type string wallets put in the amino signature.\ntype CosmosKeyAlgo = \"secp256k1\" | \"ethsecp256k1\";\n\nfunction keyAlgoForAddress(\n pubkey: Uint8Array,\n address: string\n): CosmosKeyAlgo | undefined {\n const prefix = bech32Prefix(address);\n if (deriveCosmosAddress(pubkey, prefix) === address) {\n return \"secp256k1\";\n }\n if (deriveEthAddress(pubkey, prefix) === address) {\n return \"ethsecp256k1\";\n }\n return undefined;\n}\n\n// Each Cosmos SDK chain has its own human-readable part (cosmos, celestia, axelar, ...),\n// and bech32 separates it from the data with the last \"1\" in the string.\nfunction bech32Prefix(address: string): string {\n return address.slice(0, address.lastIndexOf(\"1\"));\n}\n\nexport async function verifyCosmosSignature(\n proof: SignatureProof\n): Promise<SignatureProof> {\n try {\n const [ns, , address] = proof.address.split(/:/);\n if (ns !== \"cosmos\") {\n return { ...proof, status: ProofStatus.FAILED };\n }\n\n // Parse the proof JSON\n const pubKeyBytes = base64ToUint8Array(\n (proof.chainSpecificData as CosmosMetadata).pub_key.value\n );\n const signatureBytes = base64ToUint8Array(proof.proof);\n\n // Step 1: The pubkey must derive to the signed address; which derivation matches\n // also decides the digest used in step 3.\n const algo = keyAlgoForAddress(pubKeyBytes, address);\n if (!algo) {\n return { ...proof, status: ProofStatus.FAILED };\n }\n\n // Step 2: Build MsgSignData + StdSignDoc\n const signDoc: StdSignDoc = {\n chain_id: \"\", // must match the signing process\n account_number: \"0\",\n sequence: \"0\",\n fee: { amount: [], gas: \"0\" },\n msgs: [\n {\n type: \"sign/MsgSignData\",\n value: {\n signer: address,\n data: toBase64Bytes(proof.attestation),\n },\n },\n ],\n memo: \"\",\n };\n\n // Step 3: Serialize + hash (ADR-036 digests are keccak256 on eth-secp256k1 chains)\n const signBytes = await serializeSignDoc(signDoc);\n const digest =\n algo === \"ethsecp256k1\" ? keccak_256(signBytes) : sha256(signBytes);\n\n // Step 4: Verify using secp256k1\n const verified = secp256k1.verify(signatureBytes, digest, pubKeyBytes);\n\n return {\n ...proof,\n status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,\n };\n } catch {\n return { ...proof, status: ProofStatus.FAILED };\n }\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import { ProofStatus } from '@notabene/javascript-sdk';
|
|
3
|
+
import { PersonalMessage, Hex, Signature, Secp256k1, Address } from 'ox';
|
|
4
|
+
|
|
5
|
+
if (typeof globalThis !== 'undefined' && !globalThis.Buffer) { globalThis.Buffer = Buffer; }
|
|
6
|
+
function verifyEIP191(address, message, proof) {
|
|
7
|
+
try {
|
|
8
|
+
const payload = PersonalMessage.getSignPayload(Hex.fromString(message));
|
|
9
|
+
const signature = Signature.fromHex(proof);
|
|
10
|
+
const publicKey = Secp256k1.recoverPublicKey({ payload, signature });
|
|
11
|
+
const recovered = Address.checksum(Address.fromPublicKey(publicKey));
|
|
12
|
+
return recovered.toString() === Address.checksum(address);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async function verifyPersonalSignEIP191(proof) {
|
|
18
|
+
const [ns, , address] = proof.address.split(/:/);
|
|
19
|
+
if (ns !== "eip155") return { ...proof, status: ProofStatus.FAILED };
|
|
20
|
+
const verified = verifyEIP191(
|
|
21
|
+
address,
|
|
22
|
+
proof.attestation,
|
|
23
|
+
proof.proof
|
|
24
|
+
);
|
|
25
|
+
return {
|
|
26
|
+
...proof,
|
|
27
|
+
status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { verifyEIP191, verifyPersonalSignEIP191 };
|
|
32
|
+
//# sourceMappingURL=eth-GI4Z5V4T.js.map
|
|
33
|
+
//# sourceMappingURL=eth-GI4Z5V4T.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/eth.ts"],"names":[],"mappings":";;;;;AAGO,SAAS,YAAA,CACd,OAAA,EACA,OAAA,EACA,KAAA,EACS;AACT,EAAA,IAAI;AACF,IAAA,MAAM,UAAU,eAAA,CAAgB,cAAA,CAAe,GAAA,CAAI,UAAA,CAAW,OAAO,CAAC,CAAA;AACtE,IAAA,MAAM,SAAA,GAAY,SAAA,CAAU,OAAA,CAAQ,KAAK,CAAA;AACzC,IAAA,MAAM,YAAY,SAAA,CAAU,gBAAA,CAAiB,EAAE,OAAA,EAAS,WAAW,CAAA;AACnE,IAAA,MAAM,YAAY,OAAA,CAAQ,QAAA,CAAS,OAAA,CAAQ,aAAA,CAAc,SAAS,CAAC,CAAA;AACnE,IAAA,OAAO,SAAA,CAAU,QAAA,EAAS,KAAM,OAAA,CAAQ,SAAS,OAAO,CAAA;AAAA,EAE1D,SAAS,KAAA,EAAO;AACd,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEA,eAAsB,yBACpB,KAAA,EACyB;AACzB,EAAA,MAAM,CAAC,MAAM,OAAO,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,GAAG,CAAA;AAC/C,EAAA,IAAI,EAAA,KAAO,UAAU,OAAO,EAAE,GAAG,KAAA,EAAO,MAAA,EAAQ,YAAY,MAAA,EAAO;AAEnE,EAAA,MAAM,QAAA,GAAW,YAAA;AAAA,IACf,OAAA;AAAA,IACA,KAAA,CAAM,WAAA;AAAA,IACN,KAAA,CAAM;AAAA,GACR;AACA,EAAA,OAAO;AAAA,IACL,GAAG,KAAA;AAAA,IACH,MAAA,EAAQ,QAAA,GAAW,WAAA,CAAY,QAAA,GAAW,WAAA,CAAY;AAAA,GACxD;AACF","file":"eth-GI4Z5V4T.js","sourcesContent":["import { ProofStatus, SignatureProof } from \"@notabene/javascript-sdk\";\nimport { Secp256k1, Hex, PersonalMessage, Signature, Address } from \"ox\";\n\nexport function verifyEIP191(\n address: Hex.Hex,\n message: string,\n proof: Hex.Hex,\n): boolean {\n try {\n const payload = PersonalMessage.getSignPayload(Hex.fromString(message));\n const signature = Signature.fromHex(proof);\n const publicKey = Secp256k1.recoverPublicKey({ payload, signature });\n const recovered = Address.checksum(Address.fromPublicKey(publicKey));\n return recovered.toString() === Address.checksum(address);\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (error) {\n return false;\n }\n}\n\nexport async function verifyPersonalSignEIP191(\n proof: SignatureProof,\n): Promise<SignatureProof> {\n const [ns, , address] = proof.address.split(/:/);\n if (ns !== \"eip155\") return { ...proof, status: ProofStatus.FAILED };\n\n const verified = verifyEIP191(\n address as Hex.Hex,\n proof.attestation,\n proof.proof as Hex.Hex,\n );\n return {\n ...proof,\n status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,\n };\n}\n"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import { ProofTypes, ProofStatus } from '@notabene/javascript-sdk';
|
|
3
|
+
|
|
4
|
+
if (typeof globalThis !== 'undefined' && !globalThis.Buffer) { globalThis.Buffer = Buffer; }
|
|
5
|
+
async function verifyProof(proof, publicKey) {
|
|
6
|
+
switch (proof.type) {
|
|
7
|
+
case ProofTypes.SelfDeclaration:
|
|
8
|
+
return {
|
|
9
|
+
...proof,
|
|
10
|
+
status: proof.confirmed ? ProofStatus.VERIFIED : ProofStatus.FAILED
|
|
11
|
+
};
|
|
12
|
+
case ProofTypes.Screenshot:
|
|
13
|
+
return {
|
|
14
|
+
...proof,
|
|
15
|
+
status: proof.url ? ProofStatus.FLAGGED : ProofStatus.FAILED
|
|
16
|
+
};
|
|
17
|
+
case ProofTypes.CIP8: {
|
|
18
|
+
const { verifyCIP8Signature } = await import('./cardano-OMGAPH5N.js');
|
|
19
|
+
return verifyCIP8Signature(proof);
|
|
20
|
+
}
|
|
21
|
+
case ProofTypes.EIP191: {
|
|
22
|
+
const { verifyPersonalSignEIP191 } = await import('./eth-GI4Z5V4T.js');
|
|
23
|
+
return verifyPersonalSignEIP191(proof);
|
|
24
|
+
}
|
|
25
|
+
case ProofTypes.ED25519: {
|
|
26
|
+
const { verifySolanaSignature } = await import('./solana-ZARDAF6O.js');
|
|
27
|
+
return verifySolanaSignature(proof);
|
|
28
|
+
}
|
|
29
|
+
case ProofTypes.SOL_SIWX: {
|
|
30
|
+
const { verifySolanaSIWS } = await import('./solana-ZARDAF6O.js');
|
|
31
|
+
return verifySolanaSIWS(proof);
|
|
32
|
+
}
|
|
33
|
+
case ProofTypes.XRP_ED25519: {
|
|
34
|
+
const { verifyPersonalSignXRPL } = await import('./xrpl-52IBJ4EW.js');
|
|
35
|
+
return verifyPersonalSignXRPL(proof, publicKey);
|
|
36
|
+
}
|
|
37
|
+
case ProofTypes.XLM_ED25519: {
|
|
38
|
+
const { verifyStellarSignature } = await import('./xlm-TNRZ5CFC.js');
|
|
39
|
+
return verifyStellarSignature(proof);
|
|
40
|
+
}
|
|
41
|
+
case ProofTypes.CONCORDIUM: {
|
|
42
|
+
const { verifyConcordiumSignature } = await import('./concordium-JJ5VEIOA.js');
|
|
43
|
+
return verifyConcordiumSignature(proof);
|
|
44
|
+
}
|
|
45
|
+
case ProofTypes.COSMOS: {
|
|
46
|
+
const { verifyCosmosSignature } = await import('./cosmos-AVIWAIX7.js');
|
|
47
|
+
return verifyCosmosSignature(proof);
|
|
48
|
+
}
|
|
49
|
+
case ProofTypes.EIP712:
|
|
50
|
+
case ProofTypes.BIP137:
|
|
51
|
+
case ProofTypes.BIP322: {
|
|
52
|
+
const { verifyBTCSignature } = await import('./bitcoin-BTZ4TXEK.js');
|
|
53
|
+
return verifyBTCSignature(proof);
|
|
54
|
+
}
|
|
55
|
+
case ProofTypes.TIP191: {
|
|
56
|
+
const { verifyPersonalSignTIP191 } = await import('./tron-FI2B6DRS.js');
|
|
57
|
+
return verifyPersonalSignTIP191(proof);
|
|
58
|
+
}
|
|
59
|
+
case ProofTypes.MicroTransfer:
|
|
60
|
+
}
|
|
61
|
+
return proof;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { verifyProof };
|
|
65
|
+
//# sourceMappingURL=index.js.map
|
|
66
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts"],"names":[],"mappings":";;;;AAUA,eAAsB,WAAA,CACpB,OACA,SAAA,EACyB;AACzB,EAAA,QAAQ,MAAM,IAAA;AAAM,IAClB,KAAK,UAAA,CAAW,eAAA;AACd,MAAA,OAAO;AAAA,QACL,GAAG,KAAA;AAAA,QACH,MAAA,EAAS,KAAA,CAA2B,SAAA,GAChC,WAAA,CAAY,WACZ,WAAA,CAAY;AAAA,OAClB;AAAA,IACF,KAAK,UAAA,CAAW,UAAA;AACd,MAAA,OAAO;AAAA,QACL,GAAG,KAAA;AAAA,QACH,MAAA,EAAS,KAAA,CAA0B,GAAA,GAC/B,WAAA,CAAY,UACZ,WAAA,CAAY;AAAA,OAClB;AAAA,IACF,KAAK,WAAW,IAAA,EAAM;AACpB,MAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,MAAM,OAAO,uBAAW,CAAA;AACxD,MAAA,OAAO,oBAAoB,KAAuB,CAAA;AAAA,IACpD;AAAA,IACA,KAAK,WAAW,MAAA,EAAQ;AACtB,MAAA,MAAM,EAAE,wBAAA,EAAyB,GAAI,MAAM,OAAO,mBAAO,CAAA;AACzD,MAAA,OAAO,yBAAyB,KAAuB,CAAA;AAAA,IACzD;AAAA,IACA,KAAK,WAAW,OAAA,EAAS;AACvB,MAAA,MAAM,EAAE,qBAAA,EAAsB,GAAI,MAAM,OAAO,sBAAU,CAAA;AACzD,MAAA,OAAO,sBAAsB,KAAuB,CAAA;AAAA,IACtD;AAAA,IACA,KAAK,WAAW,QAAA,EAAU;AACxB,MAAA,MAAM,EAAE,gBAAA,EAAiB,GAAI,MAAM,OAAO,sBAAU,CAAA;AACpD,MAAA,OAAO,iBAAiB,KAAuB,CAAA;AAAA,IACjD;AAAA,IACA,KAAK,WAAW,WAAA,EAAa;AAC3B,MAAA,MAAM,EAAE,sBAAA,EAAuB,GAAI,MAAM,OAAO,oBAAQ,CAAA;AACxD,MAAA,OAAO,sBAAA,CAAuB,OAAyB,SAAS,CAAA;AAAA,IAClE;AAAA,IACA,KAAK,WAAW,WAAA,EAAa;AAC3B,MAAA,MAAM,EAAE,sBAAA,EAAuB,GAAI,MAAM,OAAO,mBAAO,CAAA;AACvD,MAAA,OAAO,uBAAuB,KAAuB,CAAA;AAAA,IACvD;AAAA,IACA,KAAK,WAAW,UAAA,EAAY;AAC1B,MAAA,MAAM,EAAE,yBAAA,EAA0B,GAAI,MAAM,OAAO,0BAAc,CAAA;AACjE,MAAA,OAAO,0BAA0B,KAAuB,CAAA;AAAA,IAC1D;AAAA,IACA,KAAK,WAAW,MAAA,EAAQ;AACtB,MAAA,MAAM,EAAE,qBAAA,EAAsB,GAAI,MAAM,OAAO,sBAAU,CAAA;AACzD,MAAA,OAAO,sBAAsB,KAAuB,CAAA;AAAA,IACtD;AAAA,IACA,KAAK,UAAA,CAAW,MAAA;AAAA,IAChB,KAAK,UAAA,CAAW,MAAA;AAAA,IAChB,KAAK,WAAW,MAAA,EAAQ;AACtB,MAAA,MAAM,EAAE,kBAAA,EAAmB,GAAI,MAAM,OAAO,uBAAW,CAAA;AACvD,MAAA,OAAO,mBAAmB,KAAuB,CAAA;AAAA,IACnD;AAAA,IACA,KAAK,WAAW,MAAA,EAAQ;AACtB,MAAA,MAAM,EAAE,wBAAA,EAAyB,GAAI,MAAM,OAAO,oBAAQ,CAAA;AAC1D,MAAA,OAAO,yBAAyB,KAAuB,CAAA;AAAA,IACzD;AAAA,IACA,KAAK,UAAA,CAAW,aAAA;AAAA;AAElB,EAAA,OAAO,KAAA;AACT","file":"index.js","sourcesContent":["import {\n type OwnershipProof,\n SignatureProof,\n DeclarationProof,\n ScreenshotProof,\n ProofTypes,\n ProofStatus,\n} from \"@notabene/javascript-sdk\";\n\n// Use dynamic imports to ensure Buffer is available before loading crypto libraries\nexport async function verifyProof(\n proof: OwnershipProof,\n publicKey?: string,\n): Promise<OwnershipProof> {\n switch (proof.type) {\n case ProofTypes.SelfDeclaration:\n return {\n ...proof,\n status: (proof as DeclarationProof).confirmed\n ? ProofStatus.VERIFIED\n : ProofStatus.FAILED,\n };\n case ProofTypes.Screenshot:\n return {\n ...proof,\n status: (proof as ScreenshotProof).url\n ? ProofStatus.FLAGGED\n : ProofStatus.FAILED,\n };\n case ProofTypes.CIP8: {\n const { verifyCIP8Signature } = await import(\"./cardano\");\n return verifyCIP8Signature(proof as SignatureProof);\n }\n case ProofTypes.EIP191: {\n const { verifyPersonalSignEIP191 } = await import(\"./eth\");\n return verifyPersonalSignEIP191(proof as SignatureProof);\n }\n case ProofTypes.ED25519: {\n const { verifySolanaSignature } = await import(\"./solana\");\n return verifySolanaSignature(proof as SignatureProof);\n }\n case ProofTypes.SOL_SIWX: {\n const { verifySolanaSIWS } = await import(\"./solana\");\n return verifySolanaSIWS(proof as SignatureProof);\n }\n case ProofTypes.XRP_ED25519: {\n const { verifyPersonalSignXRPL } = await import(\"./xrpl\");\n return verifyPersonalSignXRPL(proof as SignatureProof, publicKey);\n }\n case ProofTypes.XLM_ED25519: {\n const { verifyStellarSignature } = await import(\"./xlm\");\n return verifyStellarSignature(proof as SignatureProof);\n }\n case ProofTypes.CONCORDIUM: {\n const { verifyConcordiumSignature } = await import(\"./concordium\");\n return verifyConcordiumSignature(proof as SignatureProof);\n }\n case ProofTypes.COSMOS: {\n const { verifyCosmosSignature } = await import(\"./cosmos\");\n return verifyCosmosSignature(proof as SignatureProof);\n }\n case ProofTypes.EIP712:\n case ProofTypes.BIP137:\n case ProofTypes.BIP322: {\n const { verifyBTCSignature } = await import(\"./bitcoin\");\n return verifyBTCSignature(proof as SignatureProof);\n }\n case ProofTypes.TIP191: {\n const { verifyPersonalSignTIP191 } = await import(\"./tron\");\n return verifyPersonalSignTIP191(proof as SignatureProof);\n }\n case ProofTypes.MicroTransfer:\n }\n return proof;\n}\n"]}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import nacl from 'tweetnacl';
|
|
3
|
+
import { ProofStatus } from '@notabene/javascript-sdk';
|
|
4
|
+
import { base58, base64 } from '@scure/base';
|
|
5
|
+
|
|
6
|
+
if (typeof globalThis !== 'undefined' && !globalThis.Buffer) { globalThis.Buffer = Buffer; }
|
|
7
|
+
async function verifySolanaSignature(proof) {
|
|
8
|
+
const [ns, , address] = proof.address.split(/:/);
|
|
9
|
+
if (ns !== "solana") return { ...proof, status: ProofStatus.FAILED };
|
|
10
|
+
try {
|
|
11
|
+
const publicKey = base58.decode(address);
|
|
12
|
+
const messageBytes = new TextEncoder().encode(proof.attestation);
|
|
13
|
+
const signatureBytes = base64.decode(proof.proof);
|
|
14
|
+
const verified = nacl.sign.detached.verify(
|
|
15
|
+
messageBytes,
|
|
16
|
+
signatureBytes,
|
|
17
|
+
publicKey
|
|
18
|
+
);
|
|
19
|
+
return {
|
|
20
|
+
...proof,
|
|
21
|
+
status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED
|
|
22
|
+
};
|
|
23
|
+
} catch {
|
|
24
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function isSolanaSignInInput(obj) {
|
|
28
|
+
if (!obj || typeof obj !== "object") return false;
|
|
29
|
+
const input = obj;
|
|
30
|
+
return typeof input.domain === "string" && typeof input.address === "string" && (input.statement === void 0 || typeof input.statement === "string") && (input.uri === void 0 || typeof input.uri === "string") && (input.version === void 0 || typeof input.version === "string") && (input.chainId === void 0 || typeof input.chainId === "string") && (input.nonce === void 0 || typeof input.nonce === "string") && (input.issuedAt === void 0 || typeof input.issuedAt === "string") && (input.expirationTime === void 0 || typeof input.expirationTime === "string") && (input.notBefore === void 0 || typeof input.notBefore === "string") && (input.requestId === void 0 || typeof input.requestId === "string") && (input.resources === void 0 || Array.isArray(input.resources));
|
|
31
|
+
}
|
|
32
|
+
function isSolanaSignInMetadata(obj) {
|
|
33
|
+
if (!obj || typeof obj !== "object") return false;
|
|
34
|
+
const metadata = obj;
|
|
35
|
+
if (!metadata.account || typeof metadata.account !== "object") return false;
|
|
36
|
+
const account = metadata.account;
|
|
37
|
+
if (typeof account.address !== "string") return false;
|
|
38
|
+
if (!account.publicKey) return false;
|
|
39
|
+
if (!(account.publicKey instanceof Uint8Array)) {
|
|
40
|
+
const pkObj = account.publicKey;
|
|
41
|
+
if (typeof pkObj === "object") {
|
|
42
|
+
const keys = Object.keys(pkObj).filter((key) => !isNaN(Number(key))).sort((a, b) => Number(a) - Number(b));
|
|
43
|
+
if (keys.length === 32) {
|
|
44
|
+
const bytes = keys.map((key) => Number(pkObj[key]));
|
|
45
|
+
if (bytes.every((b) => typeof b === "number" && b >= 0 && b <= 255)) {
|
|
46
|
+
account.publicKey = new Uint8Array(bytes);
|
|
47
|
+
} else {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!metadata.signedMessage) return false;
|
|
58
|
+
if (!(metadata.signedMessage instanceof Uint8Array)) {
|
|
59
|
+
const smObj = metadata.signedMessage;
|
|
60
|
+
if (smObj.type === "Buffer" && Array.isArray(smObj.data)) {
|
|
61
|
+
metadata.signedMessage = new Uint8Array(smObj.data);
|
|
62
|
+
} else {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!metadata.signature) return false;
|
|
67
|
+
if (!(metadata.signature instanceof Uint8Array)) {
|
|
68
|
+
const sigObj = metadata.signature;
|
|
69
|
+
if (sigObj.type === "Buffer" && Array.isArray(sigObj.data)) {
|
|
70
|
+
metadata.signature = new Uint8Array(sigObj.data);
|
|
71
|
+
} else {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (!metadata.message || typeof metadata.message !== "object") {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const message = metadata.message;
|
|
79
|
+
if (typeof message === "object" && message !== null) {
|
|
80
|
+
const messageObj = message;
|
|
81
|
+
if (!messageObj.address && metadata.signedMessage instanceof Uint8Array) {
|
|
82
|
+
try {
|
|
83
|
+
const signedMessageText = new TextDecoder().decode(metadata.signedMessage);
|
|
84
|
+
const lines = signedMessageText.split("\n");
|
|
85
|
+
if (lines.length >= 2) {
|
|
86
|
+
const address = lines[1].trim();
|
|
87
|
+
if (address && /^[a-zA-Z0-9]{32,44}$/.test(address)) {
|
|
88
|
+
messageObj.address = address;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!isSolanaSignInInput(metadata.message)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
async function verifySolanaSIWS(proof) {
|
|
101
|
+
const [ns] = proof.address.split(/:/);
|
|
102
|
+
if (ns !== "solana") {
|
|
103
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
104
|
+
}
|
|
105
|
+
if (!proof.chainSpecificData || !isSolanaSignInMetadata(proof.chainSpecificData)) {
|
|
106
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const metadata = proof.chainSpecificData;
|
|
110
|
+
const signedMessageText = new TextDecoder().decode(metadata.signedMessage);
|
|
111
|
+
const parsedMessage = parseSIWSMessage(signedMessageText);
|
|
112
|
+
if (!parsedMessage) {
|
|
113
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
114
|
+
}
|
|
115
|
+
if (!validateSIWSMessage(parsedMessage, metadata.message)) {
|
|
116
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
117
|
+
}
|
|
118
|
+
const reconstructedMessage = createSIWSMessage(parsedMessage);
|
|
119
|
+
if (reconstructedMessage !== signedMessageText) {
|
|
120
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
121
|
+
}
|
|
122
|
+
const verified = nacl.sign.detached.verify(
|
|
123
|
+
metadata.signedMessage,
|
|
124
|
+
metadata.signature,
|
|
125
|
+
metadata.account.publicKey
|
|
126
|
+
);
|
|
127
|
+
return {
|
|
128
|
+
...proof,
|
|
129
|
+
status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED
|
|
130
|
+
};
|
|
131
|
+
} catch {
|
|
132
|
+
return { ...proof, status: ProofStatus.FAILED };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function parseSIWSMessage(message) {
|
|
136
|
+
try {
|
|
137
|
+
const lines = message.split("\n");
|
|
138
|
+
const header = parseHeader(lines);
|
|
139
|
+
if (!header) return null;
|
|
140
|
+
const result = { ...header };
|
|
141
|
+
let lineIndex = 2;
|
|
142
|
+
const statementResult = parseStatement(lines, lineIndex);
|
|
143
|
+
if (statementResult.statement !== void 0) {
|
|
144
|
+
result.statement = statementResult.statement;
|
|
145
|
+
lineIndex = statementResult.nextIndex;
|
|
146
|
+
}
|
|
147
|
+
const advancedFields = parseAdvancedFields(lines, lineIndex);
|
|
148
|
+
Object.assign(result, advancedFields);
|
|
149
|
+
return result;
|
|
150
|
+
} catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function parseHeader(lines) {
|
|
155
|
+
const domainMatch = lines[0]?.match(/^(.+) wants you to sign in with your Solana account:$/);
|
|
156
|
+
if (!domainMatch) return null;
|
|
157
|
+
const domain = domainMatch[1];
|
|
158
|
+
const address = lines[1];
|
|
159
|
+
if (!address || !/^[a-zA-Z0-9]{32,44}$/.test(address)) return null;
|
|
160
|
+
return { domain, address };
|
|
161
|
+
}
|
|
162
|
+
function parseStatement(lines, startIndex) {
|
|
163
|
+
let lineIndex = startIndex;
|
|
164
|
+
if (lines[lineIndex] === "" && lines[lineIndex + 1] && !lines[lineIndex + 1].includes(":")) {
|
|
165
|
+
lineIndex++;
|
|
166
|
+
const statement = lines[lineIndex];
|
|
167
|
+
lineIndex++;
|
|
168
|
+
if (lines[lineIndex] === "") {
|
|
169
|
+
lineIndex++;
|
|
170
|
+
}
|
|
171
|
+
return { statement, nextIndex: lineIndex };
|
|
172
|
+
}
|
|
173
|
+
return { nextIndex: lineIndex };
|
|
174
|
+
}
|
|
175
|
+
function parseAdvancedFields(lines, startIndex) {
|
|
176
|
+
const result = {};
|
|
177
|
+
const fieldParsers = [
|
|
178
|
+
{ prefix: "URI: ", key: "uri" },
|
|
179
|
+
{ prefix: "Version: ", key: "version" },
|
|
180
|
+
{ prefix: "Chain ID: ", key: "chainId" },
|
|
181
|
+
{ prefix: "Nonce: ", key: "nonce" },
|
|
182
|
+
{ prefix: "Issued At: ", key: "issuedAt" },
|
|
183
|
+
{ prefix: "Expiration Time: ", key: "expirationTime" },
|
|
184
|
+
{ prefix: "Not Before: ", key: "notBefore" },
|
|
185
|
+
{ prefix: "Request ID: ", key: "requestId" }
|
|
186
|
+
];
|
|
187
|
+
let lineIndex = startIndex;
|
|
188
|
+
while (lineIndex < lines.length) {
|
|
189
|
+
const line = lines[lineIndex];
|
|
190
|
+
if (!line) {
|
|
191
|
+
lineIndex++;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (line.startsWith("Resources:")) {
|
|
195
|
+
const resources = parseResources(lines, lineIndex + 1);
|
|
196
|
+
if (resources.length > 0) {
|
|
197
|
+
result.resources = resources;
|
|
198
|
+
lineIndex += resources.length + 1;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
for (const { prefix, key } of fieldParsers) {
|
|
203
|
+
if (line.startsWith(prefix)) {
|
|
204
|
+
const value = line.substring(prefix.length);
|
|
205
|
+
result[key] = value;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
lineIndex++;
|
|
210
|
+
}
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
function parseResources(lines, startIndex) {
|
|
214
|
+
const resources = [];
|
|
215
|
+
let lineIndex = startIndex;
|
|
216
|
+
while (lineIndex < lines.length && lines[lineIndex]?.startsWith("- ")) {
|
|
217
|
+
resources.push(lines[lineIndex].substring(2));
|
|
218
|
+
lineIndex++;
|
|
219
|
+
}
|
|
220
|
+
return resources;
|
|
221
|
+
}
|
|
222
|
+
function validateSIWSMessage(parsed, input) {
|
|
223
|
+
if (parsed.domain !== input.domain || parsed.address !== input.address) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
const fieldValidations = [
|
|
227
|
+
{ inputKey: "statement", parsedKey: "statement" },
|
|
228
|
+
{ inputKey: "uri", parsedKey: "uri" },
|
|
229
|
+
{ inputKey: "version", parsedKey: "version" },
|
|
230
|
+
{ inputKey: "chainId", parsedKey: "chainId" },
|
|
231
|
+
{ inputKey: "nonce", parsedKey: "nonce" },
|
|
232
|
+
{ inputKey: "issuedAt", parsedKey: "issuedAt" },
|
|
233
|
+
{ inputKey: "expirationTime", parsedKey: "expirationTime" },
|
|
234
|
+
{ inputKey: "notBefore", parsedKey: "notBefore" },
|
|
235
|
+
{ inputKey: "requestId", parsedKey: "requestId" },
|
|
236
|
+
{
|
|
237
|
+
inputKey: "resources",
|
|
238
|
+
parsedKey: "resources",
|
|
239
|
+
validator: (inputValue, parsedValue) => {
|
|
240
|
+
if (!Array.isArray(inputValue) || !Array.isArray(parsedValue)) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
return inputValue.length === parsedValue.length && inputValue.every((item, index) => item === parsedValue[index]);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
];
|
|
247
|
+
for (const { inputKey, parsedKey, validator } of fieldValidations) {
|
|
248
|
+
const inputValue = input[inputKey];
|
|
249
|
+
const parsedValue = parsed[parsedKey];
|
|
250
|
+
if (inputValue !== void 0) {
|
|
251
|
+
if (validator) {
|
|
252
|
+
if (!validator(inputValue, parsedValue)) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
} else if (inputValue !== parsedValue) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return validateTimestamps(parsed);
|
|
261
|
+
}
|
|
262
|
+
function validateTimestamps(parsed) {
|
|
263
|
+
const now = Date.now();
|
|
264
|
+
if (parsed.issuedAt) {
|
|
265
|
+
const issuedAt = new Date(parsed.issuedAt);
|
|
266
|
+
const threshold = 24 * 60 * 60 * 1e3;
|
|
267
|
+
const timeDiff = Math.abs(issuedAt.getTime() - now);
|
|
268
|
+
if (timeDiff > threshold) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (parsed.expirationTime) {
|
|
273
|
+
const expirationTime = new Date(parsed.expirationTime);
|
|
274
|
+
if (expirationTime.getTime() <= now) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (parsed.notBefore) {
|
|
279
|
+
const notBefore = new Date(parsed.notBefore);
|
|
280
|
+
if (notBefore.getTime() > now) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
function createSIWSMessage(input) {
|
|
287
|
+
let message = `${input.domain} wants you to sign in with your Solana account:
|
|
288
|
+
`;
|
|
289
|
+
message += `${input.address}`;
|
|
290
|
+
if (input.statement) {
|
|
291
|
+
message += `
|
|
292
|
+
|
|
293
|
+
${input.statement}`;
|
|
294
|
+
}
|
|
295
|
+
const fields = buildFieldLines(input);
|
|
296
|
+
if (fields.length) {
|
|
297
|
+
message += `
|
|
298
|
+
|
|
299
|
+
${fields.join("\n")}`;
|
|
300
|
+
}
|
|
301
|
+
return message;
|
|
302
|
+
}
|
|
303
|
+
function buildFieldLines(input) {
|
|
304
|
+
const fields = [];
|
|
305
|
+
const fieldMappings = [
|
|
306
|
+
{ key: "uri", prefix: "URI: " },
|
|
307
|
+
{ key: "version", prefix: "Version: " },
|
|
308
|
+
{ key: "chainId", prefix: "Chain ID: " },
|
|
309
|
+
{ key: "nonce", prefix: "Nonce: " },
|
|
310
|
+
{ key: "issuedAt", prefix: "Issued At: " },
|
|
311
|
+
{ key: "expirationTime", prefix: "Expiration Time: " },
|
|
312
|
+
{ key: "notBefore", prefix: "Not Before: " },
|
|
313
|
+
{ key: "requestId", prefix: "Request ID: " },
|
|
314
|
+
{
|
|
315
|
+
key: "resources",
|
|
316
|
+
prefix: "Resources:",
|
|
317
|
+
formatter: (value) => {
|
|
318
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
319
|
+
return ["Resources:", ...value.map((resource) => `- ${resource}`)];
|
|
320
|
+
}
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
];
|
|
325
|
+
for (const { key, prefix, formatter } of fieldMappings) {
|
|
326
|
+
const value = input[key];
|
|
327
|
+
if (value !== void 0) {
|
|
328
|
+
if (formatter) {
|
|
329
|
+
const formatted = formatter(value);
|
|
330
|
+
fields.push(...formatted);
|
|
331
|
+
} else if (typeof value === "string") {
|
|
332
|
+
fields.push(`${prefix}${value}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return fields;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export { verifySolanaSIWS, verifySolanaSignature };
|
|
340
|
+
//# sourceMappingURL=solana-ZARDAF6O.js.map
|
|
341
|
+
//# sourceMappingURL=solana-ZARDAF6O.js.map
|