@blamejs/pki 0.1.22 → 0.1.23

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.
@@ -10,8 +10,8 @@
10
10
  *
11
11
  * @intro
12
12
  * PKCS#12 (PFX) key-and-certificate store handling per RFC 7292. `parse`
13
- * decodes a `.p12` / `.pfx` container the personal-information-exchange
14
- * format OpenSSL, Windows CAPI, macOS Keychain, and NSS emit into its
13
+ * decodes a `.p12` / `.pfx` container -- the personal-information-exchange
14
+ * format OpenSSL, Windows CAPI, macOS Keychain, and NSS emit -- into its
15
15
  * bags: private keys (delegated to the PKCS#8 parser), shrouded keys
16
16
  * (algorithm surfaced, ciphertext kept opaque), certificates, CRLs,
17
17
  * secrets, and nested safe contents, each with its `friendlyName` /
@@ -26,14 +26,14 @@
26
26
  * (`macedBytes`), public-key mode the SignedData with its signers, and a
27
27
  * MAC-less store surfaces `integrityMode: "none"` for the caller's policy
28
28
  * to judge. RFC 7292
29
- * §4.1 encodes content in BER, so parsing this one format accepts exactly
30
- * two BER shapes indefinite lengths and constructed OCTET STRINGs
29
+ * sec. 4.1 encodes content in BER, so parsing this one format accepts exactly
30
+ * two BER shapes -- indefinite lengths and constructed OCTET STRINGs --
31
31
  * anywhere in the store; every other strictness verdict, and every other
32
32
  * format, stays strict DER, fail-closed.
33
33
  *
34
34
  * @card
35
35
  * Parse DER / BER / PEM RFC 7292 PKCS#12 (PFX) stores into key / cert /
36
- * CRL / secret bags with their attributes keys via the PKCS#8 parser,
36
+ * CRL / secret bags with their attributes -- keys via the PKCS#8 parser,
37
37
  * encrypted safes via CMS, MAC inputs surfaced raw for external
38
38
  * verification, fail-closed.
39
39
  */
@@ -52,26 +52,30 @@ var PemError = frameworkError.PemError;
52
52
  var NS = pkix.makeNS("pkcs12", Pkcs12Error, oid);
53
53
  var TAGS = asn1.TAGS;
54
54
 
55
- // Content types (RFC 5652 §4 / RFC 7292 §4.1).
55
+ // Content types (RFC 5652 sec. 4 / RFC 7292 sec. 4.1).
56
56
  var OID_DATA = oid.byName("data");
57
57
  var OID_SIGNED_DATA = oid.byName("signedData");
58
58
  var OID_ENVELOPED_DATA = oid.byName("envelopedData");
59
59
  var OID_ENCRYPTED_DATA = oid.byName("encryptedData");
60
- // Bag types (RFC 7292 §4.2).
60
+ // Bag types (RFC 7292 sec. 4.2).
61
61
  var OID_KEY_BAG = oid.byName("keyBag");
62
62
  var OID_SHROUDED_KEY_BAG = oid.byName("pkcs8ShroudedKeyBag");
63
63
  var OID_CERT_BAG = oid.byName("certBag");
64
64
  var OID_CRL_BAG = oid.byName("crlBag");
65
65
  var OID_SECRET_BAG = oid.byName("secretBag");
66
66
  var OID_SAFE_CONTENTS_BAG = oid.byName("safeContentsBag");
67
- // CertBag / CRLBag discriminators (§4.2.34.2.4).
67
+ // CertBag / CRLBag discriminators (sec. 4.2.3-sec. 4.2.4).
68
68
  var OID_X509_CERTIFICATE = oid.byName("x509Certificate");
69
69
  var OID_SDSI_CERTIFICATE = oid.byName("sdsiCertificate");
70
70
  var OID_X509_CRL = oid.byName("x509CRL");
71
- // Bag attributes (§4.2) + the RFC 9579 MacData arm.
71
+ // Bag attributes (sec. 4.2) + the RFC 9579 MacData arm.
72
72
  var OID_FRIENDLY_NAME = oid.byName("friendlyName");
73
73
  var OID_LOCAL_KEY_ID = oid.byName("localKeyId");
74
74
  var OID_PBMAC1 = oid.byName("pbmac1");
75
+ // The PBKDF2-params prf DEFAULT is algid-hmacWithSHA1 -- hmacWithSHA1 with NULL
76
+ // parameters (RFC 8018 sec. 5.2) -- needed for the encoded-DEFAULT rejection below.
77
+ var OID_HMAC_SHA1 = oid.byName("hmacWithSHA1");
78
+ var DER_NULL = asn1.build.nullValue();
75
79
 
76
80
  // DigestInfo ::= SEQUENCE { digestAlgorithm AlgorithmIdentifier, digest OCTET STRING }.
77
81
  var DIGEST_INFO = schema.seq([
@@ -84,9 +88,9 @@ var DIGEST_INFO = schema.seq([
84
88
  },
85
89
  });
86
90
 
87
- // PBKDF2-params (RFC 8018 §5.2), constrained to the RFC 9579 PBMAC1 profile:
91
+ // PBKDF2-params (RFC 8018 sec. 5.2), constrained to the RFC 9579 PBMAC1 profile:
88
92
  // the salt MUST use the specified (OCTET STRING) choice and keyLength MUST be
89
- // present (§4.b / §5 a MacData consumer cannot infer the MAC key size).
93
+ // present (sec. 4.b / sec. 5 -- a MacData consumer cannot infer the MAC key size).
90
94
  var PBKDF2_PARAMS = schema.seq([
91
95
  schema.field("salt", schema.octetString()),
92
96
  schema.field("iterationCount", schema.integerLeaf()),
@@ -96,9 +100,9 @@ var PBKDF2_PARAMS = schema.seq([
96
100
  assert: "sequence", code: "pkcs12/bad-mac-data", what: "PBKDF2-params",
97
101
  build: function (m, ctx) {
98
102
  if (!m.fields.keyLength.present) {
99
- throw ctx.E("pkcs12/bad-mac-data", "PBMAC1 PBKDF2-params must carry keyLength (RFC 9579 §5)");
103
+ throw ctx.E("pkcs12/bad-mac-data", "PBMAC1 PBKDF2-params must carry keyLength (RFC 9579 sec. 5)");
100
104
  }
101
- // Both counters surface as exact JS numbers a value past the bound
105
+ // Both counters surface as exact JS numbers -- a value past the bound
102
106
  // would round silently on conversion and hand a verifier wrong inputs.
103
107
  var iterations = m.fields.iterationCount.value;
104
108
  if (iterations < 1n || iterations > 2147483647n) {
@@ -109,18 +113,27 @@ var PBKDF2_PARAMS = schema.seq([
109
113
  throw ctx.E("pkcs12/bad-mac-data", "PBKDF2 keyLength must be a positive integer within the key-length range");
110
114
  }
111
115
  var prf = m.fields.prf.present ? m.fields.prf.value.result : null;
116
+ // X.690 sec. 11.5 -- a DEFAULT-valued component must be omitted from a DER
117
+ // encoding, and the prf DEFAULT is algid-hmacWithSHA1: hmacWithSHA1 with
118
+ // NULL parameters (RFC 8018 sec. 5.2). An explicit prf byte-equal to that
119
+ // default is non-canonical and rejects -- the structured-value analogue of
120
+ // the MacData iterations rule below. hmacWithSHA1 with ABSENT parameters
121
+ // is a different value from the NULL-parameters default and decodes.
122
+ if (prf && prf.oid === OID_HMAC_SHA1 && prf.parameters !== null && prf.parameters.equals(DER_NULL)) {
123
+ throw ctx.E("pkcs12/bad-mac-data", "a PBKDF2 prf equal to its DEFAULT algid-hmacWithSHA1 must be omitted (X.690 sec. 11.5, RFC 8018 sec. 5.2)");
124
+ }
112
125
  return {
113
126
  salt: m.fields.salt.value,
114
127
  iterationCount: Number(iterations),
115
128
  keyLength: Number(keyLength),
116
- prfOid: prf ? prf.oid : oid.byName("hmacWithSHA1"),
129
+ prfOid: prf ? prf.oid : OID_HMAC_SHA1,
117
130
  prfName: prf ? prf.name : "hmacWithSHA1",
118
131
  };
119
132
  },
120
133
  });
121
134
 
122
135
  // PBMAC1-params ::= SEQUENCE { keyDerivationFunc AlgorithmIdentifier{PBKDF2},
123
- // messageAuthScheme AlgorithmIdentifier } (RFC 8018 §A.5 / RFC 9579 §4).
136
+ // messageAuthScheme AlgorithmIdentifier } (RFC 8018 sec. A.5 / RFC 9579 sec. 4).
124
137
  var PBMAC1_PARAMS = schema.seq([
125
138
  schema.field("keyDerivationFunc", schema.seq([
126
139
  schema.field("algorithm", schema.oidLeaf()),
@@ -132,7 +145,7 @@ var PBMAC1_PARAMS = schema.seq([
132
145
  build: function (m, ctx) {
133
146
  var kdf = m.fields.keyDerivationFunc.value;
134
147
  if (kdf.fields.algorithm.value !== oid.byName("pbkdf2")) {
135
- throw ctx.E("pkcs12/bad-mac-data", "PBMAC1 keyDerivationFunc must be PBKDF2 (RFC 9579 §4)");
148
+ throw ctx.E("pkcs12/bad-mac-data", "PBMAC1 keyDerivationFunc must be PBKDF2 (RFC 9579 sec. 4)");
136
149
  }
137
150
  var scheme = m.fields.messageAuthScheme.value.result;
138
151
  return {
@@ -152,23 +165,23 @@ var MAC_DATA = schema.seq([
152
165
  ], {
153
166
  assert: "sequence", code: "pkcs12/bad-mac-data", what: "MacData",
154
167
  build: function (m, ctx) {
155
- // X.690 §11.5 a DEFAULT-valued component must be omitted from a DER
168
+ // X.690 sec. 11.5 -- a DEFAULT-valued component must be omitted from a DER
156
169
  // encoding, so an explicitly-encoded iterations = 1 is non-canonical.
157
170
  var it = m.fields.iterations;
158
171
  var iterations = 1;
159
172
  if (it.present) {
160
173
  var v = it.value;
161
- if (v === 1n) throw ctx.E("pkcs12/bad-mac-iterations", "iterations equal to its DEFAULT 1 must be omitted (X.690 §11.5)");
174
+ if (v === 1n) throw ctx.E("pkcs12/bad-mac-iterations", "iterations equal to its DEFAULT 1 must be omitted (X.690 sec. 11.5)");
162
175
  if (v < 1n || v > 2147483647n) throw ctx.E("pkcs12/bad-mac-iterations", "iterations must be a positive integer within the iteration-count range");
163
176
  iterations = Number(v);
164
177
  }
165
178
  var di = m.fields.mac.value.result;
166
179
  var pbmac1 = null;
167
180
  if (di.digestAlgorithm.oid === OID_PBMAC1) {
168
- // RFC 9579 §4 the PBMAC1 algorithm identifier MUST carry PBMAC1-params
181
+ // RFC 9579 sec. 4 -- the PBMAC1 algorithm identifier MUST carry PBMAC1-params
169
182
  // (the KDF and MAC scheme a verifier needs live there, not in MacData).
170
183
  if (di.digestAlgorithm.parameters === null) {
171
- throw ctx.E("pkcs12/bad-mac-data", "a PBMAC1 MacData must carry PBMAC1-params (RFC 9579 §4)");
184
+ throw ctx.E("pkcs12/bad-mac-data", "a PBMAC1 MacData must carry PBMAC1-params (RFC 9579 sec. 4)");
172
185
  }
173
186
  pbmac1 = schema.embeddedDer(PBMAC1_PARAMS, di.digestAlgorithm.parameters, ctx,
174
187
  { code: "pkcs12/bad-mac-data", what: "PBMAC1-params", ber: true }).result;
@@ -186,7 +199,7 @@ var MAC_DATA = schema.seq([
186
199
  },
187
200
  });
188
201
 
189
- // CertBag ::= SEQUENCE { certId OID, certValue [0] EXPLICIT ANY } (§4.2.3):
202
+ // CertBag ::= SEQUENCE { certId OID, certValue [0] EXPLICIT ANY } (sec. 4.2.3):
190
203
  // x509Certificate wraps the DER certificate in an OCTET STRING; sdsiCertificate
191
204
  // is an IA5String. Both arms of the closed set are accepted; the value is
192
205
  // surfaced raw (byte-exact), never re-encoded or recursively parsed.
@@ -203,15 +216,15 @@ var CERT_BAG = schema.seq([
203
216
  }
204
217
  if (certId === OID_SDSI_CERTIFICATE) {
205
218
  if (!(node.tagClass === "universal" && node.tagNumber === TAGS.IA5_STRING && node.content)) {
206
- throw ctx.E("pkcs12/bad-bag-value", "an sdsiCertificate certValue must be an IA5String (RFC 7292 §4.2.3)");
219
+ throw ctx.E("pkcs12/bad-bag-value", "an sdsiCertificate certValue must be an IA5String (RFC 7292 sec. 4.2.3)");
207
220
  }
208
221
  return { certType: "sdsiCertificate", certId: certId, certValue: node.content };
209
222
  }
210
- throw ctx.E("pkcs12/bad-cert-type", (ctx.oid.name(certId) || certId) + " is not a recognized CertBag certId (RFC 7292 §4.2.3)");
223
+ throw ctx.E("pkcs12/bad-cert-type", (ctx.oid.name(certId) || certId) + " is not a recognized CertBag certId (RFC 7292 sec. 4.2.3)");
211
224
  },
212
225
  });
213
226
 
214
- // CRLBag ::= SEQUENCE { crlId OID, crlValue [0] EXPLICIT ANY } (§4.2.4).
227
+ // CRLBag ::= SEQUENCE { crlId OID, crlValue [0] EXPLICIT ANY } (sec. 4.2.4).
215
228
  var CRL_BAG = schema.seq([
216
229
  schema.field("crlId", schema.oidLeaf()),
217
230
  schema.field("crlValue", schema.explicit(0, schema.any(), { code: "pkcs12/bad-bag-value", what: "crlValue" })),
@@ -220,15 +233,15 @@ var CRL_BAG = schema.seq([
220
233
  build: function (m, ctx) {
221
234
  var crlId = m.fields.crlId.value;
222
235
  if (crlId !== OID_X509_CRL) {
223
- throw ctx.E("pkcs12/bad-crl-type", (ctx.oid.name(crlId) || crlId) + " is not a recognized CRLBag crlId (RFC 7292 §4.2.4)");
236
+ throw ctx.E("pkcs12/bad-crl-type", (ctx.oid.name(crlId) || crlId) + " is not a recognized CRLBag crlId (RFC 7292 sec. 4.2.4)");
224
237
  }
225
238
  return { crlType: "x509CRL", crlId: crlId, crlValue: _octetContent(m.fields.crlValue.value, ctx, "an x509CRL crlValue") };
226
239
  },
227
240
  });
228
241
 
229
242
  // SecretBag ::= SEQUENCE { secretTypeId OID, secretValue [0] EXPLICIT ANY }
230
- // (§4.2.5). SecretTypes is an open set, so the type is surfaced by OID with
231
- // the value raw an unrecognized secret is representable, not a fault.
243
+ // (sec. 4.2.5). SecretTypes is an open set, so the type is surfaced by OID with
244
+ // the value raw -- an unrecognized secret is representable, not a fault.
232
245
  var SECRET_BAG = schema.seq([
233
246
  schema.field("secretTypeId", schema.oidLeaf()),
234
247
  schema.field("secretValue", schema.explicit(0, schema.any(), { code: "pkcs12/bad-bag-value", what: "secretValue" })),
@@ -241,7 +254,7 @@ var SECRET_BAG = schema.seq([
241
254
  });
242
255
 
243
256
  // SafeBag ::= SEQUENCE { bagId OID, bagValue [0] EXPLICIT ANY DEFINED BY bagId,
244
- // bagAttributes SET OF PKCS12Attribute OPTIONAL } (§4.2).
257
+ // bagAttributes SET OF PKCS12Attribute OPTIONAL } (sec. 4.2).
245
258
  // The build surfaces the structural record (bagId + the inner value node +
246
259
  // decoded attributes); the bagId dispatch happens in _buildBag, where the
247
260
  // per-parse recursion state lives.
@@ -289,14 +302,14 @@ var SAFE_BAG = schema.seq([
289
302
  },
290
303
  });
291
304
 
292
- // SafeContents ::= SEQUENCE OF SafeBag (§4.2). Strictly a SEQUENCE a SET OF
305
+ // SafeContents ::= SEQUENCE OF SafeBag (sec. 4.2). Strictly a SEQUENCE -- a SET OF
293
306
  // here is a known producer divergence and rejects.
294
307
  var SAFE_CONTENTS = schema.seqOf(SAFE_BAG, {
295
308
  code: "pkcs12/bad-safe-contents", what: "SafeContents",
296
309
  max: C.LIMITS.PKCS12_MAX_ELEMENTS, maxCode: "pkcs12/too-many-elements",
297
310
  });
298
311
 
299
- // AuthenticatedSafe ::= SEQUENCE OF ContentInfo (§4.1) each element parsed
312
+ // AuthenticatedSafe ::= SEQUENCE OF ContentInfo (sec. 4.1) -- each element parsed
300
313
  // structurally here and dispatched by contentType in _dispatchSafes.
301
314
  var AS_ELEMENT = schema.seq([
302
315
  schema.field("contentType", schema.oidLeaf()),
@@ -312,7 +325,7 @@ var AUTHENTICATED_SAFE = schema.seqOf(AS_ELEMENT, {
312
325
  max: C.LIMITS.PKCS12_MAX_ELEMENTS, maxCode: "pkcs12/too-many-elements",
313
326
  });
314
327
 
315
- // The authSafe ContentInfo (§4): contentType is data (password integrity) or
328
+ // The authSafe ContentInfo (sec. 4): contentType is data (password integrity) or
316
329
  // signedData (public-key integrity) and nothing else.
317
330
  var AUTH_SAFE = schema.seq([
318
331
  schema.field("contentType", schema.oidLeaf()),
@@ -325,7 +338,7 @@ var AUTH_SAFE = schema.seq([
325
338
  });
326
339
 
327
340
  // PFX ::= SEQUENCE { version INTEGER {v3(3)}, authSafe ContentInfo,
328
- // macData MacData OPTIONAL } (§4).
341
+ // macData MacData OPTIONAL } (sec. 4).
329
342
  var PFX = schema.seq([
330
343
  schema.field("version", pkix.versionReader(NS, { "3": 3 })),
331
344
  schema.field("authSafe", AUTH_SAFE),
@@ -342,14 +355,14 @@ var PFX = schema.seq([
342
355
  var state = { budget: { remaining: C.LIMITS.PKCS12_MAX_REDECODES }, bagDepth: 0 };
343
356
 
344
357
  if (authSafe.contentType === OID_DATA) {
345
- // §5.1 step 5B the MAC covers the value octets of the id-data OCTET
358
+ // sec. 5.1 step 5B -- the MAC covers the value octets of the id-data OCTET
346
359
  // STRING (the AuthenticatedSafe encoding), excluding the TLV header.
347
360
  // MacData itself is OPTIONAL in the PFX syntax: a store without it
348
361
  // (OpenSSL `pkcs12 -export -nomac`) carries no integrity protection at
349
- // all, which is a policy concern for the caller surfaced as
362
+ // all, which is a policy concern for the caller -- surfaced as
350
363
  // integrityMode "none", never rejected by a parser that surfaces
351
364
  // integrity inputs rather than verifying them.
352
- var macedBytes = _octetContent(authSafe.innerNode, ctx, "an id-data authSafe content");
365
+ var macedBytes = _octetContent(authSafe.innerNode, ctx, "an id-data authSafe content", "pkcs12/bad-authsafe");
353
366
  var dispatched = _dispatchSafes(_walkAuthenticatedSafe(macedBytes, state, ctx), state, ctx);
354
367
  return {
355
368
  version: version, integrityMode: macPresent ? "password" : "none", mac: mac, macedBytes: macedBytes,
@@ -357,19 +370,19 @@ var PFX = schema.seq([
357
370
  };
358
371
  }
359
372
  if (authSafe.contentType === OID_SIGNED_DATA) {
360
- // §4 integrity coherence: public-key integrity has no MacData.
361
- if (macPresent) throw ctx.E("pkcs12/bad-integrity-mode", "an id-signedData authSafe (public-key integrity) must omit macData (RFC 7292 §4)");
373
+ // sec. 4 integrity coherence: public-key integrity has no MacData.
374
+ if (macPresent) throw ctx.E("pkcs12/bad-integrity-mode", "an id-signedData authSafe (public-key integrity) must omit macData (RFC 7292 sec. 4)");
362
375
  var signed = cms.walkSignedData(authSafe.innerNode);
363
376
  if (signed.encapContentInfo.eContentType !== OID_DATA) {
364
- throw ctx.E("pkcs12/bad-authsafe", "a public-key-integrity authSafe must encapsulate id-data carrying the AuthenticatedSafe (RFC 7292 §4.1)");
377
+ throw ctx.E("pkcs12/bad-authsafe", "a public-key-integrity authSafe must encapsulate id-data carrying the AuthenticatedSafe (RFC 7292 sec. 4.1)");
365
378
  }
366
379
  if (signed.encapContentInfo.eContent === null) {
367
- throw ctx.E("pkcs12/bad-authsafe", "a public-key-integrity authSafe must carry attached eContent (RFC 7292 §4.1)");
380
+ throw ctx.E("pkcs12/bad-authsafe", "a public-key-integrity authSafe must carry attached eContent (RFC 7292 sec. 4.1)");
368
381
  }
369
- // A signer-less SignedData carries no integrity at all the one thing
382
+ // A signer-less SignedData carries no integrity at all -- the one thing
370
383
  // public-key-integrity mode exists to provide.
371
384
  if (signed.signerInfos.length < 1) {
372
- throw ctx.E("pkcs12/bad-authsafe", "a public-key-integrity authSafe must carry at least one SignerInfo (RFC 7292 §4)");
385
+ throw ctx.E("pkcs12/bad-authsafe", "a public-key-integrity authSafe must carry at least one SignerInfo (RFC 7292 sec. 4)");
373
386
  }
374
387
  var dispatchedSigned = _dispatchSafes(_walkAuthenticatedSafe(signed.encapContentInfo.eContent, state, ctx), state, ctx);
375
388
  return {
@@ -377,7 +390,7 @@ var PFX = schema.seq([
377
390
  authSafeSigned: signed, safeBags: dispatchedSigned.safeBags, encryptedSafes: dispatchedSigned.encryptedSafes,
378
391
  };
379
392
  }
380
- throw ctx.E("pkcs12/bad-authsafe-type", "authSafe contentType must be id-data or id-signedData (RFC 7292 §4), got " +
393
+ throw ctx.E("pkcs12/bad-authsafe-type", "authSafe contentType must be id-data or id-signedData (RFC 7292 sec. 4), got " +
381
394
  (ctx.oid.name(authSafe.contentType) || authSafe.contentType));
382
395
  },
383
396
  });
@@ -391,19 +404,19 @@ function _octetContent(node, ctx, what, code) {
391
404
  return node.content;
392
405
  }
393
406
 
394
- // RFC 7292 §4.1 a privacy-mode safe wraps a SafeContents, so the encrypted
407
+ // RFC 7292 sec. 4.1 -- a privacy-mode safe wraps a SafeContents, so the encrypted
395
408
  // content's declared type must be id-data whichever CMS privacy structure
396
409
  // carries it, and the ciphertext must be attached: CMS permits a detached
397
- // EncryptedContentInfo, but here the ciphertext IS the safe's contents a
410
+ // EncryptedContentInfo, but here the ciphertext IS the safe's contents -- a
398
411
  // safe without it holds nothing a passphrase could ever recover.
399
412
  function _privacySafe(content, ctx) {
400
413
  if (content.encryptedContentInfo.contentType !== OID_DATA) {
401
414
  throw ctx.E("pkcs12/bad-safe-contentinfo-type",
402
- "an encrypted safe must declare id-data (SafeContents) as its encrypted content type (RFC 7292 §4.1)");
415
+ "an encrypted safe must declare id-data (SafeContents) as its encrypted content type (RFC 7292 sec. 4.1)");
403
416
  }
404
- if (content.encryptedContentInfo.encryptedContent === null) {
417
+ if (content.encryptedContentInfo.encryptedContent === null || content.encryptedContentInfo.encryptedContent.length === 0) {
405
418
  throw ctx.E("pkcs12/bad-safe-contentinfo",
406
- "an encrypted safe must carry attached ciphertext its encryptedContent is the SafeContents (RFC 7292 §4.1)");
419
+ "an encrypted safe must carry non-empty attached ciphertext -- its encryptedContent is the SafeContents (RFC 7292 sec. 4.1)");
407
420
  }
408
421
  return content;
409
422
  }
@@ -413,7 +426,7 @@ function _privacySafe(content, ctx) {
413
426
  function _bmpValue(bytes, ctx) {
414
427
  var node = asn1.decode(bytes, { ber: true });
415
428
  if (!(node.tagClass === "universal" && node.tagNumber === TAGS.BMP_STRING)) {
416
- throw ctx.E("pkcs12/bad-friendly-name", "a friendlyName value must be a BMPString (RFC 7292 §4.2)");
429
+ throw ctx.E("pkcs12/bad-friendly-name", "a friendlyName value must be a BMPString (RFC 7292 sec. 4.2)");
417
430
  }
418
431
  return asn1.read.string(node);
419
432
  }
@@ -426,17 +439,17 @@ function _walkAuthenticatedSafe(bytes, state, ctx) {
426
439
  });
427
440
  }
428
441
 
429
- // Dispatch each AuthenticatedSafe element by contentType (§4.1): id-data
442
+ // Dispatch each AuthenticatedSafe element by contentType (sec. 4.1): id-data
430
443
  // carries plaintext SafeContents (re-decoded here); the two privacy modes are
431
444
  // full CMS structures validated by the CMS module on the already-decoded node
432
- // ciphertext stays opaque inside the CMS surface.
445
+ // -- ciphertext stays opaque inside the CMS surface.
433
446
  function _dispatchSafes(asMatch, state, ctx) {
434
447
  var safeBags = [];
435
448
  var encryptedSafes = [];
436
449
  for (var i = 0; i < asMatch.items.length; i++) {
437
450
  var el = asMatch.items[i].value.result;
438
451
  if (el.contentType === OID_DATA) {
439
- var contents = schema.embeddedDer(SAFE_CONTENTS, _octetContent(el.innerNode, ctx, "an id-data safe content"), ctx, {
452
+ var contents = schema.embeddedDer(SAFE_CONTENTS, _octetContent(el.innerNode, ctx, "an id-data safe content", "pkcs12/bad-safe-contentinfo"), ctx, {
440
453
  code: "pkcs12/bad-der", what: "a SafeContents", ber: true,
441
454
  budget: state.budget, budgetCode: "pkcs12/too-deep",
442
455
  });
@@ -448,14 +461,14 @@ function _dispatchSafes(asMatch, state, ctx) {
448
461
  } else if (el.contentType === OID_ENVELOPED_DATA) {
449
462
  encryptedSafes.push({ type: "envelopedData", content: _privacySafe(cms.walkEnvelopedData(el.innerNode), ctx) });
450
463
  } else {
451
- throw ctx.E("pkcs12/bad-safe-contentinfo-type", "an AuthenticatedSafe element must be id-data, id-encryptedData, or id-envelopedData (RFC 7292 §4.1), got " +
464
+ throw ctx.E("pkcs12/bad-safe-contentinfo-type", "an AuthenticatedSafe element must be id-data, id-encryptedData, or id-envelopedData (RFC 7292 sec. 4.1), got " +
452
465
  (ctx.oid.name(el.contentType) || el.contentType));
453
466
  }
454
467
  }
455
468
  return { safeBags: safeBags, encryptedSafes: encryptedSafes };
456
469
  }
457
470
 
458
- // bagId dispatch (§4.2, Appendix D). The bag-type set is closed: an
471
+ // bagId dispatch (sec. 4.2, Appendix D). The bag-type set is closed: an
459
472
  // unrecognized bagId rejects. Key material delegates to the exported PKCS#8
460
473
  // parsers; cert / CRL / secret values are surfaced raw.
461
474
  function _buildBag(rec, state, ctx) {
@@ -463,7 +476,7 @@ function _buildBag(rec, state, ctx) {
463
476
  type: null, bagId: rec.bagId,
464
477
  friendlyName: rec.friendlyName, localKeyId: rec.localKeyId, attributes: rec.attributes,
465
478
  };
466
- // Key bags are walked from the DECODED node their wire bytes may carry
479
+ // Key bags are walked from the DECODED node -- their wire bytes may carry
467
480
  // BER shapes the strict pkcs8 parse entry refuses, and the node already
468
481
  // exists (no re-decode).
469
482
  if (rec.bagId === OID_KEY_BAG) {
@@ -495,7 +508,7 @@ function _buildBag(rec, state, ctx) {
495
508
  return bag;
496
509
  }
497
510
  if (rec.bagId === OID_SAFE_CONTENTS_BAG) {
498
- // §4.2.6 recursion bounded independently of the codec depth cap so a
511
+ // sec. 4.2.6 recursion -- bounded independently of the codec depth cap so a
499
512
  // chain across re-decode boundaries cannot restart it.
500
513
  if (state.bagDepth + 1 > C.LIMITS.PKCS12_MAX_BAG_DEPTH) {
501
514
  throw ctx.E("pkcs12/too-deep", "safeContentsBag nesting exceeds the depth cap " + C.LIMITS.PKCS12_MAX_BAG_DEPTH);
@@ -509,7 +522,7 @@ function _buildBag(rec, state, ctx) {
509
522
  }
510
523
  return bag;
511
524
  }
512
- throw ctx.E("pkcs12/bad-bag-type", (ctx.oid.name(rec.bagId) || rec.bagId) + " is not a recognized SafeBag bagId (RFC 7292 §4.2)");
525
+ throw ctx.E("pkcs12/bad-bag-type", (ctx.oid.name(rec.bagId) || rec.bagId) + " is not a recognized SafeBag bagId (RFC 7292 sec. 4.2)");
513
526
  }
514
527
 
515
528
  /**
@@ -524,29 +537,32 @@ function _buildBag(rec, state, ctx) {
524
537
  * Parse a DER / BER `Buffer` or a PEM string into a structured PFX:
525
538
  * `{ version, integrityMode, mac, macedBytes, authSafeSigned, safeBags,
526
539
  * encryptedSafes }`. `integrityMode` is `"password"` (id-data authSafe with
527
- * MacData `mac` carries `{ kind, hashOid, hashName, hashParameters,
540
+ * MacData -- `mac` carries `{ kind, hashOid, hashName, hashParameters,
528
541
  * pbmac1, macValue, macSalt, iterations }`, where `kind` distinguishes the
529
542
  * RFC 9579 PBMAC1 arm; for PBMAC1 the required parameters are validated and
530
543
  * decoded onto `pbmac1` (`{ kdf: { salt, iterationCount, keyLength, prfOid,
531
544
  * prfName }, schemeOid, schemeName }`) with `hashParameters` keeping the raw
532
545
  * bytes, and `macedBytes` is the exact byte range the HMAC covers),
533
- * `"none"` (id-data authSafe without MacData the shape
546
+ * `"none"` (id-data authSafe without MacData -- the shape
534
547
  * `openssl pkcs12 -export -nomac` emits; `mac` is `null` and the store
535
548
  * carries no integrity protection, a policy decision left to the caller),
536
- * or `"public-key"` (id-signedData authSafe the CMS SignedData surfaced
549
+ * or `"public-key"` (id-signedData authSafe -- the CMS SignedData surfaced
537
550
  * on `authSafeSigned`, signature not verified here). Each of `safeBags` is
538
551
  * `{ type, bagId, friendlyName, localKeyId, attributes }` plus its arm:
539
552
  * a `keyBag` carries `key` (the PKCS#8 parse), a `pkcs8ShroudedKeyBag`
540
553
  * carries `encrypted` (algorithm surfaced, ciphertext opaque), `certBag` /
541
554
  * `crlBag` / `secretBag` carry their values raw and byte-exact, and a
542
555
  * `safeContentsBag` carries `nested` bags. Encrypted / enveloped safes are
543
- * surfaced structurally on `encryptedSafes` via the CMS module recipient
556
+ * surfaced structurally on `encryptedSafes` via the CMS module -- recipient
544
557
  * infos and algorithms decoded, ciphertext never parsed. MAC verification
545
558
  * and bag decryption are passphrase operations for an external layer; the
546
559
  * inputs are surfaced exactly.
547
560
  *
548
561
  * Throws `Pkcs12Error` when the bytes are not a well-formed PFX, and
549
- * `Asn1Error` when the underlying encoding is malformed.
562
+ * `Asn1Error` when the underlying encoding is malformed. Faults inside a
563
+ * delegated CMS structure (a signedData authSafe, an encrypted / enveloped
564
+ * safe) surface as `CmsError` (`cms/*`) and inside a key bag as `Pkcs8Error`
565
+ * (`pkcs8/*`) -- all `PkiError` subclasses.
550
566
  *
551
567
  * @example
552
568
  * var store = pki.schema.pkcs12.parse(der);
@@ -566,7 +582,7 @@ var parse = pkix.makeParser({
566
582
  * @related pki.schema.pkcs12.parse
567
583
  *
568
584
  * Extract the DER bytes from a PEM block (default label `PKCS12`). A
569
- * `.p12` / `.pfx` file is almost always binary the PEM path is a
585
+ * `.p12` / `.pfx` file is almost always binary -- the PEM path is a
570
586
  * convenience for stores that transit text channels.
571
587
  *
572
588
  * @example
@@ -590,8 +606,8 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label || "PKCS12",
590
606
  function pemEncode(der, label) { return pkix.pemEncode(der, label || "PKCS12", PemError); }
591
607
 
592
608
  // A PFX root leads with a universal INTEGER (version), colliding with the
593
- // PKCS#8 detector on the first child so the discriminators are children[1]
594
- // (a ContentInfo: SEQUENCE of exactly 2, OID first, [0] constructed second
609
+ // PKCS#8 detector on the first child -- so the discriminators are children[1]
610
+ // (a ContentInfo: SEQUENCE of exactly 2, OID first, [0] constructed second --
595
611
  // a shape a PrivateKeyInfo's AlgorithmIdentifier never presents) and
596
612
  // children[2] (a SEQUENCE MacData or absent, never PKCS#8's OCTET STRING).
597
613
  function matches(root) {
@@ -9,25 +9,25 @@
9
9
  * @slug pkcs8
10
10
  *
11
11
  * @intro
12
- * PKCS#8 private-key handling per RFC 5208 §5 (PrivateKeyInfo) and RFC 5958 §2
12
+ * PKCS#8 private-key handling per RFC 5208 sec. 5 (PrivateKeyInfo) and RFC 5958 sec. 2
13
13
  * (OneAsymmetricKey). `parse` turns a DER or PEM (`PRIVATE KEY`) key into a
14
14
  * structured object: version, the private-key algorithm identifier, the raw
15
- * private-key bytes, the optional attributes, and for a v2 OneAsymmetricKey
15
+ * private-key bytes, the optional attributes, and -- for a v2 OneAsymmetricKey --
16
16
  * the optional public key. It composes the same schema engine and shared PKIX
17
17
  * sub-schemas (AlgorithmIdentifier, Attribute) the other parsers use.
18
18
  *
19
19
  * A PKCS#8 key is a container, not a signed structure: it has no signature, no
20
20
  * distinguished name, and no to-be-signed region. The private-key OCTET STRING
21
- * content is kept raw the algorithm-specific inner key (an RSAPrivateKey, an
21
+ * content is kept raw -- the algorithm-specific inner key (an RSAPrivateKey, an
22
22
  * ECPrivateKey, a CurvePrivateKey) is decoded by the caller using the surfaced
23
23
  * algorithm OID, so an unknown or future key type never fails the parse. An
24
- * `ENCRYPTED PRIVATE KEY` (EncryptedPrivateKeyInfo, RFC 5958 §3) is recognized
24
+ * `ENCRYPTED PRIVATE KEY` (EncryptedPrivateKeyInfo, RFC 5958 sec. 3) is recognized
25
25
  * and surfaced with its encryption algorithm and raw ciphertext; decrypting it
26
26
  * needs a passphrase and is out of scope for structural parsing.
27
27
  *
28
28
  * @card
29
29
  * Parse DER / PEM PKCS#8 private keys (RFC 5208 / 5958) into structured,
30
- * validated fields algorithm, raw key bytes, attributes, optional public key,
30
+ * validated fields -- algorithm, raw key bytes, attributes, optional public key,
31
31
  * fail-closed. Encrypted keys are recognized, not decrypted.
32
32
  */
33
33
 
@@ -46,7 +46,7 @@ var NS = pkix.makeNS("pkcs8", Pkcs8Error, oid);
46
46
  var ALGORITHM_IDENTIFIER = pkix.algorithmIdentifier(NS);
47
47
  var ATTRIBUTE = pkix.attribute(NS);
48
48
 
49
- // version ::= INTEGER { v1(0), v2(1) } 0 and 1 are both LEGAL (the divergence
49
+ // version ::= INTEGER { v1(0), v2(1) } -- 0 and 1 are both LEGAL (the divergence
50
50
  // from every other reader; cert/CRL reject 0, CSR accepts only 0). Surface as the
51
51
  // RFC's vN number.
52
52
  var PKCS8_VERSION = pkix.versionReader(NS, { "0": 1, "1": 2 });
@@ -69,7 +69,7 @@ var PRIVATE_KEY_INFO = schema.seq([
69
69
  build: function (m) {
70
70
  var version = m.fields.version.value; // 1 (v1) | 2 (v2)
71
71
  var hasPublicKey = m.fields.publicKey.present;
72
- // RFC 5958 §2 publicKey present <=> version is v2. Enforce both directions.
72
+ // RFC 5958 sec. 2 -- publicKey present <=> version is v2. Enforce both directions.
73
73
  if (hasPublicKey && version !== 2) throw NS.E("pkcs8/bad-version", "a [1] publicKey is permitted only in a v2 OneAsymmetricKey");
74
74
  if (!hasPublicKey && version === 2) throw NS.E("pkcs8/bad-version", "a v2 OneAsymmetricKey must carry a [1] publicKey");
75
75
  return {
@@ -83,7 +83,7 @@ var PRIVATE_KEY_INFO = schema.seq([
83
83
  });
84
84
 
85
85
  // EncryptedPrivateKeyInfo ::= SEQUENCE { encryptionAlgorithm AlgorithmIdentifier,
86
- // encryptedData OCTET STRING } (RFC 5958 §3). Recognized and surfaced; the raw
86
+ // encryptedData OCTET STRING } (RFC 5958 sec. 3). Recognized and surfaced; the raw
87
87
  // ciphertext is kept for a later decryption layer (PBES2/PBKDF2 + a passphrase).
88
88
  var ENCRYPTED_PRIVATE_KEY_INFO = schema.seq([
89
89
  schema.field("encryptionAlgorithm", ALGORITHM_IDENTIFIER),
@@ -172,7 +172,7 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label || "PRIVATE
172
172
  function pemEncode(der, label) { return pkix.pemEncode(der, label || "PRIVATE KEY", PemError); }
173
173
 
174
174
  // A PKCS#8 PrivateKeyInfo root is a SEQUENCE whose first child is a universal
175
- // INTEGER (version) and third child a universal OCTET STRING (privateKey) a
175
+ // INTEGER (version) and third child a universal OCTET STRING (privateKey) -- a
176
176
  // shape the signed-envelope trio (whose children[0] is a SEQUENCE and children[2]
177
177
  // a BIT STRING) can never present, so the detectors are mutually exclusive
178
178
  // regardless of registry order. Trailing context fields are [0]/[1] in ascending
@@ -193,16 +193,16 @@ function matches(root) {
193
193
  }
194
194
 
195
195
  // NOTE: there is intentionally no `matchesEncrypted` detector. An
196
- // EncryptedPrivateKeyInfo is a SEQUENCE { SEQUENCE, OCTET STRING } a shape it
196
+ // EncryptedPrivateKeyInfo is a SEQUENCE { SEQUENCE, OCTET STRING } -- a shape it
197
197
  // shares with a PKCS#1 DigestInfo and other AlgorithmIdentifier-plus-octets
198
- // structures so it cannot be classified from structure alone. The orchestrator
198
+ // structures -- so it cannot be classified from structure alone. The orchestrator
199
199
  // does not auto-route it; an operator who knows a key is encrypted (e.g. from an
200
200
  // 'ENCRYPTED PRIVATE KEY' PEM label) calls parseEncrypted directly. A structural
201
201
  // auto-route can return once a validated encryption-algorithm discriminator (the
202
202
  // PBES layer) exists.
203
203
 
204
204
  // Validate a bare, already-decoded PrivateKeyInfo / EncryptedPrivateKeyInfo
205
- // node for a composer that holds a decoded node and must not re-parse its
205
+ // node -- for a composer that holds a decoded node and must not re-parse its
206
206
  // bytes (an RFC 7292 PFX key bag, whose wire encoding may be BER that the
207
207
  // strict `parse` entry would refuse). Same contract as the CMS walkers: the
208
208
  // node is the bare structure, typed pkcs8/* on rejection.