@multiplatform.one/gpg 7.18.0 → 7.20.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/cjs/GpgProvider.cjs +7 -4
- package/dist/cjs/GpgProvider.native.js +7 -4
- package/dist/cjs/GpgProvider.native.js.map +1 -1
- package/dist/cjs/index.cjs +7 -1
- package/dist/cjs/index.native.js +7 -1
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/signature.cjs +113 -0
- package/dist/cjs/signature.native.js +124 -0
- package/dist/cjs/signature.native.js.map +1 -0
- package/dist/cjs/signer.cjs +119 -0
- package/dist/cjs/signer.native.js +150 -0
- package/dist/cjs/signer.native.js.map +1 -0
- package/dist/esm/GpgButton.native.js.map +1 -1
- package/dist/esm/GpgContext.native.js.map +1 -1
- package/dist/esm/GpgIcon.native.js.map +1 -1
- package/dist/esm/GpgProvider.mjs +7 -4
- package/dist/esm/GpgProvider.mjs.map +1 -1
- package/dist/esm/GpgProvider.native.js +7 -4
- package/dist/esm/GpgProvider.native.js.map +1 -1
- package/dist/esm/SignInWithGpg.native.js.map +1 -1
- package/dist/esm/index.mjs +2 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/screens.native.js.map +1 -1
- package/dist/esm/signature.mjs +88 -0
- package/dist/esm/signature.mjs.map +1 -0
- package/dist/esm/signature.native.js +96 -0
- package/dist/esm/signature.native.js.map +1 -0
- package/dist/esm/signer.mjs +88 -0
- package/dist/esm/signer.mjs.map +1 -0
- package/dist/esm/signer.native.js +116 -0
- package/dist/esm/signer.native.js.map +1 -0
- package/dist/esm/useGpg.native.js.map +1 -1
- package/package.json +16 -3
- package/src/GpgProvider.tsx +7 -19
- package/src/index.ts +10 -1
- package/src/signature.ts +120 -0
- package/src/signer.ts +142 -0
- package/types/GpgProvider.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/signature.d.ts +6 -0
- package/types/signature.d.ts.map +1 -0
- package/types/signer.d.ts +47 -0
- package/types/signer.d.ts.map +1 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ripemd160, sha1 } from "@noble/hashes/legacy.js";
|
|
2
|
+
import { sha224, sha256, sha384, sha512 } from "@noble/hashes/sha2.js";
|
|
3
|
+
var BEGIN = "-----BEGIN PGP SIGNATURE-----";
|
|
4
|
+
var END = "-----END PGP SIGNATURE-----";
|
|
5
|
+
var hashes = {
|
|
6
|
+
2: sha1,
|
|
7
|
+
3: ripemd160,
|
|
8
|
+
8: sha256,
|
|
9
|
+
9: sha384,
|
|
10
|
+
10: sha512,
|
|
11
|
+
11: sha224
|
|
12
|
+
};
|
|
13
|
+
function dearmor(text) {
|
|
14
|
+
var start = text.indexOf(BEGIN);
|
|
15
|
+
var end = text.indexOf(END, start);
|
|
16
|
+
if (start < 0 || end < 0) throw new Error("No PGP SIGNATURE block in the paste");
|
|
17
|
+
var lines = text.slice(start + BEGIN.length, end).split(/\r?\n/).map(function (line) {
|
|
18
|
+
return line.trim();
|
|
19
|
+
});
|
|
20
|
+
var blank = lines.indexOf("", 1);
|
|
21
|
+
var hasHeaders = lines.slice(1, blank < 0 ? 0 : blank).some(function (line) {
|
|
22
|
+
return line.includes(": ");
|
|
23
|
+
});
|
|
24
|
+
var body = (hasHeaders ? lines.slice(blank) : lines).filter(function (line) {
|
|
25
|
+
return line && !/^=[A-Za-z0-9+/]{4}$/.test(line);
|
|
26
|
+
});
|
|
27
|
+
var binary = atob(body.join(""));
|
|
28
|
+
return Uint8Array.from(binary, function (c) {
|
|
29
|
+
return c.charCodeAt(0);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function signaturePackets(bytes) {
|
|
33
|
+
var packets = [];
|
|
34
|
+
var at = 0;
|
|
35
|
+
while (at < bytes.length) {
|
|
36
|
+
var ctb = bytes[at++];
|
|
37
|
+
if (!(ctb & 128)) throw new Error("The PGP SIGNATURE block is not OpenPGP data");
|
|
38
|
+
var tag = void 0;
|
|
39
|
+
var length = void 0;
|
|
40
|
+
if (ctb & 64) {
|
|
41
|
+
tag = ctb & 63;
|
|
42
|
+
var first = bytes[at++];
|
|
43
|
+
if (first < 192) length = first;else if (first < 224) length = (first - 192 << 8) + bytes[at++] + 192;else if (first === 255) {
|
|
44
|
+
length = new DataView(bytes.buffer, bytes.byteOffset + at, 4).getUint32(0);
|
|
45
|
+
at += 4;
|
|
46
|
+
} else throw new Error("Partial-length PGP packets are not a detached signature");
|
|
47
|
+
} else {
|
|
48
|
+
tag = ctb >> 2 & 15;
|
|
49
|
+
var size = [1, 2, 4][ctb & 3];
|
|
50
|
+
if (size === void 0) throw new Error("Indeterminate-length PGP packets are not supported");
|
|
51
|
+
length = 0;
|
|
52
|
+
for (var i = 0; i < size; i++) length = length * 256 + bytes[at++];
|
|
53
|
+
}
|
|
54
|
+
if (at + length > bytes.length) throw new Error("The PGP SIGNATURE block is truncated");
|
|
55
|
+
if (tag === 2) packets.push(bytes.subarray(at, at + length));
|
|
56
|
+
at += length;
|
|
57
|
+
}
|
|
58
|
+
return packets;
|
|
59
|
+
}
|
|
60
|
+
function quickCheck(packet, message) {
|
|
61
|
+
if (packet[0] !== 4) {
|
|
62
|
+
throw new Error(`A version ${packet[0]} PGP signature cannot be checked here; sign with a v4 key`);
|
|
63
|
+
}
|
|
64
|
+
var sigType = packet[1];
|
|
65
|
+
if (sigType !== 0 && sigType !== 1) {
|
|
66
|
+
throw new Error(`PGP signature type 0x${sigType.toString(16)} is not a document signature`);
|
|
67
|
+
}
|
|
68
|
+
var hash = hashes[packet[3]];
|
|
69
|
+
if (!hash) throw new Error(`PGP hash algorithm ${packet[3]} is not supported`);
|
|
70
|
+
var hashedEnd = 6 + (packet[4] << 8 | packet[5]);
|
|
71
|
+
var unhashedLength = packet[hashedEnd] << 8 | packet[hashedEnd + 1];
|
|
72
|
+
var left16 = hashedEnd + 2 + unhashedLength;
|
|
73
|
+
if (left16 + 2 > packet.length) throw new Error("The PGP signature packet is truncated");
|
|
74
|
+
var data = sigType === 1 ? new TextEncoder().encode(new TextDecoder().decode(message).replace(/\r?\n/g, "\n").replace(/\n/g, "\r\n")) : message;
|
|
75
|
+
var trailer = new Uint8Array(6);
|
|
76
|
+
trailer[0] = 4;
|
|
77
|
+
trailer[1] = 255;
|
|
78
|
+
new DataView(trailer.buffer).setUint32(2, hashedEnd);
|
|
79
|
+
var input = new Uint8Array(data.length + hashedEnd + trailer.length);
|
|
80
|
+
input.set(data, 0);
|
|
81
|
+
input.set(packet.subarray(0, hashedEnd), data.length);
|
|
82
|
+
input.set(trailer, data.length + hashedEnd);
|
|
83
|
+
var digest = hash(input);
|
|
84
|
+
return [packet[left16] << 8 | packet[left16 + 1], digest[0] << 8 | digest[1]];
|
|
85
|
+
}
|
|
86
|
+
function gpgSignatureCovers(blob, message) {
|
|
87
|
+
var packets = signaturePackets(dearmor(blob));
|
|
88
|
+
if (!packets.length) throw new Error("The PGP SIGNATURE block holds no signature");
|
|
89
|
+
var bytes = new TextEncoder().encode(message);
|
|
90
|
+
return packets.every(function (packet) {
|
|
91
|
+
var [expected, actual] = quickCheck(packet, bytes);
|
|
92
|
+
return expected === actual;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
export { gpgSignatureCovers };
|
|
96
|
+
//# sourceMappingURL=signature.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["ripemd160","sha1","sha224","sha256","sha384","sha512","BEGIN","END","hashes","dearmor","text","start","indexOf","end","Error","lines","slice","length","split","map","line","trim","blank","hasHeaders","some","includes","body","filter","test","binary","atob","join","Uint8Array","from","c","charCodeAt","signaturePackets","bytes","packets","at","ctb","tag","first","DataView","buffer","byteOffset","getUint32","size","i","push","subarray","quickCheck","packet","message","sigType","toString","hash","hashedEnd","unhashedLength","left16","data","TextEncoder","encode","TextDecoder","decode","replace","trailer","setUint32","input","set","digest","gpgSignatureCovers","blob","every","expected","actual"],"sources":["signature.native.js"],"sourcesContent":["import { ripemd160, sha1 } from \"@noble/hashes/legacy.js\";\nimport { sha224, sha256, sha384, sha512 } from \"@noble/hashes/sha2.js\";\nvar BEGIN = \"-----BEGIN PGP SIGNATURE-----\";\nvar END = \"-----END PGP SIGNATURE-----\";\nvar hashes = {\n 2: sha1,\n 3: ripemd160,\n 8: sha256,\n 9: sha384,\n 10: sha512,\n 11: sha224\n};\nfunction dearmor(text) {\n var start = text.indexOf(BEGIN);\n var end = text.indexOf(END, start);\n if (start < 0 || end < 0) throw new Error(\"No PGP SIGNATURE block in the paste\");\n var lines = text.slice(start + BEGIN.length, end).split(/\\r?\\n/).map(function(line) {\n return line.trim();\n });\n var blank = lines.indexOf(\"\", 1);\n var hasHeaders = lines.slice(1, blank < 0 ? 0 : blank).some(function(line) {\n return line.includes(\": \");\n });\n var body = (hasHeaders ? lines.slice(blank) : lines).filter(function(line) {\n return line && !/^=[A-Za-z0-9+/]{4}$/.test(line);\n });\n var binary = atob(body.join(\"\"));\n return Uint8Array.from(binary, function(c) {\n return c.charCodeAt(0);\n });\n}\nfunction signaturePackets(bytes) {\n var packets = [];\n var at = 0;\n while (at < bytes.length) {\n var ctb = bytes[at++];\n if (!(ctb & 128)) throw new Error(\"The PGP SIGNATURE block is not OpenPGP data\");\n var tag = void 0;\n var length = void 0;\n if (ctb & 64) {\n tag = ctb & 63;\n var first = bytes[at++];\n if (first < 192) length = first;\n else if (first < 224) length = (first - 192 << 8) + bytes[at++] + 192;\n else if (first === 255) {\n length = new DataView(bytes.buffer, bytes.byteOffset + at, 4).getUint32(0);\n at += 4;\n } else throw new Error(\"Partial-length PGP packets are not a detached signature\");\n } else {\n tag = ctb >> 2 & 15;\n var size = [\n 1,\n 2,\n 4\n ][ctb & 3];\n if (size === void 0) throw new Error(\"Indeterminate-length PGP packets are not supported\");\n length = 0;\n for (var i = 0; i < size; i++) length = length * 256 + bytes[at++];\n }\n if (at + length > bytes.length) throw new Error(\"The PGP SIGNATURE block is truncated\");\n if (tag === 2) packets.push(bytes.subarray(at, at + length));\n at += length;\n }\n return packets;\n}\nfunction quickCheck(packet, message) {\n if (packet[0] !== 4) {\n throw new Error(`A version ${packet[0]} PGP signature cannot be checked here; sign with a v4 key`);\n }\n var sigType = packet[1];\n if (sigType !== 0 && sigType !== 1) {\n throw new Error(`PGP signature type 0x${sigType.toString(16)} is not a document signature`);\n }\n var hash = hashes[packet[3]];\n if (!hash) throw new Error(`PGP hash algorithm ${packet[3]} is not supported`);\n var hashedEnd = 6 + (packet[4] << 8 | packet[5]);\n var unhashedLength = packet[hashedEnd] << 8 | packet[hashedEnd + 1];\n var left16 = hashedEnd + 2 + unhashedLength;\n if (left16 + 2 > packet.length) throw new Error(\"The PGP signature packet is truncated\");\n var data = sigType === 1 ? new TextEncoder().encode(new TextDecoder().decode(message).replace(/\\r?\\n/g, \"\\n\").replace(/\\n/g, \"\\r\\n\")) : message;\n var trailer = new Uint8Array(6);\n trailer[0] = 4;\n trailer[1] = 255;\n new DataView(trailer.buffer).setUint32(2, hashedEnd);\n var input = new Uint8Array(data.length + hashedEnd + trailer.length);\n input.set(data, 0);\n input.set(packet.subarray(0, hashedEnd), data.length);\n input.set(trailer, data.length + hashedEnd);\n var digest = hash(input);\n return [\n packet[left16] << 8 | packet[left16 + 1],\n digest[0] << 8 | digest[1]\n ];\n}\nfunction gpgSignatureCovers(blob, message) {\n var packets = signaturePackets(dearmor(blob));\n if (!packets.length) throw new Error(\"The PGP SIGNATURE block holds no signature\");\n var bytes = new TextEncoder().encode(message);\n return packets.every(function(packet) {\n var [expected, actual] = quickCheck(packet, bytes);\n return expected === actual;\n });\n}\nexport {\n gpgSignatureCovers\n};\n//# sourceMappingURL=signature.js.map\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
import { gpgSignatureCovers } from "./signature.mjs";
|
|
3
|
+
import { gpgSignatureCovers as gpgSignatureCovers2 } from "./signature.mjs";
|
|
4
|
+
const GPG_SIGNATURE_BEGIN = "-----BEGIN PGP SIGNATURE-----";
|
|
5
|
+
const GPG_PUBLIC_KEY_BEGIN = "-----BEGIN PGP PUBLIC KEY BLOCK-----";
|
|
6
|
+
function gpgSignCommand(message) {
|
|
7
|
+
return `{ printf %s '${message.replace(/'/g, "'\\''")}' | gpg --detach-sign --armor; gpg --armor --export; }`;
|
|
8
|
+
}
|
|
9
|
+
function assertGpgSignature(blob) {
|
|
10
|
+
const trimmed = blob.trim();
|
|
11
|
+
if (!trimmed.includes(GPG_SIGNATURE_BEGIN) || !trimmed.includes(GPG_PUBLIC_KEY_BEGIN)) {
|
|
12
|
+
throw new Error("Paste the FULL output \u2014 it must contain both a PGP SIGNATURE and a PUBLIC KEY block.");
|
|
13
|
+
}
|
|
14
|
+
return trimmed;
|
|
15
|
+
}
|
|
16
|
+
function createGpgSigner() {
|
|
17
|
+
let pending = null;
|
|
18
|
+
let settle = null;
|
|
19
|
+
const listeners = /* @__PURE__ */new Set();
|
|
20
|
+
const emit = () => {
|
|
21
|
+
for (const listener of listeners) listener();
|
|
22
|
+
};
|
|
23
|
+
const signer = {
|
|
24
|
+
family: "gpg",
|
|
25
|
+
address: null,
|
|
26
|
+
capabilities: ["signMessage"],
|
|
27
|
+
get pending() {
|
|
28
|
+
return pending;
|
|
29
|
+
},
|
|
30
|
+
signMessage(message) {
|
|
31
|
+
settle?.reject(new Error("GPG signing was superseded by a newer request"));
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
settle = {
|
|
34
|
+
resolve,
|
|
35
|
+
reject
|
|
36
|
+
};
|
|
37
|
+
pending = {
|
|
38
|
+
message,
|
|
39
|
+
command: gpgSignCommand(message)
|
|
40
|
+
};
|
|
41
|
+
emit();
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
submit(blob, request) {
|
|
45
|
+
if (!settle || !pending) throw new Error("No GPG signature was requested");
|
|
46
|
+
if (request !== pending) {
|
|
47
|
+
throw new Error("That GPG request was superseded; run the current command and paste its output");
|
|
48
|
+
}
|
|
49
|
+
const signature = assertGpgSignature(blob);
|
|
50
|
+
if (!gpgSignatureCovers(signature, pending.message)) {
|
|
51
|
+
throw new Error("That signature is over a different message; run the current command and paste its output");
|
|
52
|
+
}
|
|
53
|
+
const {
|
|
54
|
+
resolve
|
|
55
|
+
} = settle;
|
|
56
|
+
settle = null;
|
|
57
|
+
pending = null;
|
|
58
|
+
emit();
|
|
59
|
+
resolve(signature);
|
|
60
|
+
},
|
|
61
|
+
cancel(reason) {
|
|
62
|
+
if (!settle) return;
|
|
63
|
+
const {
|
|
64
|
+
reject
|
|
65
|
+
} = settle;
|
|
66
|
+
settle = null;
|
|
67
|
+
pending = null;
|
|
68
|
+
emit();
|
|
69
|
+
reject(reason ?? new Error("GPG signing was cancelled"));
|
|
70
|
+
},
|
|
71
|
+
subscribe(listener) {
|
|
72
|
+
listeners.add(listener);
|
|
73
|
+
return () => listeners.delete(listener);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return signer;
|
|
77
|
+
}
|
|
78
|
+
function useGpgSigner(signer = defaultSigner()) {
|
|
79
|
+
useSyncExternalStore(signer.subscribe, () => signer.pending, () => signer.pending);
|
|
80
|
+
return signer;
|
|
81
|
+
}
|
|
82
|
+
let sharedSigner = null;
|
|
83
|
+
function defaultSigner() {
|
|
84
|
+
sharedSigner ??= createGpgSigner();
|
|
85
|
+
return sharedSigner;
|
|
86
|
+
}
|
|
87
|
+
export { GPG_PUBLIC_KEY_BEGIN, GPG_SIGNATURE_BEGIN, assertGpgSignature, createGpgSigner, gpgSignCommand, gpgSignatureCovers2 as gpgSignatureCovers, useGpgSigner };
|
|
88
|
+
//# sourceMappingURL=signer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useSyncExternalStore","gpgSignatureCovers","gpgSignatureCovers2","GPG_SIGNATURE_BEGIN","GPG_PUBLIC_KEY_BEGIN","gpgSignCommand","message","replace","assertGpgSignature","blob","trimmed","trim","includes","Error","createGpgSigner","pending","settle","listeners","Set","emit","listener","signer","family","address","capabilities","signMessage","reject","Promise","resolve","command","submit","request","signature","cancel","reason","subscribe","add","delete","useGpgSigner","defaultSigner","sharedSigner"],"sources":["../../src/signer.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
import { gpgSignatureCovers } from "./signature.native.js";
|
|
3
|
+
import { gpgSignatureCovers as gpgSignatureCovers2 } from "./signature.native.js";
|
|
4
|
+
var GPG_SIGNATURE_BEGIN = "-----BEGIN PGP SIGNATURE-----";
|
|
5
|
+
var GPG_PUBLIC_KEY_BEGIN = "-----BEGIN PGP PUBLIC KEY BLOCK-----";
|
|
6
|
+
function gpgSignCommand(message) {
|
|
7
|
+
return `{ printf %s '${message.replace(/'/g, "'\\''")}' | gpg --detach-sign --armor; gpg --armor --export; }`;
|
|
8
|
+
}
|
|
9
|
+
function assertGpgSignature(blob) {
|
|
10
|
+
var trimmed = blob.trim();
|
|
11
|
+
if (!trimmed.includes(GPG_SIGNATURE_BEGIN) || !trimmed.includes(GPG_PUBLIC_KEY_BEGIN)) {
|
|
12
|
+
throw new Error("Paste the FULL output \u2014 it must contain both a PGP SIGNATURE and a PUBLIC KEY block.");
|
|
13
|
+
}
|
|
14
|
+
return trimmed;
|
|
15
|
+
}
|
|
16
|
+
function createGpgSigner() {
|
|
17
|
+
var pending = null;
|
|
18
|
+
var settle = null;
|
|
19
|
+
var listeners = /* @__PURE__ */new Set();
|
|
20
|
+
var emit = function emit2() {
|
|
21
|
+
var _iteratorNormalCompletion = true,
|
|
22
|
+
_didIteratorError = false,
|
|
23
|
+
_iteratorError = void 0;
|
|
24
|
+
try {
|
|
25
|
+
for (var _iterator = listeners[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
26
|
+
var listener = _step.value;
|
|
27
|
+
listener();
|
|
28
|
+
}
|
|
29
|
+
} catch (err) {
|
|
30
|
+
_didIteratorError = true;
|
|
31
|
+
_iteratorError = err;
|
|
32
|
+
} finally {
|
|
33
|
+
try {
|
|
34
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
35
|
+
_iterator.return();
|
|
36
|
+
}
|
|
37
|
+
} finally {
|
|
38
|
+
if (_didIteratorError) {
|
|
39
|
+
throw _iteratorError;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var signer = {
|
|
45
|
+
family: "gpg",
|
|
46
|
+
address: null,
|
|
47
|
+
capabilities: ["signMessage"],
|
|
48
|
+
get pending() {
|
|
49
|
+
return pending;
|
|
50
|
+
},
|
|
51
|
+
signMessage(message) {
|
|
52
|
+
settle === null || settle === void 0 ? void 0 : settle.reject(new Error("GPG signing was superseded by a newer request"));
|
|
53
|
+
return new Promise(function (resolve, reject) {
|
|
54
|
+
settle = {
|
|
55
|
+
resolve,
|
|
56
|
+
reject
|
|
57
|
+
};
|
|
58
|
+
pending = {
|
|
59
|
+
message,
|
|
60
|
+
command: gpgSignCommand(message)
|
|
61
|
+
};
|
|
62
|
+
emit();
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
submit(blob, request) {
|
|
66
|
+
if (!settle || !pending) throw new Error("No GPG signature was requested");
|
|
67
|
+
if (request !== pending) {
|
|
68
|
+
throw new Error("That GPG request was superseded; run the current command and paste its output");
|
|
69
|
+
}
|
|
70
|
+
var signature = assertGpgSignature(blob);
|
|
71
|
+
if (!gpgSignatureCovers(signature, pending.message)) {
|
|
72
|
+
throw new Error("That signature is over a different message; run the current command and paste its output");
|
|
73
|
+
}
|
|
74
|
+
var {
|
|
75
|
+
resolve
|
|
76
|
+
} = settle;
|
|
77
|
+
settle = null;
|
|
78
|
+
pending = null;
|
|
79
|
+
emit();
|
|
80
|
+
resolve(signature);
|
|
81
|
+
},
|
|
82
|
+
cancel(reason) {
|
|
83
|
+
if (!settle) return;
|
|
84
|
+
var {
|
|
85
|
+
reject
|
|
86
|
+
} = settle;
|
|
87
|
+
settle = null;
|
|
88
|
+
pending = null;
|
|
89
|
+
emit();
|
|
90
|
+
reject(reason !== null && reason !== void 0 ? reason : new Error("GPG signing was cancelled"));
|
|
91
|
+
},
|
|
92
|
+
subscribe(listener) {
|
|
93
|
+
listeners.add(listener);
|
|
94
|
+
return function () {
|
|
95
|
+
return listeners.delete(listener);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
return signer;
|
|
100
|
+
}
|
|
101
|
+
function useGpgSigner() {
|
|
102
|
+
var signer = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : defaultSigner();
|
|
103
|
+
useSyncExternalStore(signer.subscribe, function () {
|
|
104
|
+
return signer.pending;
|
|
105
|
+
}, function () {
|
|
106
|
+
return signer.pending;
|
|
107
|
+
});
|
|
108
|
+
return signer;
|
|
109
|
+
}
|
|
110
|
+
var sharedSigner = null;
|
|
111
|
+
function defaultSigner() {
|
|
112
|
+
sharedSigner !== null && sharedSigner !== void 0 ? sharedSigner : sharedSigner = createGpgSigner();
|
|
113
|
+
return sharedSigner;
|
|
114
|
+
}
|
|
115
|
+
export { GPG_PUBLIC_KEY_BEGIN, GPG_SIGNATURE_BEGIN, assertGpgSignature, createGpgSigner, gpgSignCommand, gpgSignatureCovers2 as gpgSignatureCovers, useGpgSigner };
|
|
116
|
+
//# sourceMappingURL=signer.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useSyncExternalStore","gpgSignatureCovers","gpgSignatureCovers2","GPG_SIGNATURE_BEGIN","GPG_PUBLIC_KEY_BEGIN","gpgSignCommand","message","replace","assertGpgSignature","blob","trimmed","trim","includes","Error","createGpgSigner","pending","settle","listeners","Set","emit","emit2","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_iterator","Symbol","iterator","_step","next","done","listener","value","err","return","signer","family","address","capabilities","signMessage","reject","Promise","resolve","command","submit","request","signature","cancel","reason","subscribe","add","delete","useGpgSigner","arguments","length","defaultSigner","sharedSigner"],"sources":["signer.native.js"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\nimport { gpgSignatureCovers } from \"./signature\";\nimport { gpgSignatureCovers as gpgSignatureCovers2 } from \"./signature\";\nvar GPG_SIGNATURE_BEGIN = \"-----BEGIN PGP SIGNATURE-----\";\nvar GPG_PUBLIC_KEY_BEGIN = \"-----BEGIN PGP PUBLIC KEY BLOCK-----\";\nfunction gpgSignCommand(message) {\n return `{ printf %s '${message.replace(/'/g, \"'\\\\''\")}' | gpg --detach-sign --armor; gpg --armor --export; }`;\n}\nfunction assertGpgSignature(blob) {\n var trimmed = blob.trim();\n if (!trimmed.includes(GPG_SIGNATURE_BEGIN) || !trimmed.includes(GPG_PUBLIC_KEY_BEGIN)) {\n throw new Error(\"Paste the FULL output \\u2014 it must contain both a PGP SIGNATURE and a PUBLIC KEY block.\");\n }\n return trimmed;\n}\nfunction createGpgSigner() {\n var pending = null;\n var settle = null;\n var listeners = /* @__PURE__ */ new Set();\n var emit = function emit2() {\n var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;\n try {\n for (var _iterator = listeners[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var listener = _step.value;\n listener();\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n };\n var signer = {\n family: \"gpg\",\n address: null,\n capabilities: [\n \"signMessage\"\n ],\n get pending() {\n return pending;\n },\n signMessage(message) {\n settle === null || settle === void 0 ? void 0 : settle.reject(new Error(\"GPG signing was superseded by a newer request\"));\n return new Promise(function(resolve, reject) {\n settle = {\n resolve,\n reject\n };\n pending = {\n message,\n command: gpgSignCommand(message)\n };\n emit();\n });\n },\n submit(blob, request) {\n if (!settle || !pending) throw new Error(\"No GPG signature was requested\");\n if (request !== pending) {\n throw new Error(\"That GPG request was superseded; run the current command and paste its output\");\n }\n var signature = assertGpgSignature(blob);\n if (!gpgSignatureCovers(signature, pending.message)) {\n throw new Error(\"That signature is over a different message; run the current command and paste its output\");\n }\n var { resolve } = settle;\n settle = null;\n pending = null;\n emit();\n resolve(signature);\n },\n cancel(reason) {\n if (!settle) return;\n var { reject } = settle;\n settle = null;\n pending = null;\n emit();\n reject(reason !== null && reason !== void 0 ? reason : new Error(\"GPG signing was cancelled\"));\n },\n subscribe(listener) {\n listeners.add(listener);\n return function() {\n return listeners.delete(listener);\n };\n }\n };\n return signer;\n}\nfunction useGpgSigner() {\n var signer = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : defaultSigner();\n useSyncExternalStore(signer.subscribe, function() {\n return signer.pending;\n }, function() {\n return signer.pending;\n });\n return signer;\n}\nvar sharedSigner = null;\nfunction defaultSigner() {\n sharedSigner !== null && sharedSigner !== void 0 ? sharedSigner : sharedSigner = createGpgSigner();\n return sharedSigner;\n}\nexport {\n GPG_PUBLIC_KEY_BEGIN,\n GPG_SIGNATURE_BEGIN,\n assertGpgSignature,\n createGpgSigner,\n gpgSignCommand,\n gpgSignatureCovers2 as gpgSignatureCovers,\n useGpgSigner\n};\n//# sourceMappingURL=signer.js.map\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useContext","GpgContext","GpgStatusState","useGpg","useGpg2","gpg","message","command","status","ERROR","error","Error","isLoading","isSuccess","isError","isReady","signIn","signIn2","Promise","reject","reset","nonce","resetStatus","LOADING","SUCCESS"],"sources":["
|
|
1
|
+
{"version":3,"names":["useContext","GpgContext","GpgStatusState","useGpg","useGpg2","gpg","message","command","status","ERROR","error","Error","isLoading","isSuccess","isError","isReady","signIn","signIn2","Promise","reject","reset","nonce","resetStatus","LOADING","SUCCESS"],"sources":["useGpg.native.js"],"sourcesContent":["import { useContext } from \"react\";\nimport { GpgContext, GpgStatusState } from \"./GpgContext\";\nvar useGpg = function useGpg2() {\n var gpg = useContext(GpgContext);\n if (!gpg) {\n return {\n message: null,\n command: null,\n status: GpgStatusState.ERROR,\n error: new Error(\"useGpg hook must be inside a GpgProvider.\"),\n isLoading: false,\n isSuccess: false,\n isError: true,\n isReady: false,\n signIn: function signIn2() {\n return Promise.reject();\n },\n reset: function reset() {\n }\n };\n }\n var { nonce, message, command, status, signIn, resetStatus } = gpg;\n var isLoading = status === GpgStatusState.LOADING || nonce.isLoading;\n var isSuccess = status === GpgStatusState.SUCCESS;\n var isError = status === GpgStatusState.ERROR;\n var isReady = !!message && !isLoading;\n return {\n message,\n command,\n status,\n error: nonce.error,\n isLoading,\n isSuccess,\n isError,\n isReady,\n signIn,\n reset: function reset() {\n return resetStatus();\n }\n };\n};\nexport {\n useGpg\n};\n//# sourceMappingURL=useGpg.js.map\n"],"mappings":"AAAA,SAASA,UAAU,QAAQ,OAAO;AAClC,SAASC,UAAU,EAAEC,cAAc,QAAQ,wBAAc;AACzD,IAAIC,MAAM,GAAG,SAASC,OAAOA,CAAA,EAAG;EAC9B,IAAIC,GAAG,GAAGL,UAAU,CAACC,UAAU,CAAC;EAChC,IAAI,CAACI,GAAG,EAAE;IACR,OAAO;MACLC,OAAO,EAAE,IAAI;MACbC,OAAO,EAAE,IAAI;MACbC,MAAM,EAAEN,cAAc,CAACO,KAAK;MAC5BC,KAAK,EAAE,IAAIC,KAAK,CAAC,2CAA2C,CAAC;MAC7DC,SAAS,EAAE,KAAK;MAChBC,SAAS,EAAE,KAAK;MAChBC,OAAO,EAAE,IAAI;MACbC,OAAO,EAAE,KAAK;MACdC,MAAM,EAAE,SAASC,OAAOA,CAAA,EAAG;QACzB,OAAOC,OAAO,CAACC,MAAM,CAAC,CAAC;MACzB,CAAC;MACDC,KAAK,EAAE,SAASA,KAAKA,CAAA,EAAG,CACxB;IACF,CAAC;EACH;EACA,IAAI;IAAEC,KAAK;IAAEf,OAAO;IAAEC,OAAO;IAAEC,MAAM;IAAEQ,MAAM;IAAEM;EAAY,CAAC,GAAGjB,GAAG;EAClE,IAAIO,SAAS,GAAGJ,MAAM,KAAKN,cAAc,CAACqB,OAAO,IAAIF,KAAK,CAACT,SAAS;EACpE,IAAIC,SAAS,GAAGL,MAAM,KAAKN,cAAc,CAACsB,OAAO;EACjD,IAAIV,OAAO,GAAGN,MAAM,KAAKN,cAAc,CAACO,KAAK;EAC7C,IAAIM,OAAO,GAAG,CAAC,CAACT,OAAO,IAAI,CAACM,SAAS;EACrC,OAAO;IACLN,OAAO;IACPC,OAAO;IACPC,MAAM;IACNE,KAAK,EAAEW,KAAK,CAACX,KAAK;IAClBE,SAAS;IACTC,SAAS;IACTC,OAAO;IACPC,OAAO;IACPC,MAAM;IACNI,KAAK,EAAE,SAASA,KAAKA,CAAA,EAAG;MACtB,OAAOE,WAAW,CAAC,CAAC;IACtB;EACF,CAAC;AACH,CAAC;AACD,SACEnB,MAAM","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplatform.one/gpg",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.20.0",
|
|
4
4
|
"description": "Sign-In With GPG (SIWG) screen + GpgProvider for the connectkit modal — no viem",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"connectkit",
|
|
@@ -49,16 +49,29 @@
|
|
|
49
49
|
"types": "./types/index.d.ts",
|
|
50
50
|
"import": "./dist/esm/index.mjs",
|
|
51
51
|
"require": "./dist/cjs/index.cjs"
|
|
52
|
+
},
|
|
53
|
+
"./signer": {
|
|
54
|
+
"source": "./src/signer.ts",
|
|
55
|
+
"react-native": {
|
|
56
|
+
"types": "./types/signer.d.ts",
|
|
57
|
+
"import": "./src/signer.ts",
|
|
58
|
+
"require": "./src/signer.ts",
|
|
59
|
+
"default": "./src/signer.ts"
|
|
60
|
+
},
|
|
61
|
+
"types": "./types/signer.d.ts",
|
|
62
|
+
"import": "./dist/esm/signer.mjs",
|
|
63
|
+
"require": "./dist/cjs/signer.cjs"
|
|
52
64
|
}
|
|
53
65
|
},
|
|
54
66
|
"publishConfig": {
|
|
55
67
|
"access": "public"
|
|
56
68
|
},
|
|
57
69
|
"dependencies": {
|
|
58
|
-
"@multiplatform.one/connectkit": "7.
|
|
70
|
+
"@multiplatform.one/connectkit": "7.20.0",
|
|
71
|
+
"@noble/hashes": "^1.8.0"
|
|
59
72
|
},
|
|
60
73
|
"devDependencies": {
|
|
61
|
-
"@multiplatform.one/config": "7.
|
|
74
|
+
"@multiplatform.one/config": "7.20.0",
|
|
62
75
|
"@tamagui/build": "2.7.6",
|
|
63
76
|
"react": "19.2.5",
|
|
64
77
|
"typescript": "~5.9.3"
|
package/src/GpgProvider.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { useQuery } from "@tanstack/react-query";
|
|
|
3
3
|
|
|
4
4
|
import { Context as ConnectKitContext } from "@multiplatform.one/connectkit";
|
|
5
5
|
import { GpgContext, GpgConfig, GpgStatusState } from "./GpgContext";
|
|
6
|
+
import { assertGpgSignature, gpgSignCommand } from "./signer";
|
|
6
7
|
|
|
7
8
|
type Props = GpgConfig & {
|
|
8
9
|
children: ReactNode;
|
|
@@ -71,28 +72,15 @@ export const GpgProvider = ({
|
|
|
71
72
|
nonce.data ? (buildMessageRef.current ?? defaultBuildMessage)({ nonce: nonce.data }) : null,
|
|
72
73
|
[nonce.data],
|
|
73
74
|
);
|
|
74
|
-
|
|
75
|
-
// key, concatenated — exactly the blob the user pastes back. printf %s (no newline) so
|
|
76
|
-
// the signed bytes are byte-identical to `message`. Single quotes in the message are
|
|
77
|
-
// POSIX-escaped ('\'' ) so a custom buildMessage can't break out of the quoting.
|
|
78
|
-
const command = useMemo(
|
|
79
|
-
() =>
|
|
80
|
-
message
|
|
81
|
-
? `{ printf %s '${message.replace(/'/g, "'\\''")}' | gpg --detach-sign --armor; gpg --armor --export; }`
|
|
82
|
-
: null,
|
|
83
|
-
[message],
|
|
84
|
-
);
|
|
75
|
+
const command = useMemo(() => (message ? gpgSignCommand(message) : null), [message]);
|
|
85
76
|
|
|
86
77
|
const signIn = async (pastedBlob: string): Promise<boolean> => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
) {
|
|
78
|
+
let blob: string;
|
|
79
|
+
try {
|
|
80
|
+
blob = assertGpgSignature(pastedBlob);
|
|
81
|
+
} catch (error) {
|
|
92
82
|
setStatus(GpgStatusState.ERROR);
|
|
93
|
-
throw
|
|
94
|
-
"Paste the FULL output — it must contain both a PGP SIGNATURE and a PUBLIC KEY block.",
|
|
95
|
-
);
|
|
83
|
+
throw error;
|
|
96
84
|
}
|
|
97
85
|
if (!message) {
|
|
98
86
|
setStatus(GpgStatusState.ERROR);
|
package/src/index.ts
CHANGED
|
@@ -2,8 +2,17 @@
|
|
|
2
2
|
//
|
|
3
3
|
// A connection-free signer: the user signs a challenge in their terminal and pastes
|
|
4
4
|
// the result back. Pulls in NO wagmi/viem — depends only on @multiplatform.one/connectkit
|
|
5
|
-
// — so a GPG-only app can install it without the wallet stack.
|
|
5
|
+
// and @noble/hashes — so a GPG-only app can install it without the wallet stack.
|
|
6
6
|
export { useGpg } from "./useGpg";
|
|
7
|
+
export {
|
|
8
|
+
createGpgSigner,
|
|
9
|
+
useGpgSigner,
|
|
10
|
+
gpgSignCommand,
|
|
11
|
+
assertGpgSignature,
|
|
12
|
+
gpgSignatureCovers,
|
|
13
|
+
type GpgSigner,
|
|
14
|
+
type GpgSignRequest,
|
|
15
|
+
} from "./signer";
|
|
7
16
|
export { GpgProvider, GPG_NONCE_QUERY_KEY } from "./GpgProvider";
|
|
8
17
|
export { GpgContext, GpgStatusState } from "./GpgContext";
|
|
9
18
|
export type { GpgConfig, GpgContextValue } from "./GpgContext";
|
package/src/signature.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { ripemd160, sha1 } from "@noble/hashes/legacy.js";
|
|
2
|
+
import { sha224, sha256, sha384, sha512 } from "@noble/hashes/sha2.js";
|
|
3
|
+
|
|
4
|
+
// Checks that a pasted detached signature was made over a given message,
|
|
5
|
+
// without verifying it: every OpenPGP signature packet carries the first two
|
|
6
|
+
// bytes of its digest, so recomputing that digest over the message tells a
|
|
7
|
+
// signature for this message from one for another (1 in 65536 slips through).
|
|
8
|
+
// Keycloak's GPG verifier still does the real check.
|
|
9
|
+
|
|
10
|
+
const BEGIN = "-----BEGIN PGP SIGNATURE-----";
|
|
11
|
+
const END = "-----END PGP SIGNATURE-----";
|
|
12
|
+
|
|
13
|
+
const hashes: Record<number, (data: Uint8Array) => Uint8Array> = {
|
|
14
|
+
2: sha1,
|
|
15
|
+
3: ripemd160,
|
|
16
|
+
8: sha256,
|
|
17
|
+
9: sha384,
|
|
18
|
+
10: sha512,
|
|
19
|
+
11: sha224,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** The binary packets inside the first armored PGP SIGNATURE block of `text`. */
|
|
23
|
+
function dearmor(text: string): Uint8Array {
|
|
24
|
+
const start = text.indexOf(BEGIN);
|
|
25
|
+
const end = text.indexOf(END, start);
|
|
26
|
+
if (start < 0 || end < 0) throw new Error("No PGP SIGNATURE block in the paste");
|
|
27
|
+
const lines = text
|
|
28
|
+
.slice(start + BEGIN.length, end)
|
|
29
|
+
.split(/\r?\n/)
|
|
30
|
+
.map((line) => line.trim());
|
|
31
|
+
const blank = lines.indexOf("", 1);
|
|
32
|
+
const hasHeaders = lines.slice(1, blank < 0 ? 0 : blank).some((line) => line.includes(": "));
|
|
33
|
+
const body = (hasHeaders ? lines.slice(blank) : lines).filter(
|
|
34
|
+
(line) => line && !/^=[A-Za-z0-9+/]{4}$/.test(line),
|
|
35
|
+
);
|
|
36
|
+
const binary = atob(body.join(""));
|
|
37
|
+
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Signature packet bodies (tag 2), in old or new packet format. */
|
|
41
|
+
function signaturePackets(bytes: Uint8Array): Uint8Array[] {
|
|
42
|
+
const packets: Uint8Array[] = [];
|
|
43
|
+
let at = 0;
|
|
44
|
+
while (at < bytes.length) {
|
|
45
|
+
const ctb = bytes[at++];
|
|
46
|
+
if (!(ctb & 0x80)) throw new Error("The PGP SIGNATURE block is not OpenPGP data");
|
|
47
|
+
let tag: number;
|
|
48
|
+
let length: number;
|
|
49
|
+
if (ctb & 0x40) {
|
|
50
|
+
tag = ctb & 0x3f;
|
|
51
|
+
const first = bytes[at++];
|
|
52
|
+
if (first < 192) length = first;
|
|
53
|
+
else if (first < 224) length = ((first - 192) << 8) + bytes[at++] + 192;
|
|
54
|
+
else if (first === 255) {
|
|
55
|
+
length = new DataView(bytes.buffer, bytes.byteOffset + at, 4).getUint32(0);
|
|
56
|
+
at += 4;
|
|
57
|
+
} else throw new Error("Partial-length PGP packets are not a detached signature");
|
|
58
|
+
} else {
|
|
59
|
+
tag = (ctb >> 2) & 0x0f;
|
|
60
|
+
const size = [1, 2, 4][ctb & 0x03];
|
|
61
|
+
if (size === undefined) throw new Error("Indeterminate-length PGP packets are not supported");
|
|
62
|
+
length = 0;
|
|
63
|
+
for (let i = 0; i < size; i++) length = length * 256 + bytes[at++];
|
|
64
|
+
}
|
|
65
|
+
if (at + length > bytes.length) throw new Error("The PGP SIGNATURE block is truncated");
|
|
66
|
+
if (tag === 2) packets.push(bytes.subarray(at, at + length));
|
|
67
|
+
at += length;
|
|
68
|
+
}
|
|
69
|
+
return packets;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The first two digest bytes of a v4 signature packet recomputed over `message`. */
|
|
73
|
+
function quickCheck(packet: Uint8Array, message: Uint8Array): [expected: number, actual: number] {
|
|
74
|
+
if (packet[0] !== 4) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`A version ${packet[0]} PGP signature cannot be checked here; sign with a v4 key`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
const sigType = packet[1];
|
|
80
|
+
if (sigType !== 0x00 && sigType !== 0x01) {
|
|
81
|
+
throw new Error(`PGP signature type 0x${sigType.toString(16)} is not a document signature`);
|
|
82
|
+
}
|
|
83
|
+
const hash = hashes[packet[3]];
|
|
84
|
+
if (!hash) throw new Error(`PGP hash algorithm ${packet[3]} is not supported`);
|
|
85
|
+
const hashedEnd = 6 + ((packet[4] << 8) | packet[5]);
|
|
86
|
+
const unhashedLength = (packet[hashedEnd] << 8) | packet[hashedEnd + 1];
|
|
87
|
+
const left16 = hashedEnd + 2 + unhashedLength;
|
|
88
|
+
if (left16 + 2 > packet.length) throw new Error("The PGP signature packet is truncated");
|
|
89
|
+
|
|
90
|
+
const data =
|
|
91
|
+
sigType === 0x01
|
|
92
|
+
? new TextEncoder().encode(
|
|
93
|
+
new TextDecoder().decode(message).replace(/\r?\n/g, "\n").replace(/\n/g, "\r\n"),
|
|
94
|
+
)
|
|
95
|
+
: message;
|
|
96
|
+
const trailer = new Uint8Array(6);
|
|
97
|
+
trailer[0] = 0x04;
|
|
98
|
+
trailer[1] = 0xff;
|
|
99
|
+
new DataView(trailer.buffer).setUint32(2, hashedEnd);
|
|
100
|
+
const input = new Uint8Array(data.length + hashedEnd + trailer.length);
|
|
101
|
+
input.set(data, 0);
|
|
102
|
+
input.set(packet.subarray(0, hashedEnd), data.length);
|
|
103
|
+
input.set(trailer, data.length + hashedEnd);
|
|
104
|
+
const digest = hash(input);
|
|
105
|
+
return [(packet[left16] << 8) | packet[left16 + 1], (digest[0] << 8) | digest[1]];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* True when every signature in the pasted armored PGP SIGNATURE block was made
|
|
110
|
+
* over `message`'s UTF-8 bytes. Throws on a block it cannot read.
|
|
111
|
+
*/
|
|
112
|
+
export function gpgSignatureCovers(blob: string, message: string): boolean {
|
|
113
|
+
const packets = signaturePackets(dearmor(blob));
|
|
114
|
+
if (!packets.length) throw new Error("The PGP SIGNATURE block holds no signature");
|
|
115
|
+
const bytes = new TextEncoder().encode(message);
|
|
116
|
+
return packets.every((packet) => {
|
|
117
|
+
const [expected, actual] = quickCheck(packet, bytes);
|
|
118
|
+
return expected === actual;
|
|
119
|
+
});
|
|
120
|
+
}
|