@multiplatform.one/gpg 7.17.0 → 7.19.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.
Files changed (59) hide show
  1. package/dist/cjs/GpgIcon.cjs +55 -0
  2. package/dist/cjs/GpgIcon.native.js +72 -0
  3. package/dist/cjs/GpgIcon.native.js.map +1 -0
  4. package/dist/cjs/GpgProvider.cjs +7 -4
  5. package/dist/cjs/GpgProvider.native.js +7 -4
  6. package/dist/cjs/GpgProvider.native.js.map +1 -1
  7. package/dist/cjs/index.cjs +7 -1
  8. package/dist/cjs/index.native.js +7 -1
  9. package/dist/cjs/index.native.js.map +1 -1
  10. package/dist/cjs/screens.cjs +9 -2
  11. package/dist/cjs/screens.native.js +15 -2
  12. package/dist/cjs/screens.native.js.map +1 -1
  13. package/dist/cjs/signature.cjs +113 -0
  14. package/dist/cjs/signature.native.js +124 -0
  15. package/dist/cjs/signature.native.js.map +1 -0
  16. package/dist/cjs/signer.cjs +119 -0
  17. package/dist/cjs/signer.native.js +150 -0
  18. package/dist/cjs/signer.native.js.map +1 -0
  19. package/dist/esm/GpgIcon.mjs +30 -0
  20. package/dist/esm/GpgIcon.mjs.map +1 -0
  21. package/dist/esm/GpgIcon.native.js +33 -0
  22. package/dist/esm/GpgIcon.native.js.map +1 -0
  23. package/dist/esm/GpgProvider.mjs +7 -4
  24. package/dist/esm/GpgProvider.mjs.map +1 -1
  25. package/dist/esm/GpgProvider.native.js +7 -4
  26. package/dist/esm/GpgProvider.native.js.map +1 -1
  27. package/dist/esm/index.mjs +2 -1
  28. package/dist/esm/index.mjs.map +1 -1
  29. package/dist/esm/index.native.js +2 -1
  30. package/dist/esm/index.native.js.map +1 -1
  31. package/dist/esm/screens.mjs +9 -2
  32. package/dist/esm/screens.mjs.map +1 -1
  33. package/dist/esm/screens.native.js +15 -2
  34. package/dist/esm/screens.native.js.map +1 -1
  35. package/dist/esm/signature.mjs +88 -0
  36. package/dist/esm/signature.mjs.map +1 -0
  37. package/dist/esm/signature.native.js +96 -0
  38. package/dist/esm/signature.native.js.map +1 -0
  39. package/dist/esm/signer.mjs +88 -0
  40. package/dist/esm/signer.mjs.map +1 -0
  41. package/dist/esm/signer.native.js +116 -0
  42. package/dist/esm/signer.native.js.map +1 -0
  43. package/package.json +16 -3
  44. package/src/GpgIcon.tsx +20 -0
  45. package/src/GpgProvider.tsx +7 -19
  46. package/src/index.ts +10 -1
  47. package/src/screens.tsx +11 -1
  48. package/src/signature.ts +120 -0
  49. package/src/signer.ts +142 -0
  50. package/types/GpgIcon.d.ts +3 -0
  51. package/types/GpgIcon.d.ts.map +1 -0
  52. package/types/GpgProvider.d.ts.map +1 -1
  53. package/types/index.d.ts +1 -0
  54. package/types/index.d.ts.map +1 -1
  55. package/types/screens.d.ts.map +1 -1
  56. package/types/signature.d.ts +6 -0
  57. package/types/signature.d.ts.map +1 -0
  58. package/types/signer.d.ts +47 -0
  59. package/types/signer.d.ts.map +1 -0
@@ -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
+ }
package/src/signer.ts ADDED
@@ -0,0 +1,142 @@
1
+ import { useSyncExternalStore } from "react";
2
+ import { gpgSignatureCovers } from "./signature";
3
+
4
+ export { gpgSignatureCovers } from "./signature";
5
+
6
+ // The GPG copy/paste signer as a client, shared by login (GpgProvider) and any
7
+ // other signing an app needs. There is no wallet to talk to: signMessage parks a
8
+ // request, the app shows the terminal command, the user pastes the output back
9
+ // and submit() resolves the pending promise. Only react and @noble/hashes are
10
+ // imported, so the `@multiplatform.one/gpg/signer` subpath stays out of the
11
+ // connectkit graph.
12
+
13
+ export const GPG_SIGNATURE_BEGIN = "-----BEGIN PGP SIGNATURE-----";
14
+ export const GPG_PUBLIC_KEY_BEGIN = "-----BEGIN PGP PUBLIC KEY BLOCK-----";
15
+
16
+ /**
17
+ * The terminal command that signs `message`: a detached armored signature plus
18
+ * the exported public key, which is the blob the user pastes back. printf %s adds
19
+ * no newline, so the signed bytes equal `message`. Single quotes are POSIX-escaped
20
+ * so a message cannot break out of the quoting.
21
+ */
22
+ export function gpgSignCommand(message: string): string {
23
+ return `{ printf %s '${message.replace(/'/g, "'\\''")}' | gpg --detach-sign --armor; gpg --armor --export; }`;
24
+ }
25
+
26
+ /** Trim the pasted terminal output and reject it unless it holds both armored blocks. */
27
+ export function assertGpgSignature(blob: string): string {
28
+ const trimmed = blob.trim();
29
+ if (!trimmed.includes(GPG_SIGNATURE_BEGIN) || !trimmed.includes(GPG_PUBLIC_KEY_BEGIN)) {
30
+ throw new Error(
31
+ "Paste the FULL output — it must contain both a PGP SIGNATURE and a PUBLIC KEY block.",
32
+ );
33
+ }
34
+ return trimmed;
35
+ }
36
+
37
+ export interface GpgSignRequest {
38
+ message: string;
39
+ command: string;
40
+ }
41
+
42
+ export interface GpgSigner {
43
+ readonly family: "gpg";
44
+ /** A GPG identity is the key fingerprint, which only arrives with the signature. */
45
+ readonly address: null;
46
+ readonly capabilities: readonly "signMessage"[];
47
+ /** The request waiting for pasted output, or null. */
48
+ readonly pending: GpgSignRequest | null;
49
+ /**
50
+ * Ask the user to sign `message` in their terminal. Resolves with the pasted
51
+ * armored signature followed by the armored public key, the same blob Keycloak's
52
+ * GPG verifier takes. A second call cancels the first.
53
+ */
54
+ signMessage(message: string): Promise<string>;
55
+ /**
56
+ * Hand pasted output to `request`, the pending request the paste box showed.
57
+ * Throws, and keeps the request pending, when `request` is no longer the
58
+ * pending one, when the paste is incomplete, or when its signature was made
59
+ * over a different message.
60
+ */
61
+ submit(blob: string, request: GpgSignRequest): void;
62
+ cancel(reason?: Error): void;
63
+ subscribe(listener: () => void): () => void;
64
+ }
65
+
66
+ export function createGpgSigner(): GpgSigner {
67
+ let pending: GpgSignRequest | null = null;
68
+ let settle: { resolve: (blob: string) => void; reject: (error: Error) => void } | null = null;
69
+ const listeners = new Set<() => void>();
70
+ const emit = () => {
71
+ for (const listener of listeners) listener();
72
+ };
73
+
74
+ const signer: GpgSigner = {
75
+ family: "gpg",
76
+ address: null,
77
+ capabilities: ["signMessage"],
78
+ get pending() {
79
+ return pending;
80
+ },
81
+ signMessage(message) {
82
+ settle?.reject(new Error("GPG signing was superseded by a newer request"));
83
+ return new Promise<string>((resolve, reject) => {
84
+ settle = { resolve, reject };
85
+ pending = { message, command: gpgSignCommand(message) };
86
+ emit();
87
+ });
88
+ },
89
+ submit(blob, request) {
90
+ if (!settle || !pending) throw new Error("No GPG signature was requested");
91
+ if (request !== pending) {
92
+ throw new Error(
93
+ "That GPG request was superseded; run the current command and paste its output",
94
+ );
95
+ }
96
+ const signature = assertGpgSignature(blob);
97
+ if (!gpgSignatureCovers(signature, pending.message)) {
98
+ throw new Error(
99
+ "That signature is over a different message; run the current command and paste its output",
100
+ );
101
+ }
102
+ const { resolve } = settle;
103
+ settle = null;
104
+ pending = null;
105
+ emit();
106
+ resolve(signature);
107
+ },
108
+ cancel(reason) {
109
+ if (!settle) return;
110
+ const { reject } = settle;
111
+ settle = null;
112
+ pending = null;
113
+ emit();
114
+ reject(reason ?? new Error("GPG signing was cancelled"));
115
+ },
116
+ subscribe(listener) {
117
+ listeners.add(listener);
118
+ return () => listeners.delete(listener);
119
+ },
120
+ };
121
+ return signer;
122
+ }
123
+
124
+ /**
125
+ * Subscribe a component to a GPG signer so it re-renders when a request is
126
+ * parked or settled. Omitted, it is the app-wide signer, so the component that
127
+ * asks for a signature and the one that shows the paste box share one request.
128
+ */
129
+ export function useGpgSigner(signer: GpgSigner = defaultSigner()): GpgSigner {
130
+ useSyncExternalStore(
131
+ signer.subscribe,
132
+ () => signer.pending,
133
+ () => signer.pending,
134
+ );
135
+ return signer;
136
+ }
137
+
138
+ let sharedSigner: GpgSigner | null = null;
139
+ function defaultSigner(): GpgSigner {
140
+ sharedSigner ??= createGpgSigner();
141
+ return sharedSigner;
142
+ }
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ export declare const GpgIcon: React.FC;
3
+ //# sourceMappingURL=GpgIcon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GpgIcon.d.ts","sourceRoot":"","sources":["../src/GpgIcon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAe3B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"GpgProvider.d.ts","sourceRoot":"","sources":["../src/GpgProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyC,MAAM,OAAO,CAAC;AAIzE,OAAO,EAAc,SAAS,EAAkB,MAAM,cAAc,CAAC;AAErE,KAAK,KAAK,GAAG,SAAS,GAAG;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAkBhD,eAAO,MAAM,WAAW,GAAI,mFAOzB,KAAK,4CAqGP,CAAC"}
1
+ {"version":3,"file":"GpgProvider.d.ts","sourceRoot":"","sources":["../src/GpgProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyC,MAAM,OAAO,CAAC;AAIzE,OAAO,EAAc,SAAS,EAAkB,MAAM,cAAc,CAAC;AAGrE,KAAK,KAAK,GAAG,SAAS,GAAG;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAkBhD,eAAO,MAAM,WAAW,GAAI,mFAOzB,KAAK,4CAwFP,CAAC"}
package/types/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { useGpg } from "./useGpg";
2
+ export { createGpgSigner, useGpgSigner, gpgSignCommand, assertGpgSignature, gpgSignatureCovers, type GpgSigner, type GpgSignRequest, } from "./signer";
2
3
  export { GpgProvider, GPG_NONCE_QUERY_KEY } from "./GpgProvider";
3
4
  export { GpgContext, GpgStatusState } from "./GpgContext";
4
5
  export type { GpgConfig, GpgContextValue } from "./GpgContext";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1D,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EACL,eAAe,EACf,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,SAAS,EACd,KAAK,cAAc,GACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1D,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"screens.d.ts","sourceRoot":"","sources":["../src/screens.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACf,MAAM,+BAA+B,CAAC;AAOvC,eAAO,MAAM,SAAS,EAAE,SAOvB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAGtC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,GAAG,oBAAoB,CAUrF,CAAC"}
1
+ {"version":3,"file":"screens.d.ts","sourceRoot":"","sources":["../src/screens.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACf,MAAM,+BAA+B,CAAC;AAWvC,eAAO,MAAM,SAAS,EAAE,SAavB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAGtC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,GAAG,oBAAoB,CAUrF,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * True when every signature in the pasted armored PGP SIGNATURE block was made
3
+ * over `message`'s UTF-8 bytes. Throws on a block it cannot read.
4
+ */
5
+ export declare function gpgSignatureCovers(blob: string, message: string): boolean;
6
+ //# sourceMappingURL=signature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../src/signature.ts"],"names":[],"mappings":"AA2GA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAQzE"}
@@ -0,0 +1,47 @@
1
+ export { gpgSignatureCovers } from "./signature";
2
+ export declare const GPG_SIGNATURE_BEGIN = "-----BEGIN PGP SIGNATURE-----";
3
+ export declare const GPG_PUBLIC_KEY_BEGIN = "-----BEGIN PGP PUBLIC KEY BLOCK-----";
4
+ /**
5
+ * The terminal command that signs `message`: a detached armored signature plus
6
+ * the exported public key, which is the blob the user pastes back. printf %s adds
7
+ * no newline, so the signed bytes equal `message`. Single quotes are POSIX-escaped
8
+ * so a message cannot break out of the quoting.
9
+ */
10
+ export declare function gpgSignCommand(message: string): string;
11
+ /** Trim the pasted terminal output and reject it unless it holds both armored blocks. */
12
+ export declare function assertGpgSignature(blob: string): string;
13
+ export interface GpgSignRequest {
14
+ message: string;
15
+ command: string;
16
+ }
17
+ export interface GpgSigner {
18
+ readonly family: "gpg";
19
+ /** A GPG identity is the key fingerprint, which only arrives with the signature. */
20
+ readonly address: null;
21
+ readonly capabilities: readonly "signMessage"[];
22
+ /** The request waiting for pasted output, or null. */
23
+ readonly pending: GpgSignRequest | null;
24
+ /**
25
+ * Ask the user to sign `message` in their terminal. Resolves with the pasted
26
+ * armored signature followed by the armored public key, the same blob Keycloak's
27
+ * GPG verifier takes. A second call cancels the first.
28
+ */
29
+ signMessage(message: string): Promise<string>;
30
+ /**
31
+ * Hand pasted output to `request`, the pending request the paste box showed.
32
+ * Throws, and keeps the request pending, when `request` is no longer the
33
+ * pending one, when the paste is incomplete, or when its signature was made
34
+ * over a different message.
35
+ */
36
+ submit(blob: string, request: GpgSignRequest): void;
37
+ cancel(reason?: Error): void;
38
+ subscribe(listener: () => void): () => void;
39
+ }
40
+ export declare function createGpgSigner(): GpgSigner;
41
+ /**
42
+ * Subscribe a component to a GPG signer so it re-renders when a request is
43
+ * parked or settled. Omitted, it is the app-wide signer, so the component that
44
+ * asks for a signature and the one that shows the paste box share one request.
45
+ */
46
+ export declare function useGpgSigner(signer?: GpgSigner): GpgSigner;
47
+ //# sourceMappingURL=signer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AASjD,eAAO,MAAM,mBAAmB,kCAAkC,CAAC;AACnE,eAAO,MAAM,oBAAoB,yCAAyC,CAAC;AAE3E;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,yFAAyF;AACzF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQvD;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IACvB,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,SAAS,aAAa,EAAE,CAAC;IAChD,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC;;;;OAIG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;;OAKG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACpD,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IAC7B,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7C;AAED,wBAAgB,eAAe,IAAI,SAAS,CAwD3C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,SAA2B,GAAG,SAAS,CAO3E"}