@noz-ele/edgca 0.1.0 → 0.3.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/bytes.d.ts CHANGED
@@ -6,4 +6,5 @@ export declare function bytesToBinary(bytes: Uint8Array): string;
6
6
  export declare function binaryToBytes(binary: string): Uint8Array;
7
7
  export declare function cloneBytes(bytes: Uint8Array): Uint8Array;
8
8
  export declare function arrayBufferFromBytes(bytes: Uint8Array): ArrayBuffer;
9
+ export declare function randomBytes(length: number): Uint8Array;
9
10
  //# sourceMappingURL=bytes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../src/bytes.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,UAAU,CAWpE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAWhE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAEnD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAYpD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAUvD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAQxD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAExD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAInE"}
1
+ {"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../src/bytes.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,UAAU,CAWpE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAWhE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAEnD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAYpD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAUvD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAQxD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAExD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAInE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAItD"}
package/dist/bytes.js CHANGED
@@ -56,3 +56,8 @@ export function arrayBufferFromBytes(bytes) {
56
56
  copy.set(bytes);
57
57
  return copy.buffer;
58
58
  }
59
+ export function randomBytes(length) {
60
+ const bytes = new Uint8Array(length);
61
+ crypto.getRandomValues(bytes);
62
+ return bytes;
63
+ }
package/dist/ca.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import type { CertificateAuthority, CreateRootCAOptions, ImportCertificateAuthorityOptions, IssueClientCertOptions, IssueIntermediateCAOptions, IssuedClientCertificate } from "./types.js";
1
+ import type { CertificateAuthority, CreateRootCAOptions, ImportCertificateAuthorityOptions, IssueClientCertForPublicKeyOptions, IssueClientCertOptions, IssueIntermediateCAOptions, IssuedClientCertificate, IssuedClientCertificateForPublicKey } from "./types.js";
2
2
  export declare function createRootCA(options: CreateRootCAOptions): Promise<CertificateAuthority>;
3
3
  export declare function issueIntermediateCA(options: IssueIntermediateCAOptions): Promise<CertificateAuthority>;
4
4
  export declare function issueClientCert(options: IssueClientCertOptions): Promise<IssuedClientCertificate>;
5
+ export declare function issueClientCertForPublicKey(options: IssueClientCertForPublicKeyOptions): Promise<IssuedClientCertificateForPublicKey>;
5
6
  export declare function importCertificateAuthority(options: ImportCertificateAuthorityOptions): Promise<CertificateAuthority>;
6
7
  //# sourceMappingURL=ca.d.ts.map
package/dist/ca.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ca.d.ts","sourceRoot":"","sources":["../src/ca.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,iCAAiC,EACjC,sBAAsB,EACtB,0BAA0B,EAC1B,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAapB,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAwB9F;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA8B5G;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAyCvG;AAED,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAkB1H"}
1
+ {"version":3,"file":"ca.d.ts","sourceRoot":"","sources":["../src/ca.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,iCAAiC,EACjC,kCAAkC,EAClC,sBAAsB,EACtB,0BAA0B,EAC1B,uBAAuB,EACvB,mCAAmC,EAEpC,MAAM,YAAY,CAAC;AAapB,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA0B9F;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAgC5G;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAqBvG;AAED,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,kCAAkC,GAC1C,OAAO,CAAC,mCAAmC,CAAC,CAkB9C;AAuDD,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAe1H"}
package/dist/ca.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import { bytesEqual, cloneBytes } from "./bytes.js";
2
- import { assertKeyPairMatches, exportSpki, generateKeyPair, importPrivateKeyPem, keyIdentifierFromSpki, keyPairFromPrivateKeyPem, privateKeyToPem, publicKeyToPem, signDer } from "./crypto.js";
2
+ import { assertKeyPairMatches, curveOf, exportSpki, generateKeyPair, keyIdentifierFromSpki, signDer } from "./crypto.js";
3
3
  import { encodeName } from "./name.js";
4
4
  import { certificateToPem, pemToDer, pemToDerWithLabel, splitPemBlocks } from "./pem.js";
5
5
  import { parseCertificateDer } from "./parser.js";
6
6
  import { authorityKeyIdentifierExtension, basicConstraintsCaExtension, basicConstraintsLeafExtension, buildCertificate, buildTbsCertificate, extendedKeyUsageClientAuthExtension, keyUsageExtension, subjectAltNameExtension, subjectKeyIdentifierExtension } from "./x509.js";
7
7
  export async function createRootCA(options) {
8
- const keyPair = await resolveKeyPair(options.privateKeyPem);
8
+ const keyPair = await resolveKeyPair(options.keyPair);
9
+ const issuerCurve = curveOf(keyPair.privateKey);
9
10
  const subjectNameDer = encodeName(options.subject);
10
11
  const spki = await exportSpki(keyPair.publicKey);
11
12
  const keyIdentifier = await keyIdentifierFromSpki(spki);
@@ -23,10 +24,11 @@ export async function createRootCA(options) {
23
24
  issuerNameDer: subjectNameDer,
24
25
  subjectNameDer,
25
26
  subjectPublicKeyInfoDer: spki,
26
- extensions
27
+ extensions,
28
+ issuerCurve
27
29
  });
28
30
  const signatureDer = await signDer(keyPair.privateKey, tbsCertificateDer);
29
- const certDer = buildCertificate(tbsCertificateDer, signatureDer);
31
+ const certDer = buildCertificate(tbsCertificateDer, signatureDer, issuerCurve);
30
32
  return assembleCertificateAuthority(certDer, keyPair, "");
31
33
  }
32
34
  export async function issueIntermediateCA(options) {
@@ -34,7 +36,8 @@ export async function issueIntermediateCA(options) {
34
36
  const issuerChainPem = options.ca.issuerChainPem;
35
37
  assertCanIssueCertificate(issuer);
36
38
  assertCanIssueIntermediate(issuer, issuerChainPem, options.pathLenConstraint);
37
- const keyPair = await resolveKeyPair(options.privateKeyPem);
39
+ const issuerCurve = curveOf(options.ca.privateKey);
40
+ const keyPair = await resolveKeyPair(options.keyPair);
38
41
  const subjectNameDer = encodeName(options.subject);
39
42
  const spki = await exportSpki(keyPair.publicKey);
40
43
  const subjectKeyIdentifier = await keyIdentifierFromSpki(spki);
@@ -52,23 +55,57 @@ export async function issueIntermediateCA(options) {
52
55
  issuerNameDer: issuer.subjectNameDer,
53
56
  subjectNameDer,
54
57
  subjectPublicKeyInfoDer: spki,
55
- extensions
58
+ extensions,
59
+ issuerCurve
56
60
  });
57
61
  const signatureDer = await signDer(options.ca.privateKey, tbsCertificateDer);
58
- const certDer = buildCertificate(tbsCertificateDer, signatureDer);
62
+ const certDer = buildCertificate(tbsCertificateDer, signatureDer, issuerCurve);
59
63
  const childChainPem = joinPemChain([options.ca.certPem, issuerChainPem]);
60
64
  return assembleCertificateAuthority(certDer, keyPair, childChainPem);
61
65
  }
62
66
  export async function issueClientCert(options) {
63
- const issuer = await parseIssuer(options.ca);
64
- const issuerChainPem = options.ca.issuerChainPem;
65
- assertCanIssueCertificate(issuer);
66
67
  const keyPair = await generateKeyPair();
67
- const subjectNameDer = encodeName(options.subject);
68
- const spki = await exportSpki(keyPair.publicKey);
68
+ const built = await buildClientCertificate(options.ca, keyPair.publicKey, {
69
+ subject: options.subject,
70
+ days: options.days,
71
+ notBefore: options.notBefore,
72
+ serialNumber: options.serialNumber,
73
+ dnsNames: options.dnsNames,
74
+ ipAddresses: options.ipAddresses
75
+ });
76
+ return {
77
+ certPem: built.certPem,
78
+ certDer: built.certDer,
79
+ privateKey: keyPair.privateKey,
80
+ publicKey: keyPair.publicKey,
81
+ certChainPem: built.certChainPem
82
+ };
83
+ }
84
+ export async function issueClientCertForPublicKey(options) {
85
+ const built = await buildClientCertificate(options.ca, options.publicKey, {
86
+ subject: options.subject,
87
+ days: options.days,
88
+ notBefore: options.notBefore,
89
+ serialNumber: options.serialNumber,
90
+ dnsNames: options.dnsNames,
91
+ ipAddresses: options.ipAddresses
92
+ });
93
+ return {
94
+ certPem: built.certPem,
95
+ certDer: built.certDer,
96
+ certChainPem: built.certChainPem
97
+ };
98
+ }
99
+ async function buildClientCertificate(ca, subjectPublicKey, content) {
100
+ const issuer = await parseIssuer(ca);
101
+ const issuerChainPem = ca.issuerChainPem;
102
+ assertCanIssueCertificate(issuer);
103
+ const issuerCurve = curveOf(ca.privateKey);
104
+ const subjectNameDer = encodeName(content.subject);
105
+ const spki = await exportSpki(subjectPublicKey);
69
106
  const subjectKeyIdentifier = await keyIdentifierFromSpki(spki);
70
107
  const authorityKeyIdentifier = issuer.subjectKeyIdentifier ?? await keyIdentifierFromSpki(issuer.subjectPublicKeyInfoDer);
71
- const san = subjectAltNameExtension(options.dnsNames, options.ipAddresses);
108
+ const san = subjectAltNameExtension(content.dnsNames, content.ipAddresses);
72
109
  const extensions = [
73
110
  basicConstraintsLeafExtension(),
74
111
  keyUsageExtension(["digitalSignature"]),
@@ -78,25 +115,22 @@ export async function issueClientCert(options) {
78
115
  ...(san ? [san] : [])
79
116
  ];
80
117
  const { tbsCertificateDer } = buildTbsCertificate({
81
- serialNumber: options.serialNumber,
82
- notBefore: options.notBefore,
83
- days: options.days,
118
+ serialNumber: content.serialNumber,
119
+ notBefore: content.notBefore,
120
+ days: content.days,
84
121
  issuerNameDer: issuer.subjectNameDer,
85
122
  subjectNameDer,
86
123
  subjectPublicKeyInfoDer: spki,
87
- extensions
124
+ extensions,
125
+ issuerCurve
88
126
  });
89
- const signatureDer = await signDer(options.ca.privateKey, tbsCertificateDer);
90
- const certDer = buildCertificate(tbsCertificateDer, signatureDer);
127
+ const signatureDer = await signDer(ca.privateKey, tbsCertificateDer);
128
+ const certDer = buildCertificate(tbsCertificateDer, signatureDer, issuerCurve);
91
129
  const certPem = certificateToPem(certDer);
92
130
  return {
93
131
  certPem,
94
- privateKeyPem: await privateKeyToPem(keyPair.privateKey),
95
- publicKeyPem: await publicKeyToPem(keyPair.publicKey),
96
132
  certDer: cloneBytes(certDer),
97
- privateKey: keyPair.privateKey,
98
- publicKey: keyPair.publicKey,
99
- certChainPem: joinPemChain([certPem, options.ca.certPem, issuerChainPem])
133
+ certChainPem: joinPemChain([certPem, ca.certPem, issuerChainPem])
100
134
  };
101
135
  }
102
136
  export async function importCertificateAuthority(options) {
@@ -104,14 +138,11 @@ export async function importCertificateAuthority(options) {
104
138
  assertIssuerChainPem(issuerChainPem);
105
139
  const certDer = pemToDerWithLabel(options.certPem, "CERTIFICATE");
106
140
  const parsed = await parseCertificateDer(certDer);
107
- const privateKey = await importPrivateKeyPem(options.privateKeyPem);
108
- await assertKeyPairMatches(privateKey, parsed.publicKey);
141
+ await assertKeyPairMatches(options.privateKey, parsed.publicKey);
109
142
  return {
110
143
  certPem: options.certPem,
111
- privateKeyPem: options.privateKeyPem,
112
- publicKeyPem: await publicKeyToPem(parsed.publicKey),
113
144
  certDer: cloneBytes(certDer),
114
- privateKey,
145
+ privateKey: options.privateKey,
115
146
  publicKey: parsed.publicKey,
116
147
  issuerChainPem
117
148
  };
@@ -128,17 +159,15 @@ function assertIssuerChainPem(chainPem) {
128
159
  pemToDerWithLabel(block, "CERTIFICATE");
129
160
  }
130
161
  }
131
- async function resolveKeyPair(privateKeyPem) {
132
- if (privateKeyPem !== undefined) {
133
- return keyPairFromPrivateKeyPem(privateKeyPem);
162
+ async function resolveKeyPair(provided) {
163
+ if (provided !== undefined) {
164
+ return provided;
134
165
  }
135
166
  return generateKeyPair();
136
167
  }
137
168
  async function assembleCertificateAuthority(certDer, keyPair, issuerChainPem) {
138
169
  return {
139
170
  certPem: certificateToPem(certDer),
140
- privateKeyPem: await privateKeyToPem(keyPair.privateKey),
141
- publicKeyPem: await publicKeyToPem(keyPair.publicKey),
142
171
  certDer: cloneBytes(certDer),
143
172
  privateKey: keyPair.privateKey,
144
173
  publicKey: keyPair.publicKey,
package/dist/crypto.d.ts CHANGED
@@ -1,16 +1,16 @@
1
- export declare function generateKeyPair(): Promise<CryptoKeyPair>;
1
+ export type SupportedCurve = "P-256" | "P-384" | "P-521";
2
+ export declare function curveOf(key: CryptoKey): SupportedCurve;
3
+ export declare function componentSizeForCurve(curve: SupportedCurve): number;
4
+ export declare function signatureAlgorithmOidForCurve(curve: SupportedCurve): string;
5
+ export declare function generateKeyPair(curve?: SupportedCurve): Promise<CryptoKeyPair>;
2
6
  export declare function signDer(privateKey: CryptoKey, data: Uint8Array): Promise<Uint8Array>;
3
7
  export declare function verifyDer(publicKey: CryptoKey, signatureDer: Uint8Array, data: Uint8Array): Promise<boolean>;
4
8
  export declare function digestSha256(data: Uint8Array): Promise<Uint8Array>;
5
9
  export declare function digestSha1(data: Uint8Array): Promise<Uint8Array>;
6
10
  export declare function keyIdentifierFromSpki(spki: Uint8Array): Promise<Uint8Array>;
7
- export declare function privateKeyToPem(key: CryptoKey): Promise<string>;
8
- export declare function publicKeyToPem(key: CryptoKey): Promise<string>;
9
11
  export declare function exportSpki(key: CryptoKey): Promise<Uint8Array>;
10
- export declare function importPrivateKeyPem(pem: string): Promise<CryptoKey>;
11
- export declare function keyPairFromPrivateKeyPem(pem: string): Promise<CryptoKeyPair>;
12
12
  export declare function importPublicKeySpki(spki: Uint8Array): Promise<CryptoKey>;
13
13
  export declare function assertKeyPairMatches(privateKey: CryptoKey, publicKey: CryptoKey): Promise<void>;
14
- export declare function ecdsaRawToDer(raw: Uint8Array): Uint8Array;
15
- export declare function ecdsaDerToRaw(signature: Uint8Array): Uint8Array;
14
+ export declare function ecdsaRawToDer(raw: Uint8Array, componentSize: number): Uint8Array;
15
+ export declare function ecdsaDerToRaw(signature: Uint8Array, componentSize: number): Uint8Array;
16
16
  //# sourceMappingURL=crypto.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAcA,wBAAsB,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC,CAE9D;AAED,wBAAsB,OAAO,CAAC,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAG1F;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAOlH;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAExE;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAEtE;AAID,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAWjF;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAGrE;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpE;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAEpE;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAEzE;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAOlF;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAE9E;AAED,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAQrG;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,CAMzD;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,UAAU,GAAG,UAAU,CAY/D"}
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAkCzD,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,cAAc,CAUtD;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAEnE;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAE3E;AAED,wBAAsB,eAAe,CAAC,KAAK,GAAE,cAAwB,GAAG,OAAO,CAAC,aAAa,CAAC,CAE7F;AAED,wBAAsB,OAAO,CAAC,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAO1F;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CASlH;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAExE;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAEtE;AAID,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAWjF;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAEpE;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAS9E;AAkCD,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAQrG;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,UAAU,CAMhF;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,UAAU,CAetF"}
package/dist/crypto.js CHANGED
@@ -1,23 +1,61 @@
1
1
  import { arrayBufferFromBytes, concatBytes } from "./bytes.js";
2
- import { integer, readChildren, readElement, readSequenceChildren, sequence, TAG } from "./der.js";
3
- import { pemToDerWithLabel, privateKeyDerToPem, publicKeyDerToPem } from "./pem.js";
4
- const EC_ALGORITHM = {
5
- name: "ECDSA",
6
- namedCurve: "P-256"
2
+ import { decodeOid, integer, readChildren, readElement, readSequenceChildren, sequence, TAG } from "./der.js";
3
+ import { OID } from "./oids.js";
4
+ const CURVE_PROFILE = {
5
+ "P-256": {
6
+ componentSize: 32,
7
+ hash: "SHA-256",
8
+ signatureAlgorithmOid: OID.ecdsaWithSha256,
9
+ curveOid: OID.secp256r1
10
+ },
11
+ "P-384": {
12
+ componentSize: 48,
13
+ hash: "SHA-384",
14
+ signatureAlgorithmOid: OID.ecdsaWithSha384,
15
+ curveOid: OID.secp384r1
16
+ },
17
+ "P-521": {
18
+ componentSize: 66,
19
+ hash: "SHA-512",
20
+ signatureAlgorithmOid: OID.ecdsaWithSha512,
21
+ curveOid: OID.secp521r1
22
+ }
7
23
  };
8
- const ECDSA_SIGN_ALGORITHM = {
9
- name: "ECDSA",
10
- hash: "SHA-256"
24
+ const CURVE_OID_TO_NAME = {
25
+ [OID.secp256r1]: "P-256",
26
+ [OID.secp384r1]: "P-384",
27
+ [OID.secp521r1]: "P-521"
11
28
  };
12
- export async function generateKeyPair() {
13
- return crypto.subtle.generateKey(EC_ALGORITHM, true, ["sign", "verify"]);
29
+ export function curveOf(key) {
30
+ const algorithm = key.algorithm;
31
+ if (algorithm.name !== "ECDSA") {
32
+ throw new Error(`Expected ECDSA key, got ${algorithm.name}`);
33
+ }
34
+ const curve = algorithm.namedCurve;
35
+ if (curve !== "P-256" && curve !== "P-384" && curve !== "P-521") {
36
+ throw new Error(`Unsupported ECDSA curve: ${curve}`);
37
+ }
38
+ return curve;
39
+ }
40
+ export function componentSizeForCurve(curve) {
41
+ return CURVE_PROFILE[curve].componentSize;
42
+ }
43
+ export function signatureAlgorithmOidForCurve(curve) {
44
+ return CURVE_PROFILE[curve].signatureAlgorithmOid;
45
+ }
46
+ export async function generateKeyPair(curve = "P-256") {
47
+ return crypto.subtle.generateKey({ name: "ECDSA", namedCurve: curve }, true, ["sign", "verify"]);
14
48
  }
15
49
  export async function signDer(privateKey, data) {
16
- const raw = new Uint8Array(await crypto.subtle.sign(ECDSA_SIGN_ALGORITHM, privateKey, arrayBufferFromBytes(data)));
17
- return ecdsaRawToDer(raw);
50
+ const curve = curveOf(privateKey);
51
+ const profile = CURVE_PROFILE[curve];
52
+ const raw = new Uint8Array(await crypto.subtle.sign({ name: "ECDSA", hash: profile.hash }, privateKey, arrayBufferFromBytes(data)));
53
+ return ecdsaRawToDer(raw, profile.componentSize);
18
54
  }
19
55
  export async function verifyDer(publicKey, signatureDer, data) {
20
- return crypto.subtle.verify(ECDSA_SIGN_ALGORITHM, publicKey, arrayBufferFromBytes(ecdsaDerToRaw(signatureDer)), arrayBufferFromBytes(data));
56
+ const curve = curveOf(publicKey);
57
+ const profile = CURVE_PROFILE[curve];
58
+ return crypto.subtle.verify({ name: "ECDSA", hash: profile.hash }, publicKey, arrayBufferFromBytes(ecdsaDerToRaw(signatureDer, profile.componentSize)), arrayBufferFromBytes(data));
21
59
  }
22
60
  export async function digestSha256(data) {
23
61
  return new Uint8Array(await crypto.subtle.digest("SHA-256", arrayBufferFromBytes(data)));
@@ -39,30 +77,43 @@ export async function keyIdentifierFromSpki(spki) {
39
77
  }
40
78
  return digestSha1(subjectPublicKey.value.subarray(1));
41
79
  }
42
- export async function privateKeyToPem(key) {
43
- const der = new Uint8Array(await crypto.subtle.exportKey("pkcs8", key));
44
- return privateKeyDerToPem(der);
45
- }
46
- export async function publicKeyToPem(key) {
47
- const der = new Uint8Array(await crypto.subtle.exportKey("spki", key));
48
- return publicKeyDerToPem(der);
49
- }
50
80
  export async function exportSpki(key) {
51
81
  return new Uint8Array(await crypto.subtle.exportKey("spki", key));
52
82
  }
53
- export async function importPrivateKeyPem(pem) {
54
- return crypto.subtle.importKey("pkcs8", arrayBufferFromBytes(pemToDerWithLabel(pem, "PRIVATE KEY")), EC_ALGORITHM, true, ["sign"]);
55
- }
56
- export async function keyPairFromPrivateKeyPem(pem) {
57
- const privateKey = await importPrivateKeyPem(pem);
58
- const jwk = await crypto.subtle.exportKey("jwk", privateKey);
59
- delete jwk.d;
60
- jwk.key_ops = ["verify"];
61
- const publicKey = await crypto.subtle.importKey("jwk", jwk, EC_ALGORITHM, true, ["verify"]);
62
- return { privateKey, publicKey };
63
- }
64
83
  export async function importPublicKeySpki(spki) {
65
- return crypto.subtle.importKey("spki", arrayBufferFromBytes(spki), EC_ALGORITHM, true, ["verify"]);
84
+ const curve = curveFromSpki(spki);
85
+ return crypto.subtle.importKey("spki", arrayBufferFromBytes(spki), { name: "ECDSA", namedCurve: curve }, true, ["verify"]);
86
+ }
87
+ // SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }
88
+ // AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY }
89
+ // For EC keys, parameters is the named-curve OID.
90
+ function curveFromSpki(spki) {
91
+ const root = readElement(spki);
92
+ if (root.tag !== TAG.SEQUENCE) {
93
+ throw new Error("Invalid SubjectPublicKeyInfo");
94
+ }
95
+ const algorithm = readSequenceChildren(root)[0];
96
+ if (!algorithm || algorithm.tag !== TAG.SEQUENCE) {
97
+ throw new Error("Invalid SubjectPublicKeyInfo algorithm");
98
+ }
99
+ const algorithmChildren = readSequenceChildren(algorithm);
100
+ const algorithmOidElement = algorithmChildren[0];
101
+ const parametersElement = algorithmChildren[1];
102
+ if (!algorithmOidElement || algorithmOidElement.tag !== TAG.OBJECT_IDENTIFIER) {
103
+ throw new Error("Invalid SubjectPublicKeyInfo algorithm OID");
104
+ }
105
+ if (decodeOid(algorithmOidElement.value) !== OID.ecPublicKey) {
106
+ throw new Error("SubjectPublicKeyInfo is not an EC public key");
107
+ }
108
+ if (!parametersElement || parametersElement.tag !== TAG.OBJECT_IDENTIFIER) {
109
+ throw new Error("EC SubjectPublicKeyInfo parameters must be a named-curve OID");
110
+ }
111
+ const curveOid = decodeOid(parametersElement.value);
112
+ const curve = CURVE_OID_TO_NAME[curveOid];
113
+ if (!curve) {
114
+ throw new Error(`Unsupported EC named curve OID: ${curveOid}`);
115
+ }
116
+ return curve;
66
117
  }
67
118
  export async function assertKeyPairMatches(privateKey, publicKey) {
68
119
  const data = new TextEncoder().encode("edgca-key-pair-check");
@@ -72,13 +123,13 @@ export async function assertKeyPairMatches(privateKey, publicKey) {
72
123
  throw new Error("Private key does not match CA certificate public key");
73
124
  }
74
125
  }
75
- export function ecdsaRawToDer(raw) {
76
- if (raw.length !== 64) {
77
- throw new Error("P-256 ECDSA raw signature must be 64 bytes");
126
+ export function ecdsaRawToDer(raw, componentSize) {
127
+ if (raw.length !== componentSize * 2) {
128
+ throw new Error(`ECDSA raw signature must be ${componentSize * 2} bytes`);
78
129
  }
79
- return sequence(integer(raw.subarray(0, 32)), integer(raw.subarray(32)));
130
+ return sequence(integer(raw.subarray(0, componentSize)), integer(raw.subarray(componentSize)));
80
131
  }
81
- export function ecdsaDerToRaw(signature) {
132
+ export function ecdsaDerToRaw(signature, componentSize) {
82
133
  const root = readElement(signature);
83
134
  if (root.tag !== TAG.SEQUENCE || root.end !== signature.length) {
84
135
  throw new Error("Invalid DER ECDSA signature");
@@ -87,18 +138,21 @@ export function ecdsaDerToRaw(signature) {
87
138
  if (!r || !s || r.tag !== TAG.INTEGER || s.tag !== TAG.INTEGER) {
88
139
  throw new Error("Invalid DER ECDSA signature integers");
89
140
  }
90
- return concatBytes([integerToFixedWidth(r.value), integerToFixedWidth(s.value)]);
141
+ return concatBytes([
142
+ integerToFixedWidth(r.value, componentSize),
143
+ integerToFixedWidth(s.value, componentSize)
144
+ ]);
91
145
  }
92
- function integerToFixedWidth(value) {
146
+ function integerToFixedWidth(value, size) {
93
147
  let start = 0;
94
148
  while (start < value.length - 1 && value[start] === 0) {
95
149
  start += 1;
96
150
  }
97
151
  const trimmed = value.subarray(start);
98
- if (trimmed.length > 32) {
99
- throw new Error("ECDSA integer is wider than P-256");
152
+ if (trimmed.length > size) {
153
+ throw new Error(`ECDSA integer is wider than ${size} bytes`);
100
154
  }
101
- const out = new Uint8Array(32);
102
- out.set(trimmed, 32 - trimmed.length);
155
+ const out = new Uint8Array(size);
156
+ out.set(trimmed, size - trimmed.length);
103
157
  return out;
104
158
  }
package/dist/csr.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ import type { Subject } from "./types.js";
2
+ export interface CertificateSigningRequestExtension {
3
+ oid: string;
4
+ critical: boolean;
5
+ valueDer: Uint8Array;
6
+ }
7
+ export interface CertificateSigningRequestAttribute {
8
+ oid: string;
9
+ valuesDer: ReadonlyArray<Uint8Array>;
10
+ }
11
+ export interface ParsedCertificateSigningRequest {
12
+ subject: Subject;
13
+ publicKey: CryptoKey;
14
+ subjectPublicKeyInfoDer: Uint8Array;
15
+ requestedDnsNames: readonly string[];
16
+ requestedIpAddresses: readonly string[];
17
+ requestedExtensions: readonly CertificateSigningRequestExtension[];
18
+ otherAttributes: readonly CertificateSigningRequestAttribute[];
19
+ signatureAlgorithmOid: string;
20
+ signatureDer: Uint8Array;
21
+ certificationRequestInfoDer: Uint8Array;
22
+ }
23
+ export declare function parseCertificateSigningRequest(input: string | Uint8Array): Promise<ParsedCertificateSigningRequest>;
24
+ export declare function verifyCertificateSigningRequestSignature(csr: ParsedCertificateSigningRequest): Promise<boolean>;
25
+ //# sourceMappingURL=csr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csr.d.ts","sourceRoot":"","sources":["../src/csr.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAwB,MAAM,YAAY,CAAC;AAkBhE,MAAM,WAAW,kCAAkC;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,UAAU,CAAC;CACtB;AAED,MAAM,WAAW,kCAAkC;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,uBAAuB,EAAE,UAAU,CAAC;IACpC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,mBAAmB,EAAE,SAAS,kCAAkC,EAAE,CAAC;IACnE,eAAe,EAAE,SAAS,kCAAkC,EAAE,CAAC;IAC/D,qBAAqB,EAAE,MAAM,CAAC;IAC9B,YAAY,EAAE,UAAU,CAAC;IACzB,2BAA2B,EAAE,UAAU,CAAC;CACzC;AAED,wBAAsB,8BAA8B,CAClD,KAAK,EAAE,MAAM,GAAG,UAAU,GACzB,OAAO,CAAC,+BAA+B,CAAC,CAwE1C;AAED,wBAAsB,wCAAwC,CAC5D,GAAG,EAAE,+BAA+B,GACnC,OAAO,CAAC,OAAO,CAAC,CAElB"}