@freedomofpress/sigstore-browser 0.1.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 (193) hide show
  1. package/.github/workflows/conformance.yml +29 -0
  2. package/.github/workflows/publish.yml +45 -0
  3. package/README.md +10 -0
  4. package/dist/asn1/error.d.ts +5 -0
  5. package/dist/asn1/error.d.ts.map +1 -0
  6. package/dist/asn1/error.js +20 -0
  7. package/dist/asn1/error.js.map +1 -0
  8. package/dist/asn1/index.d.ts +2 -0
  9. package/dist/asn1/index.d.ts.map +1 -0
  10. package/dist/asn1/index.js +17 -0
  11. package/dist/asn1/index.js.map +1 -0
  12. package/dist/asn1/length.d.ts +4 -0
  13. package/dist/asn1/length.d.ts.map +1 -0
  14. package/dist/asn1/length.js +59 -0
  15. package/dist/asn1/length.js.map +1 -0
  16. package/dist/asn1/obj.d.ts +15 -0
  17. package/dist/asn1/obj.d.ts.map +1 -0
  18. package/dist/asn1/obj.js +153 -0
  19. package/dist/asn1/obj.js.map +1 -0
  20. package/dist/asn1/parse.d.ts +7 -0
  21. package/dist/asn1/parse.d.ts.map +1 -0
  22. package/dist/asn1/parse.js +118 -0
  23. package/dist/asn1/parse.js.map +1 -0
  24. package/dist/asn1/tag.d.ts +17 -0
  25. package/dist/asn1/tag.d.ts.map +1 -0
  26. package/dist/asn1/tag.js +86 -0
  27. package/dist/asn1/tag.js.map +1 -0
  28. package/dist/bundle.d.ts +87 -0
  29. package/dist/bundle.d.ts.map +1 -0
  30. package/dist/bundle.js +2 -0
  31. package/dist/bundle.js.map +1 -0
  32. package/dist/canonicalize.d.ts +2 -0
  33. package/dist/canonicalize.d.ts.map +1 -0
  34. package/dist/canonicalize.js +64 -0
  35. package/dist/canonicalize.js.map +1 -0
  36. package/dist/cli.d.ts +3 -0
  37. package/dist/cli.d.ts.map +1 -0
  38. package/dist/cli.js +217 -0
  39. package/dist/cli.js.map +1 -0
  40. package/dist/crypto.d.ts +5 -0
  41. package/dist/crypto.d.ts.map +1 -0
  42. package/dist/crypto.js +260 -0
  43. package/dist/crypto.js.map +1 -0
  44. package/dist/dsse.d.ts +2 -0
  45. package/dist/dsse.d.ts.map +1 -0
  46. package/dist/dsse.js +42 -0
  47. package/dist/dsse.js.map +1 -0
  48. package/dist/encoding.d.ts +17 -0
  49. package/dist/encoding.d.ts.map +1 -0
  50. package/dist/encoding.js +112 -0
  51. package/dist/encoding.js.map +1 -0
  52. package/dist/errors.d.ts +39 -0
  53. package/dist/errors.d.ts.map +1 -0
  54. package/dist/errors.js +79 -0
  55. package/dist/errors.js.map +1 -0
  56. package/dist/index.d.ts +8 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +5 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/interfaces.d.ts +104 -0
  61. package/dist/interfaces.d.ts.map +1 -0
  62. package/dist/interfaces.js +26 -0
  63. package/dist/interfaces.js.map +1 -0
  64. package/dist/oid.d.ts +5 -0
  65. package/dist/oid.d.ts.map +1 -0
  66. package/dist/oid.js +25 -0
  67. package/dist/oid.js.map +1 -0
  68. package/dist/pem.d.ts +3 -0
  69. package/dist/pem.d.ts.map +1 -0
  70. package/dist/pem.js +41 -0
  71. package/dist/pem.js.map +1 -0
  72. package/dist/rfc3161/error.d.ts +3 -0
  73. package/dist/rfc3161/error.d.ts.map +1 -0
  74. package/dist/rfc3161/error.js +18 -0
  75. package/dist/rfc3161/error.js.map +1 -0
  76. package/dist/rfc3161/index.d.ts +2 -0
  77. package/dist/rfc3161/index.d.ts.map +1 -0
  78. package/dist/rfc3161/index.js +17 -0
  79. package/dist/rfc3161/index.js.map +1 -0
  80. package/dist/rfc3161/timestamp.d.ts +36 -0
  81. package/dist/rfc3161/timestamp.d.ts.map +1 -0
  82. package/dist/rfc3161/timestamp.js +203 -0
  83. package/dist/rfc3161/timestamp.js.map +1 -0
  84. package/dist/rfc3161/tstinfo.d.ts +13 -0
  85. package/dist/rfc3161/tstinfo.d.ts.map +1 -0
  86. package/dist/rfc3161/tstinfo.js +47 -0
  87. package/dist/rfc3161/tstinfo.js.map +1 -0
  88. package/dist/sigstore.d.ts +33 -0
  89. package/dist/sigstore.d.ts.map +1 -0
  90. package/dist/sigstore.js +531 -0
  91. package/dist/sigstore.js.map +1 -0
  92. package/dist/stream.d.ts +22 -0
  93. package/dist/stream.d.ts.map +1 -0
  94. package/dist/stream.js +114 -0
  95. package/dist/stream.js.map +1 -0
  96. package/dist/timestamp/tsa.d.ts +24 -0
  97. package/dist/timestamp/tsa.d.ts.map +1 -0
  98. package/dist/timestamp/tsa.js +150 -0
  99. package/dist/timestamp/tsa.js.map +1 -0
  100. package/dist/tlog/body.d.ts +8 -0
  101. package/dist/tlog/body.d.ts.map +1 -0
  102. package/dist/tlog/body.js +45 -0
  103. package/dist/tlog/body.js.map +1 -0
  104. package/dist/tlog/checkpoint.d.ts +23 -0
  105. package/dist/tlog/checkpoint.d.ts.map +1 -0
  106. package/dist/tlog/checkpoint.js +187 -0
  107. package/dist/tlog/checkpoint.js.map +1 -0
  108. package/dist/tlog/dsse.d.ts +4 -0
  109. package/dist/tlog/dsse.d.ts.map +1 -0
  110. package/dist/tlog/dsse.js +85 -0
  111. package/dist/tlog/dsse.js.map +1 -0
  112. package/dist/tlog/hashedrekord.d.ts +4 -0
  113. package/dist/tlog/hashedrekord.d.ts.map +1 -0
  114. package/dist/tlog/hashedrekord.js +63 -0
  115. package/dist/tlog/hashedrekord.js.map +1 -0
  116. package/dist/tlog/intoto.d.ts +4 -0
  117. package/dist/tlog/intoto.d.ts.map +1 -0
  118. package/dist/tlog/intoto.js +50 -0
  119. package/dist/tlog/intoto.js.map +1 -0
  120. package/dist/tlog/merkle.d.ts +3 -0
  121. package/dist/tlog/merkle.d.ts.map +1 -0
  122. package/dist/tlog/merkle.js +112 -0
  123. package/dist/tlog/merkle.js.map +1 -0
  124. package/dist/trust/tuf-root.json +1 -0
  125. package/dist/trust/tuf.d.ts +95 -0
  126. package/dist/trust/tuf.d.ts.map +1 -0
  127. package/dist/trust/tuf.js +150 -0
  128. package/dist/trust/tuf.js.map +1 -0
  129. package/dist/x509/cert.d.ts +52 -0
  130. package/dist/x509/cert.d.ts.map +1 -0
  131. package/dist/x509/cert.js +256 -0
  132. package/dist/x509/cert.js.map +1 -0
  133. package/dist/x509/chain.d.ts +20 -0
  134. package/dist/x509/chain.d.ts.map +1 -0
  135. package/dist/x509/chain.js +141 -0
  136. package/dist/x509/chain.js.map +1 -0
  137. package/dist/x509/ext.d.ts +48 -0
  138. package/dist/x509/ext.d.ts.map +1 -0
  139. package/dist/x509/ext.js +164 -0
  140. package/dist/x509/ext.js.map +1 -0
  141. package/dist/x509/index.d.ts +4 -0
  142. package/dist/x509/index.d.ts.map +1 -0
  143. package/dist/x509/index.js +19 -0
  144. package/dist/x509/index.js.map +1 -0
  145. package/dist/x509/sct.d.ts +25 -0
  146. package/dist/x509/sct.d.ts.map +1 -0
  147. package/dist/x509/sct.js +123 -0
  148. package/dist/x509/sct.js.map +1 -0
  149. package/eslint.config.mjs +31 -0
  150. package/package.json +39 -0
  151. package/src/asn1/error.ts +18 -0
  152. package/src/asn1/index.ts +16 -0
  153. package/src/asn1/length.ts +71 -0
  154. package/src/asn1/obj.ts +184 -0
  155. package/src/asn1/parse.ts +146 -0
  156. package/src/asn1/tag.ts +105 -0
  157. package/src/bundle.ts +99 -0
  158. package/src/canonicalize.ts +61 -0
  159. package/src/cli.ts +285 -0
  160. package/src/crypto.ts +310 -0
  161. package/src/dsse.ts +49 -0
  162. package/src/encoding.ts +141 -0
  163. package/src/errors.ts +82 -0
  164. package/src/index.browser.test.ts +219 -0
  165. package/src/index.ts +14 -0
  166. package/src/interfaces.ts +118 -0
  167. package/src/oid.ts +28 -0
  168. package/src/pem.ts +48 -0
  169. package/src/rfc3161/error.ts +16 -0
  170. package/src/rfc3161/index.ts +17 -0
  171. package/src/rfc3161/timestamp.ts +270 -0
  172. package/src/rfc3161/tstinfo.ts +80 -0
  173. package/src/sigstore.ts +728 -0
  174. package/src/stream.ts +141 -0
  175. package/src/timestamp/tsa.ts +205 -0
  176. package/src/tlog/body.ts +65 -0
  177. package/src/tlog/checkpoint.ts +248 -0
  178. package/src/tlog/dsse.ts +147 -0
  179. package/src/tlog/hashedrekord.ts +130 -0
  180. package/src/tlog/intoto.ts +102 -0
  181. package/src/tlog/merkle.ts +152 -0
  182. package/src/trust/README.md +38 -0
  183. package/src/trust/tuf-root.json +1 -0
  184. package/src/trust/tuf.ts +223 -0
  185. package/src/types/tuf-browser.d.ts +13 -0
  186. package/src/x509/cert.ts +319 -0
  187. package/src/x509/chain.ts +212 -0
  188. package/src/x509/ext.ts +212 -0
  189. package/src/x509/index.ts +19 -0
  190. package/src/x509/sct.ts +154 -0
  191. package/tsconfig.json +18 -0
  192. package/vite.config.playwright.ts +26 -0
  193. package/vite.config.ts +18 -0
@@ -0,0 +1,141 @@
1
+ /*
2
+ Copyright 2023 The Sigstore Authors.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+ export function toArrayBuffer(view: ArrayBufferView): ArrayBuffer {
17
+ const { byteLength } = view;
18
+
19
+ if (byteLength === 0) {
20
+ return new ArrayBuffer(0);
21
+ }
22
+
23
+ const { buffer, byteOffset } = view;
24
+
25
+ if (
26
+ buffer instanceof ArrayBuffer &&
27
+ byteOffset === 0 &&
28
+ byteLength === buffer.byteLength
29
+ ) {
30
+ return buffer;
31
+ }
32
+
33
+ const clone = new Uint8Array(byteLength);
34
+ clone.set(new Uint8Array(buffer, byteOffset, byteLength));
35
+ return clone.buffer;
36
+ }
37
+
38
+ export function base64ToUint8Array(base64: string): Uint8Array {
39
+ const binaryString = atob(base64);
40
+ const length = binaryString.length;
41
+ const bytes = new Uint8Array(length);
42
+
43
+ for (let i = 0; i < length; i++) {
44
+ bytes[i] = binaryString.charCodeAt(i);
45
+ }
46
+
47
+ return bytes;
48
+ }
49
+
50
+ export function base64UrlToUint8Array(base64url: string): Uint8Array {
51
+ let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
52
+ while (base64.length % 4 !== 0) {
53
+ base64 += '=';
54
+ }
55
+ return base64ToUint8Array(base64);
56
+ }
57
+
58
+ export function Uint8ArrayToBase64(uint8Array: Uint8Array): string {
59
+ let binaryString = "";
60
+
61
+ for (let i = 0; i < uint8Array.length; i++) {
62
+ binaryString += String.fromCharCode(uint8Array[i]);
63
+ }
64
+
65
+ return btoa(binaryString);
66
+ }
67
+
68
+ export function hexToUint8Array(hex: string): Uint8Array {
69
+ if (hex.length % 2 !== 0) {
70
+ throw new Error("Hex string must have an even length");
71
+ }
72
+
73
+ const length = hex.length / 2;
74
+ const uint8Array = new Uint8Array(length);
75
+
76
+ for (let i = 0; i < length; i++) {
77
+ uint8Array[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
78
+ }
79
+
80
+ return uint8Array;
81
+ }
82
+
83
+ export function Uint8ArrayToHex(uint8Array: Uint8Array): string {
84
+ let hexString = "";
85
+
86
+ for (let i = 0; i < uint8Array.length; i++) {
87
+ let hex = uint8Array[i].toString(16);
88
+ if (hex.length === 1) {
89
+ hex = "0" + hex;
90
+ }
91
+ hexString += hex;
92
+ }
93
+
94
+ return hexString;
95
+ }
96
+
97
+ export function stringToUint8Array(str: string): Uint8Array {
98
+ // Defaults to utf-8, but utf-8 is ascii compatible
99
+ const encoder = new TextEncoder();
100
+ return encoder.encode(str);
101
+ }
102
+
103
+ export function Uint8ArrayToString(uint8Array: Uint8Array): string {
104
+ const decoder = new TextDecoder("utf-8");
105
+ return decoder.decode(uint8Array);
106
+ }
107
+
108
+ export function readBigInt64BE(
109
+ uint8Array: Uint8Array,
110
+ offset?: number,
111
+ ): bigint {
112
+ if (offset === undefined) {
113
+ offset = 0;
114
+ }
115
+ const hex = Uint8ArrayToHex(uint8Array.slice(offset, offset + 8));
116
+ return BigInt(`0x${hex}`);
117
+ }
118
+
119
+ export function base64Encode(str: string): string {
120
+ return Uint8ArrayToBase64(stringToUint8Array(str));
121
+ }
122
+
123
+ export function base64Decode(str: string): string {
124
+ return Uint8ArrayToString(base64ToUint8Array(str));
125
+ }
126
+
127
+ /**
128
+ * Constant-time equality comparison for Uint8Arrays
129
+ * Prevents timing attacks by always comparing all bytes
130
+ */
131
+ export function uint8ArrayEqual(a: Uint8Array, b: Uint8Array): boolean {
132
+ if (a.byteLength !== b.byteLength) {
133
+ return false;
134
+ }
135
+
136
+ let result = 0;
137
+ for (let i = 0; i < a.byteLength; i++) {
138
+ result |= a[i] ^ b[i];
139
+ }
140
+ return result === 0;
141
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,82 @@
1
+ /*
2
+ Copyright 2023 The Sigstore Authors.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ /**
18
+ * Base class for all verification errors
19
+ * Provides structured error handling with error codes
20
+ */
21
+ export class VerificationError extends Error {
22
+ constructor(public code: string, message: string) {
23
+ super(message);
24
+ this.name = 'VerificationError';
25
+ Object.setPrototypeOf(this, VerificationError.prototype);
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Error thrown when timestamp verification fails
31
+ */
32
+ export class TimestampError extends VerificationError {
33
+ constructor(message: string) {
34
+ super('TIMESTAMP_ERROR', message);
35
+ this.name = 'TimestampError';
36
+ Object.setPrototypeOf(this, TimestampError.prototype);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Error thrown when certificate verification fails
42
+ */
43
+ export class CertificateError extends VerificationError {
44
+ constructor(message: string) {
45
+ super('CERTIFICATE_ERROR', message);
46
+ this.name = 'CertificateError';
47
+ Object.setPrototypeOf(this, CertificateError.prototype);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Error thrown when transparency log verification fails
53
+ */
54
+ export class TLogError extends VerificationError {
55
+ constructor(message: string) {
56
+ super('TLOG_ERROR', message);
57
+ this.name = 'TLogError';
58
+ Object.setPrototypeOf(this, TLogError.prototype);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Error thrown when signature verification fails
64
+ */
65
+ export class SignatureError extends VerificationError {
66
+ constructor(message: string) {
67
+ super('SIGNATURE_ERROR', message);
68
+ this.name = 'SignatureError';
69
+ Object.setPrototypeOf(this, SignatureError.prototype);
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Error thrown when policy verification fails
75
+ */
76
+ export class PolicyError extends VerificationError {
77
+ constructor(message: string) {
78
+ super('POLICY_ERROR', message);
79
+ this.name = 'PolicyError';
80
+ Object.setPrototypeOf(this, PolicyError.prototype);
81
+ }
82
+ }
@@ -0,0 +1,219 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { SigstoreVerifier } from "./sigstore.js";
3
+
4
+ describe("Sigstore Browser Integration Tests", () => {
5
+ it("should initialize SigstoreVerifier in browser", () => {
6
+ const verifier = new SigstoreVerifier();
7
+ expect(verifier).toBeDefined();
8
+ expect(verifier).toBeInstanceOf(SigstoreVerifier);
9
+ });
10
+
11
+ it("should have crypto.subtle available for ECDSA P-256 verification", async () => {
12
+ expect(globalThis.crypto).toBeDefined();
13
+ expect(globalThis.crypto.subtle).toBeDefined();
14
+
15
+ // Test ECDSA P-256 which is what Sigstore uses
16
+ const keyPair = await crypto.subtle.generateKey(
17
+ {
18
+ name: "ECDSA",
19
+ namedCurve: "P-256",
20
+ },
21
+ true,
22
+ ["sign", "verify"]
23
+ );
24
+
25
+ const data = new TextEncoder().encode("test");
26
+ const signature = await crypto.subtle.sign(
27
+ { name: "ECDSA", hash: "SHA-256" },
28
+ keyPair.privateKey,
29
+ data
30
+ );
31
+
32
+ const valid = await crypto.subtle.verify(
33
+ { name: "ECDSA", hash: "SHA-256" },
34
+ keyPair.publicKey,
35
+ signature,
36
+ data
37
+ );
38
+
39
+ expect(valid).toBe(true);
40
+ });
41
+
42
+ it("should have crypto.subtle available for ECDSA P-384 verification", async () => {
43
+ // Test ECDSA P-384 which is also used by Sigstore
44
+ const keyPair = await crypto.subtle.generateKey(
45
+ {
46
+ name: "ECDSA",
47
+ namedCurve: "P-384",
48
+ },
49
+ true,
50
+ ["sign", "verify"]
51
+ );
52
+
53
+ const data = new TextEncoder().encode("test");
54
+ const signature = await crypto.subtle.sign(
55
+ { name: "ECDSA", hash: "SHA-384" },
56
+ keyPair.privateKey,
57
+ data
58
+ );
59
+
60
+ const valid = await crypto.subtle.verify(
61
+ { name: "ECDSA", hash: "SHA-384" },
62
+ keyPair.publicKey,
63
+ signature,
64
+ data
65
+ );
66
+
67
+ expect(valid).toBe(true);
68
+ });
69
+
70
+ it("should support SHA-256 hashing in browser", async () => {
71
+ const data = new TextEncoder().encode("test data");
72
+ const hash = await crypto.subtle.digest("SHA-256", data);
73
+
74
+ expect(hash).toBeInstanceOf(ArrayBuffer);
75
+ expect(hash.byteLength).toBe(32);
76
+ });
77
+
78
+ it("should support SHA-384 hashing in browser", async () => {
79
+ const data = new TextEncoder().encode("test data");
80
+ const hash = await crypto.subtle.digest("SHA-384", data);
81
+
82
+ expect(hash).toBeInstanceOf(ArrayBuffer);
83
+ expect(hash.byteLength).toBe(48);
84
+ });
85
+
86
+ it("should support SHA-512 hashing in browser", async () => {
87
+ const data = new TextEncoder().encode("test data");
88
+ const hash = await crypto.subtle.digest("SHA-512", data);
89
+
90
+ expect(hash).toBeInstanceOf(ArrayBuffer);
91
+ expect(hash.byteLength).toBe(64);
92
+ });
93
+
94
+ it("should support X.509 certificate key import (SPKI format)", async () => {
95
+ const keyPair = await crypto.subtle.generateKey(
96
+ {
97
+ name: "ECDSA",
98
+ namedCurve: "P-256",
99
+ },
100
+ true,
101
+ ["sign", "verify"]
102
+ );
103
+
104
+ const exported = await crypto.subtle.exportKey("spki", keyPair.publicKey);
105
+ const imported = await crypto.subtle.importKey(
106
+ "spki",
107
+ exported,
108
+ { name: "ECDSA", namedCurve: "P-256" },
109
+ true,
110
+ ["verify"]
111
+ );
112
+
113
+ expect(imported).toBeDefined();
114
+ expect(imported.type).toBe("public");
115
+ });
116
+
117
+ it("should support RSA-PSS for signature verification", async () => {
118
+ const keyPair = await crypto.subtle.generateKey(
119
+ {
120
+ name: "RSA-PSS",
121
+ modulusLength: 2048,
122
+ publicExponent: new Uint8Array([1, 0, 1]),
123
+ hash: "SHA-256",
124
+ },
125
+ true,
126
+ ["sign", "verify"]
127
+ );
128
+
129
+ const data = new TextEncoder().encode("test");
130
+ const signature = await crypto.subtle.sign(
131
+ {
132
+ name: "RSA-PSS",
133
+ saltLength: 32,
134
+ },
135
+ keyPair.privateKey,
136
+ data
137
+ );
138
+
139
+ const valid = await crypto.subtle.verify(
140
+ {
141
+ name: "RSA-PSS",
142
+ saltLength: 32,
143
+ },
144
+ keyPair.publicKey,
145
+ signature,
146
+ data
147
+ );
148
+
149
+ expect(valid).toBe(true);
150
+ });
151
+
152
+ it("should support TextEncoder/TextDecoder for UTF-8", () => {
153
+ const encoder = new TextEncoder();
154
+ const decoder = new TextDecoder();
155
+
156
+ const text = "Hello Sigstore 🔐";
157
+ const encoded = encoder.encode(text);
158
+ const decoded = decoder.decode(encoded);
159
+
160
+ expect(decoded).toBe(text);
161
+ expect(encoded).toBeInstanceOf(Uint8Array);
162
+ });
163
+
164
+ it("should support base64 encoding/decoding (atob/btoa)", () => {
165
+ const text = "Hello Sigstore";
166
+ const base64 = btoa(text);
167
+ const decoded = atob(base64);
168
+
169
+ expect(decoded).toBe(text);
170
+ expect(base64).toBe("SGVsbG8gU2lnc3RvcmU=");
171
+ });
172
+
173
+ it("should support Uint8Array operations", () => {
174
+ const arr1 = new Uint8Array([1, 2, 3, 4, 5]);
175
+ const arr2 = new Uint8Array([6, 7, 8, 9, 10]);
176
+
177
+ const combined = new Uint8Array(arr1.length + arr2.length);
178
+ combined.set(arr1, 0);
179
+ combined.set(arr2, arr1.length);
180
+
181
+ expect(combined.length).toBe(10);
182
+ expect(combined[0]).toBe(1);
183
+ expect(combined[5]).toBe(6);
184
+ });
185
+
186
+ it("should support ArrayBuffer operations", () => {
187
+ const buffer = new ArrayBuffer(32);
188
+ const view = new Uint8Array(buffer);
189
+
190
+ view[0] = 255;
191
+ view[31] = 128;
192
+
193
+ expect(buffer.byteLength).toBe(32);
194
+ expect(view[0]).toBe(255);
195
+ expect(view[31]).toBe(128);
196
+ });
197
+
198
+ it("should support DataView for byte manipulation", () => {
199
+ const buffer = new ArrayBuffer(8);
200
+ const view = new DataView(buffer);
201
+
202
+ view.setUint32(0, 0x12345678, false); // big-endian
203
+ view.setUint32(4, 0x9abcdef0, false);
204
+
205
+ expect(view.getUint32(0, false)).toBe(0x12345678);
206
+ expect(view.getUint8(0)).toBe(0x12);
207
+ expect(view.getUint8(1)).toBe(0x34);
208
+ });
209
+
210
+ it("should support JSON parsing of complex structures", () => {
211
+ const json = '{"mediaType":"application/vnd.dev.sigstore.bundle+json;version=0.3","verificationMaterial":{"tlogEntries":[{"logIndex":"123"}]}}';
212
+ const parsed = JSON.parse(json);
213
+
214
+ expect(parsed.mediaType).toBeDefined();
215
+ expect(parsed.verificationMaterial).toBeDefined();
216
+ expect(parsed.verificationMaterial.tlogEntries).toBeInstanceOf(Array);
217
+ expect(parsed.verificationMaterial.tlogEntries[0].logIndex).toBe("123");
218
+ });
219
+ });
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export { SigstoreVerifier } from "./sigstore.js";
2
+ export type { SigstoreVerifierOptions } from "./sigstore.js";
3
+ export type { SigstoreBundle, TLogEntry, VerificationMaterial, MessageSignature, TimestampVerificationData, RFC3161Timestamp } from "./bundle.js";
4
+ export type { TrustedRoot, Sigstore, RawTimestampAuthority } from "./interfaces.js";
5
+ export {
6
+ VerificationError,
7
+ TimestampError,
8
+ CertificateError,
9
+ TLogError,
10
+ SignatureError,
11
+ PolicyError,
12
+ } from "./errors.js";
13
+ export { verifyRFC3161Timestamp, verifyBundleTimestamp } from "./timestamp/tsa.js";
14
+ export { TrustedRootProvider, type TrustedRootProviderOptions } from "./trust/tuf.js";
@@ -0,0 +1,118 @@
1
+ import { X509Certificate } from "./x509/index.js";
2
+
3
+ export enum SigstoreRoots {
4
+ certificateAuthorities = "certificateAuthorities",
5
+ ctlogs = "ctlogs",
6
+ timestampAuthorities = "timestampAuthorities",
7
+ tlogs = "tlogs",
8
+ }
9
+
10
+ export type RawTimestampAuthorities = RawTimestampAuthority[];
11
+
12
+ export interface TrustedRoot {
13
+ mediaType: string;
14
+ tlogs: RawLogs;
15
+ certificateAuthorities: RawCAs;
16
+ ctlogs: RawLogs;
17
+ timestampAuthorities: RawTimestampAuthorities;
18
+ }
19
+
20
+ export interface RawTimestampAuthority {
21
+ subject: {
22
+ organization: string;
23
+ commonName: string;
24
+ };
25
+ certChain: {
26
+ certificates: {
27
+ rawBytes: string;
28
+ }[];
29
+ };
30
+ validFor: {
31
+ start: string;
32
+ end?: string;
33
+ };
34
+ }
35
+
36
+ export interface CTLog {
37
+ logID: Uint8Array;
38
+ publicKey: CryptoKey;
39
+ validFor: {
40
+ start: Date;
41
+ end: Date;
42
+ };
43
+ }
44
+
45
+ export interface RekorKeyInfo {
46
+ publicKey: CryptoKey;
47
+ logId: Uint8Array;
48
+ }
49
+
50
+ export interface CertAuthority {
51
+ certChain: X509Certificate[];
52
+ validFor: {
53
+ start: Date;
54
+ end: Date;
55
+ };
56
+ }
57
+
58
+ export interface Sigstore {
59
+ rekor: RekorKeyInfo | undefined;
60
+ ctlogs: CTLog[];
61
+ certificateAuthorities: CertAuthority[];
62
+ timestampAuthorities: CertAuthority[];
63
+ }
64
+
65
+ export interface RawLog {
66
+ baseUrl: string;
67
+ hashAlgorithm: string;
68
+ publicKey: {
69
+ rawBytes: string;
70
+ keyDetails: string;
71
+ validFor: {
72
+ start: string;
73
+ end?: string;
74
+ };
75
+ };
76
+ logId: {
77
+ keyId: string;
78
+ };
79
+ }
80
+
81
+ export type RawLogs = RawLog[];
82
+
83
+ export interface RawCA {
84
+ subject: {
85
+ organization: string;
86
+ commonName: string;
87
+ };
88
+ uri: string;
89
+ certChain: {
90
+ certificates: {
91
+ rawBytes: string;
92
+ }[];
93
+ };
94
+ validFor: {
95
+ start: string;
96
+ end?: string;
97
+ };
98
+ }
99
+
100
+ export type RawCAs = RawCA[];
101
+
102
+ export enum KeyTypes {
103
+ Ecdsa = "ECDSA",
104
+ Ed25519 = "Ed25519",
105
+ RSA = "RSA",
106
+ }
107
+
108
+ export enum EcdsaTypes {
109
+ P256 = "P-256",
110
+ P384 = "P-384",
111
+ P521 = "P-521",
112
+ }
113
+
114
+ export enum HashAlgorithms {
115
+ SHA256 = "SHA-256",
116
+ SHA384 = "SHA-384",
117
+ SHA512 = "SHA-512",
118
+ }
package/src/oid.ts ADDED
@@ -0,0 +1,28 @@
1
+ // From https://github.com/sigstore/sigstore-js/blob/main/packages/core/src/oid.ts
2
+
3
+ export const ECDSA_SIGNATURE_ALGOS: Record<string, string> = {
4
+ "1.2.840.10045.4.3.1": "sha224",
5
+ "1.2.840.10045.4.3.2": "sha256",
6
+ "1.2.840.10045.4.3.3": "sha384",
7
+ "1.2.840.10045.4.3.4": "sha512",
8
+ };
9
+
10
+ // RSA signature algorithms
11
+ export const RSA_SIGNATURE_ALGOS: Record<string, string> = {
12
+ "1.2.840.113549.1.1.11": "sha256", // sha256WithRSAEncryption
13
+ "1.2.840.113549.1.1.12": "sha384", // sha384WithRSAEncryption
14
+ "1.2.840.113549.1.1.13": "sha512", // sha512WithRSAEncryption
15
+ "1.2.840.113549.1.1.5": "sha1", // sha1WithRSAEncryption
16
+ };
17
+
18
+ export const SHA2_HASH_ALGOS: Record<string, string> = {
19
+ "2.16.840.1.101.3.4.2.1": "sha256",
20
+ "2.16.840.1.101.3.4.2.2": "sha384",
21
+ "2.16.840.1.101.3.4.2.3": "sha512",
22
+ };
23
+
24
+ export const ECDSA_CURVE_NAMES: Record<string, string> = {
25
+ "1.2.840.10045.3.1.7": "secp256r1",
26
+ "1.3.132.0.34": "secp384r1",
27
+ "1.3.132.0.35": "secp521r1",
28
+ };
package/src/pem.ts ADDED
@@ -0,0 +1,48 @@
1
+ /*
2
+ Copyright 2023 The Sigstore Authors.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import { base64ToUint8Array, Uint8ArrayToBase64 } from "./encoding.js";
18
+
19
+ const PEM_HEADER = /-----BEGIN (.*)-----/;
20
+ const PEM_FOOTER = /-----END (.*)-----/;
21
+
22
+ export function toDER(certificate: string): Uint8Array {
23
+ let der = "";
24
+
25
+ certificate.split("\n").forEach((line) => {
26
+ if (line.match(PEM_HEADER) || line.match(PEM_FOOTER)) {
27
+ return;
28
+ }
29
+
30
+ der += line;
31
+ });
32
+
33
+ return base64ToUint8Array(der);
34
+ }
35
+
36
+ // Translates a DER-encoded buffer into a PEM-encoded string. Standard PEM
37
+ // encoding dictates that each certificate should have a trailing newline after
38
+ // the footer.
39
+ export function fromDER(certificate: Uint8Array, type = "CERTIFICATE"): string {
40
+ // Base64-encode the certificate.
41
+ const der = Uint8ArrayToBase64(certificate);
42
+ // Split the certificate into lines of 64 characters.
43
+ const lines = der.match(/.{1,64}/g) || "";
44
+
45
+ return [`-----BEGIN ${type}-----`, ...lines, `-----END ${type}-----`]
46
+ .join("\n")
47
+ .concat("\n");
48
+ }
@@ -0,0 +1,16 @@
1
+ /*
2
+ Copyright 2023 The Sigstore Authors.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+ export class RFC3161TimestampVerificationError extends Error {}
@@ -0,0 +1,17 @@
1
+ /*
2
+ Copyright 2023 The Sigstore Authors.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ export { RFC3161Timestamp } from "./timestamp.js";