@orangecheck/sdk 0.1.0 → 0.1.2
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/attestation.js +26 -2
- package/dist/attestation.js.map +1 -1
- package/dist/attestation.mjs +26 -2
- package/dist/attestation.mjs.map +1 -1
- package/dist/canonical.js +27 -5
- package/dist/canonical.js.map +1 -1
- package/dist/canonical.mjs +27 -5
- package/dist/canonical.mjs.map +1 -1
- package/dist/identity.js +130 -44
- package/dist/identity.js.map +1 -1
- package/dist/identity.mjs +130 -44
- package/dist/identity.mjs.map +1 -1
- package/dist/index.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +200 -57
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +199 -58
- package/dist/index.mjs.map +1 -1
- package/dist/verify.js +42 -8
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +42 -8
- package/dist/verify.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
var buffer = require('buffer');
|
|
4
4
|
var bip322Js = require('bip322-js');
|
|
5
5
|
var bitcoinMessage = require('bitcoinjs-message');
|
|
6
|
+
var secp256k1 = require('@noble/curves/secp256k1');
|
|
7
|
+
var sha2 = require('@noble/hashes/sha2');
|
|
8
|
+
var base = require('@scure/base');
|
|
6
9
|
|
|
7
10
|
function _interopNamespace(e) {
|
|
8
11
|
if (e && e.__esModule) return e;
|
|
@@ -37,15 +40,27 @@ function randomNonce16BHexLower() {
|
|
|
37
40
|
}
|
|
38
41
|
} catch {
|
|
39
42
|
}
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
throw new Error("crypto.getRandomValues unavailable \u2014 refusing to mint a weak nonce");
|
|
44
|
+
}
|
|
45
|
+
var IDENTITY_PROTOCOL_RE = /^[a-z][a-z0-9_-]*$/;
|
|
46
|
+
var IDENTITY_FORBIDDEN_CHARS = /[\r\n,]/;
|
|
47
|
+
function assertSafeIdentity({ protocol, identifier }) {
|
|
48
|
+
if (!IDENTITY_PROTOCOL_RE.test(protocol)) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`invalid identity protocol: ${JSON.stringify(protocol)} \u2014 must match /^[a-z][a-z0-9_-]*$/`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (!identifier || IDENTITY_FORBIDDEN_CHARS.test(identifier)) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`invalid identity identifier: ${JSON.stringify(identifier)} \u2014 must be non-empty and contain no newlines or commas`
|
|
56
|
+
);
|
|
42
57
|
}
|
|
43
|
-
return toLowerHex(arr);
|
|
44
58
|
}
|
|
45
59
|
function formatIdentities(identities = []) {
|
|
46
60
|
if (identities.length === 0) {
|
|
47
61
|
return "";
|
|
48
62
|
}
|
|
63
|
+
for (const id of identities) assertSafeIdentity(id);
|
|
49
64
|
const formatted = identities.map(({ protocol, identifier }) => `${protocol}:${identifier}`).sort();
|
|
50
65
|
return formatted.join(",");
|
|
51
66
|
}
|
|
@@ -53,7 +68,10 @@ function parseIdentities(identitiesStr) {
|
|
|
53
68
|
if (!identitiesStr || identitiesStr.trim() === "") {
|
|
54
69
|
return [];
|
|
55
70
|
}
|
|
56
|
-
|
|
71
|
+
if (IDENTITY_FORBIDDEN_CHARS.test(identitiesStr.replace(/,/g, ""))) {
|
|
72
|
+
throw new Error("identities field contains forbidden characters (newline)");
|
|
73
|
+
}
|
|
74
|
+
const bindings = identitiesStr.split(",").map((binding) => {
|
|
57
75
|
const colonIndex = binding.indexOf(":");
|
|
58
76
|
if (colonIndex === -1) {
|
|
59
77
|
throw new Error(`Invalid identity binding format: ${binding}`);
|
|
@@ -62,7 +80,14 @@ function parseIdentities(identitiesStr) {
|
|
|
62
80
|
protocol: binding.substring(0, colonIndex).trim(),
|
|
63
81
|
identifier: binding.substring(colonIndex + 1).trim()
|
|
64
82
|
};
|
|
65
|
-
})
|
|
83
|
+
});
|
|
84
|
+
for (const b of bindings) {
|
|
85
|
+
if (!b.protocol || !b.identifier) {
|
|
86
|
+
throw new Error(`Invalid identity binding: ${b.protocol}:${b.identifier}`);
|
|
87
|
+
}
|
|
88
|
+
assertSafeIdentity(b);
|
|
89
|
+
}
|
|
90
|
+
return bindings;
|
|
66
91
|
}
|
|
67
92
|
function buildCanonicalMessage({ address, identities }, extensions = {}) {
|
|
68
93
|
const nonce = randomNonce16BHexLower();
|
|
@@ -498,6 +523,21 @@ function isNip07Available() {
|
|
|
498
523
|
}
|
|
499
524
|
return !!window.nostr;
|
|
500
525
|
}
|
|
526
|
+
async function getNostrPublicKey() {
|
|
527
|
+
if (!isNip07Available()) {
|
|
528
|
+
log2.warn("NIP-07 extension not available");
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
try {
|
|
532
|
+
const nostr = window.nostr;
|
|
533
|
+
const pubkey = await nostr.getPublicKey();
|
|
534
|
+
log2.info({ pubkey }, "Got Nostr public key from NIP-07");
|
|
535
|
+
return pubkey;
|
|
536
|
+
} catch (err) {
|
|
537
|
+
log2.error({ error: err }, "Failed to get Nostr public key");
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
501
541
|
async function signNostrEvent(event) {
|
|
502
542
|
if (!isNip07Available()) {
|
|
503
543
|
log2.warn("NIP-07 extension not available");
|
|
@@ -513,6 +553,17 @@ async function signNostrEvent(event) {
|
|
|
513
553
|
return null;
|
|
514
554
|
}
|
|
515
555
|
}
|
|
556
|
+
function getNip07Info() {
|
|
557
|
+
if (!isNip07Available()) {
|
|
558
|
+
return { available: false };
|
|
559
|
+
}
|
|
560
|
+
const nostr = window.nostr;
|
|
561
|
+
return {
|
|
562
|
+
available: true,
|
|
563
|
+
name: nostr._metadata?.name,
|
|
564
|
+
version: nostr._metadata?.version
|
|
565
|
+
};
|
|
566
|
+
}
|
|
516
567
|
|
|
517
568
|
// src/attestation.ts
|
|
518
569
|
var log3 = createLogger("ocp/attestation");
|
|
@@ -773,7 +824,13 @@ async function verifySignature({
|
|
|
773
824
|
}
|
|
774
825
|
function parseCanonicalMessage(msg) {
|
|
775
826
|
const lines = msg.split("\n").map((l) => l.trim());
|
|
776
|
-
const getVal = (prefix) =>
|
|
827
|
+
const getVal = (prefix) => {
|
|
828
|
+
const needle = prefix + ":";
|
|
829
|
+
const hit = lines.find((l) => l.toLowerCase().startsWith(needle));
|
|
830
|
+
if (!hit) return void 0;
|
|
831
|
+
const idx = hit.indexOf(":");
|
|
832
|
+
return hit.slice(idx + 1).trim();
|
|
833
|
+
};
|
|
777
834
|
const address = getVal("address") || "";
|
|
778
835
|
const identitiesStr = getVal("identities") || "";
|
|
779
836
|
let identities = [];
|
|
@@ -828,8 +885,7 @@ async function getAddressUtxos(address, network, opts) {
|
|
|
828
885
|
const resp = await fetch(url, {
|
|
829
886
|
method: "GET",
|
|
830
887
|
headers: { Accept: "application/json" },
|
|
831
|
-
signal: AbortSignal.timeout(
|
|
832
|
-
// 10s timeout
|
|
888
|
+
signal: AbortSignal.timeout(5e3)
|
|
833
889
|
});
|
|
834
890
|
if (!resp.ok) {
|
|
835
891
|
throw new Error(`HTTP ${resp.status}`);
|
|
@@ -934,13 +990,18 @@ async function verify2(input, opts = {}) {
|
|
|
934
990
|
codes.push("network_testmode");
|
|
935
991
|
return { ok: false, codes, network, attestation_id };
|
|
936
992
|
}
|
|
937
|
-
if (opts.expectedAud
|
|
938
|
-
|
|
993
|
+
if (opts.expectedAud) {
|
|
994
|
+
const aud = parsed.extensions["aud"];
|
|
995
|
+
if (!aud || aud !== opts.expectedAud) {
|
|
996
|
+
codes.push("aud_mismatch");
|
|
997
|
+
return { ok: false, codes, network, attestation_id };
|
|
998
|
+
}
|
|
939
999
|
}
|
|
940
1000
|
if (parsed.extensions["expires"]) {
|
|
941
1001
|
const exp = Date.parse(parsed.extensions["expires"]);
|
|
942
1002
|
if (!Number.isNaN(exp) && exp < Date.now()) {
|
|
943
1003
|
codes.push("expired");
|
|
1004
|
+
return { ok: false, codes, network, attestation_id };
|
|
944
1005
|
}
|
|
945
1006
|
}
|
|
946
1007
|
const sigCode = await verifySignature({
|
|
@@ -1018,7 +1079,7 @@ async function verify2(input, opts = {}) {
|
|
|
1018
1079
|
metrics
|
|
1019
1080
|
};
|
|
1020
1081
|
} catch (utxoError) {
|
|
1021
|
-
|
|
1082
|
+
log4.error({ err: utxoError }, "UTXO fetch failed");
|
|
1022
1083
|
codes.push("bad_request");
|
|
1023
1084
|
return { ok: false, codes, network, attestation_id };
|
|
1024
1085
|
}
|
|
@@ -1268,9 +1329,25 @@ var identityVerificationLimiter = createRateLimiter(5, 6e4);
|
|
|
1268
1329
|
|
|
1269
1330
|
// src/identity/dns.ts
|
|
1270
1331
|
var log5 = createLogger("identity-verification/dns");
|
|
1332
|
+
var PRIVATE_HOST_RE = /^(?:localhost|.*\.local|.*\.internal|.*\.localhost|.*\.lan|.*\.home|.*\.arpa|\[?::1\]?|\[?::\]?)$/i;
|
|
1333
|
+
var PRIVATE_IPV4_RE = /^(?:10\.|127\.|169\.254\.|192\.168\.|172\.(?:1[6-9]|2\d|3[01])\.|0\.|100\.(?:6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.|22[4-9]\.|23\d\.|24\d\.|25[0-5]\.)/;
|
|
1334
|
+
var WELL_KNOWN_TIMEOUT_MS = 5e3;
|
|
1335
|
+
function isSafePublicHost(host) {
|
|
1336
|
+
const h = host.toLowerCase();
|
|
1337
|
+
if (PRIVATE_HOST_RE.test(h)) return false;
|
|
1338
|
+
if (PRIVATE_IPV4_RE.test(h)) return false;
|
|
1339
|
+
if (h.startsWith("[") || /^[0-9a-f:]+$/.test(h)) return false;
|
|
1340
|
+
return true;
|
|
1341
|
+
}
|
|
1271
1342
|
async function verifyDnsIdentity(attestationId, domain) {
|
|
1272
1343
|
log5.info({ attestationId, domain }, "Verifying DNS identity");
|
|
1273
1344
|
const cleanDomain = domain.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
1345
|
+
if (!isValidDomain(cleanDomain)) {
|
|
1346
|
+
return { verified: false, error: `invalid domain format: ${cleanDomain}` };
|
|
1347
|
+
}
|
|
1348
|
+
if (!isSafePublicHost(cleanDomain)) {
|
|
1349
|
+
return { verified: false, error: "domain resolves to a disallowed private host" };
|
|
1350
|
+
}
|
|
1274
1351
|
const wellKnownResult = await checkWellKnownFile(attestationId, cleanDomain);
|
|
1275
1352
|
if (wellKnownResult.verified) {
|
|
1276
1353
|
return wellKnownResult;
|
|
@@ -1282,38 +1359,31 @@ async function verifyDnsIdentity(attestationId, domain) {
|
|
|
1282
1359
|
};
|
|
1283
1360
|
}
|
|
1284
1361
|
async function checkWellKnownFile(attestationId, domain) {
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
verified: true,
|
|
1307
|
-
method: "well-known",
|
|
1308
|
-
url
|
|
1309
|
-
};
|
|
1310
|
-
} else {
|
|
1311
|
-
log5.info({ domain, url }, "Well-known file exists but no attestation ID found");
|
|
1312
|
-
}
|
|
1362
|
+
const url = `https://${domain}/.well-known/orangecheck.txt`;
|
|
1363
|
+
try {
|
|
1364
|
+
log5.info({ url }, "Checking well-known file");
|
|
1365
|
+
const response = await fetch(url, {
|
|
1366
|
+
method: "GET",
|
|
1367
|
+
headers: { Accept: "text/plain" },
|
|
1368
|
+
// Don't follow redirects to other domains
|
|
1369
|
+
redirect: "manual",
|
|
1370
|
+
signal: AbortSignal.timeout(WELL_KNOWN_TIMEOUT_MS)
|
|
1371
|
+
});
|
|
1372
|
+
if (response.ok) {
|
|
1373
|
+
const content = await response.text();
|
|
1374
|
+
if (content.includes(attestationId)) {
|
|
1375
|
+
log5.info({ domain, url }, "Found attestation ID in well-known file");
|
|
1376
|
+
return {
|
|
1377
|
+
verified: true,
|
|
1378
|
+
method: "well-known",
|
|
1379
|
+
url
|
|
1380
|
+
};
|
|
1381
|
+
} else {
|
|
1382
|
+
log5.info({ domain, url }, "Well-known file exists but no attestation ID found");
|
|
1313
1383
|
}
|
|
1314
|
-
} catch (err) {
|
|
1315
|
-
log5.warn({ error: err, url }, "Failed to fetch well-known file");
|
|
1316
1384
|
}
|
|
1385
|
+
} catch (err) {
|
|
1386
|
+
log5.warn({ error: err, url }, "Failed to fetch well-known file");
|
|
1317
1387
|
}
|
|
1318
1388
|
return {
|
|
1319
1389
|
verified: false,
|
|
@@ -1386,14 +1456,32 @@ function isDnsIdentityVerified(attestationId, _domain, cachedContent) {
|
|
|
1386
1456
|
|
|
1387
1457
|
// src/identity/github.ts
|
|
1388
1458
|
var log6 = createLogger("identity-verification/github");
|
|
1459
|
+
var GITHUB_GIST_RAW_HOST = "gist.githubusercontent.com";
|
|
1460
|
+
var MAX_GISTS_SCANNED = 30;
|
|
1461
|
+
var MAX_FILES_PER_GIST = 10;
|
|
1462
|
+
var FETCH_TIMEOUT_MS = 5e3;
|
|
1463
|
+
var GITHUB_USERNAME_RE = /^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$/;
|
|
1464
|
+
function isSafeGistRawUrl(raw) {
|
|
1465
|
+
try {
|
|
1466
|
+
const u = new URL(raw);
|
|
1467
|
+
return u.protocol === "https:" && u.hostname === GITHUB_GIST_RAW_HOST;
|
|
1468
|
+
} catch {
|
|
1469
|
+
return false;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1389
1472
|
async function verifyGitHubIdentity(attestationId, username) {
|
|
1390
1473
|
log6.info({ attestationId, username }, "Verifying GitHub identity");
|
|
1474
|
+
if (!GITHUB_USERNAME_RE.test(username)) {
|
|
1475
|
+
return { verified: false, error: "invalid GitHub username format" };
|
|
1476
|
+
}
|
|
1391
1477
|
try {
|
|
1392
|
-
const response = await fetch(
|
|
1393
|
-
|
|
1394
|
-
|
|
1478
|
+
const response = await fetch(
|
|
1479
|
+
`https://api.github.com/users/${encodeURIComponent(username)}/gists?per_page=${MAX_GISTS_SCANNED}`,
|
|
1480
|
+
{
|
|
1481
|
+
headers: { Accept: "application/vnd.github.v3+json" },
|
|
1482
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
1395
1483
|
}
|
|
1396
|
-
|
|
1484
|
+
);
|
|
1397
1485
|
if (!response.ok) {
|
|
1398
1486
|
if (response.status === 404) {
|
|
1399
1487
|
return {
|
|
@@ -1408,7 +1496,7 @@ async function verifyGitHubIdentity(attestationId, username) {
|
|
|
1408
1496
|
}
|
|
1409
1497
|
const gists = await response.json();
|
|
1410
1498
|
log6.info({ username, gistCount: gists.length }, "Fetched GitHub gists");
|
|
1411
|
-
for (const gist of gists) {
|
|
1499
|
+
for (const gist of gists.slice(0, MAX_GISTS_SCANNED)) {
|
|
1412
1500
|
if (gist.description?.includes(attestationId)) {
|
|
1413
1501
|
log6.info({ username, gistId: gist.id }, "Found attestation ID in gist description");
|
|
1414
1502
|
return {
|
|
@@ -1417,7 +1505,8 @@ async function verifyGitHubIdentity(attestationId, username) {
|
|
|
1417
1505
|
gistId: gist.id
|
|
1418
1506
|
};
|
|
1419
1507
|
}
|
|
1420
|
-
|
|
1508
|
+
const files = Object.entries(gist.files).slice(0, MAX_FILES_PER_GIST);
|
|
1509
|
+
for (const [filename, file] of files) {
|
|
1421
1510
|
if (file.content && file.content.includes(attestationId)) {
|
|
1422
1511
|
log6.info(
|
|
1423
1512
|
{ username, gistId: gist.id, filename },
|
|
@@ -1429,8 +1518,17 @@ async function verifyGitHubIdentity(attestationId, username) {
|
|
|
1429
1518
|
gistId: gist.id
|
|
1430
1519
|
};
|
|
1431
1520
|
}
|
|
1521
|
+
if (!isSafeGistRawUrl(file.raw_url)) {
|
|
1522
|
+
log6.warn(
|
|
1523
|
+
{ filename, rawUrl: file.raw_url },
|
|
1524
|
+
"skipping gist file: raw_url failed allow-list"
|
|
1525
|
+
);
|
|
1526
|
+
continue;
|
|
1527
|
+
}
|
|
1432
1528
|
try {
|
|
1433
|
-
const fileResponse = await fetch(file.raw_url
|
|
1529
|
+
const fileResponse = await fetch(file.raw_url, {
|
|
1530
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
1531
|
+
});
|
|
1434
1532
|
if (fileResponse.ok) {
|
|
1435
1533
|
const content = await fileResponse.text();
|
|
1436
1534
|
if (content.includes(attestationId)) {
|
|
@@ -1508,15 +1606,38 @@ function isGitHubIdentityVerified(attestationId, _username, cachedGists) {
|
|
|
1508
1606
|
function getGistCreationUrl() {
|
|
1509
1607
|
return "https://gist.github.com/";
|
|
1510
1608
|
}
|
|
1511
|
-
|
|
1512
|
-
// src/identity/nostr.ts
|
|
1513
1609
|
var log7 = createLogger("identity-verification/nostr");
|
|
1610
|
+
var HEX_64_RE = /^[0-9a-f]{64}$/;
|
|
1611
|
+
function toHex(bytes) {
|
|
1612
|
+
let out = "";
|
|
1613
|
+
for (const b of bytes) out += b.toString(16).padStart(2, "0");
|
|
1614
|
+
return out;
|
|
1615
|
+
}
|
|
1616
|
+
function fromHex(hex) {
|
|
1617
|
+
if (hex.length % 2 !== 0) throw new Error("odd-length hex");
|
|
1618
|
+
const out = new Uint8Array(hex.length / 2);
|
|
1619
|
+
for (let i = 0; i < out.length; i++) {
|
|
1620
|
+
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
1621
|
+
}
|
|
1622
|
+
return out;
|
|
1623
|
+
}
|
|
1514
1624
|
function npubToHex(npub) {
|
|
1515
|
-
|
|
1516
|
-
|
|
1625
|
+
const trimmed = npub.trim();
|
|
1626
|
+
if (HEX_64_RE.test(trimmed.toLowerCase())) {
|
|
1627
|
+
return trimmed.toLowerCase();
|
|
1628
|
+
}
|
|
1629
|
+
if (!trimmed.toLowerCase().startsWith("npub1")) {
|
|
1630
|
+
throw new Error(`not an npub: ${trimmed.slice(0, 12)}\u2026`);
|
|
1517
1631
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1632
|
+
const decoded = base.bech32.decode(trimmed.toLowerCase(), 1023);
|
|
1633
|
+
if (decoded.prefix !== "npub") {
|
|
1634
|
+
throw new Error(`bech32 prefix mismatch: expected npub, got ${decoded.prefix}`);
|
|
1635
|
+
}
|
|
1636
|
+
const bytes = base.bech32.fromWords(decoded.words);
|
|
1637
|
+
if (bytes.length !== 32) {
|
|
1638
|
+
throw new Error(`invalid npub payload length: ${bytes.length}`);
|
|
1639
|
+
}
|
|
1640
|
+
return toHex(Uint8Array.from(bytes));
|
|
1520
1641
|
}
|
|
1521
1642
|
async function queryNostrEvents(options) {
|
|
1522
1643
|
const { authors, kinds, search, relays = DEFAULT_RELAYS, limit = 100 } = options;
|
|
@@ -1587,11 +1708,31 @@ async function queryNostrEvents(options) {
|
|
|
1587
1708
|
return events;
|
|
1588
1709
|
}
|
|
1589
1710
|
async function verifyNostrEventSignature(event) {
|
|
1590
|
-
|
|
1711
|
+
try {
|
|
1712
|
+
if (!event.id || !event.sig || !event.pubkey || typeof event.id !== "string" || typeof event.sig !== "string" || typeof event.pubkey !== "string" || event.sig.length !== 128 || !HEX_64_RE.test(event.id) || !HEX_64_RE.test(event.pubkey)) {
|
|
1713
|
+
return false;
|
|
1714
|
+
}
|
|
1715
|
+
const preimage = JSON.stringify([
|
|
1716
|
+
0,
|
|
1717
|
+
event.pubkey,
|
|
1718
|
+
event.created_at,
|
|
1719
|
+
event.kind,
|
|
1720
|
+
event.tags ?? [],
|
|
1721
|
+
event.content ?? ""
|
|
1722
|
+
]);
|
|
1723
|
+
const computedId = toHex(sha2.sha256(new TextEncoder().encode(preimage)));
|
|
1724
|
+
if (computedId !== event.id.toLowerCase()) {
|
|
1725
|
+
log7.warn(
|
|
1726
|
+
{ eventId: event.id, computedId },
|
|
1727
|
+
"nostr event id does not match its content"
|
|
1728
|
+
);
|
|
1729
|
+
return false;
|
|
1730
|
+
}
|
|
1731
|
+
return secp256k1.schnorr.verify(fromHex(event.sig), fromHex(event.id), fromHex(event.pubkey));
|
|
1732
|
+
} catch (err) {
|
|
1733
|
+
log7.warn({ err }, "schnorr verify threw");
|
|
1591
1734
|
return false;
|
|
1592
1735
|
}
|
|
1593
|
-
log7.warn("Nostr signature verification not fully implemented");
|
|
1594
|
-
return true;
|
|
1595
1736
|
}
|
|
1596
1737
|
async function verifyNostrIdentity(attestationId, npub, relays) {
|
|
1597
1738
|
log7.info({ attestationId, npub }, "Verifying Nostr identity");
|
|
@@ -1967,6 +2108,8 @@ exports.getDnsVerificationInstructions = getDnsVerificationInstructions;
|
|
|
1967
2108
|
exports.getGistCreationUrl = getGistCreationUrl;
|
|
1968
2109
|
exports.getGitHubVerificationInstructions = getGitHubVerificationInstructions;
|
|
1969
2110
|
exports.getManualVerificationInstructions = getManualVerificationInstructions;
|
|
2111
|
+
exports.getNip07Info = getNip07Info;
|
|
2112
|
+
exports.getNostrPublicKey = getNostrPublicKey;
|
|
1970
2113
|
exports.getNostrVerificationInstructions = getNostrVerificationInstructions;
|
|
1971
2114
|
exports.getTweetUrl = getTweetUrl;
|
|
1972
2115
|
exports.getTwitterComposeUrl = getTwitterComposeUrl;
|