@simplewebauthn/server 13.3.0 → 13.3.2
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/esm/helpers/validateCertificatePath.d.ts.map +1 -1
- package/esm/helpers/validateCertificatePath.js +32 -38
- package/esm/registration/verifications/verifyAttestationAndroidSafetyNet.d.ts.map +1 -1
- package/esm/registration/verifications/verifyAttestationAndroidSafetyNet.js +1 -0
- package/esm/registration/verifications/verifyAttestationPacked.d.ts.map +1 -1
- package/esm/registration/verifications/verifyAttestationPacked.js +1 -0
- package/package.json +1 -1
- package/script/helpers/validateCertificatePath.d.ts.map +1 -1
- package/script/helpers/validateCertificatePath.js +31 -37
- package/script/registration/verifications/verifyAttestationAndroidSafetyNet.d.ts.map +1 -1
- package/script/registration/verifications/verifyAttestationAndroidSafetyNet.js +1 -0
- package/script/registration/verifications/verifyAttestationPacked.d.ts.map +1 -1
- package/script/registration/verifications/verifyAttestationPacked.js +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateCertificatePath.d.ts","sourceRoot":"","sources":["../../src/helpers/validateCertificatePath.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validateCertificatePath.d.ts","sourceRoot":"","sources":["../../src/helpers/validateCertificatePath.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EAAE,EACrB,eAAe,GAAE,MAAM,EAAO,GAC7B,OAAO,CAAC,OAAO,CAAC,CAiHlB"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { X509Certificate } from '@peculiar/x509';
|
|
1
|
+
import { X509Certificate, X509ChainBuilder } from '@peculiar/x509';
|
|
2
2
|
import { isCertRevoked } from './isCertRevoked.js';
|
|
3
|
-
import { getWebCrypto } from './iso/isoCrypto/getWebCrypto.js';
|
|
4
3
|
/**
|
|
5
4
|
* Traverse an array of PEM certificates and ensure they form a proper chain
|
|
6
5
|
* @param x5cCertsPEM Typically the result of `x5c.map(convertASN1toPEM)`
|
|
@@ -11,7 +10,6 @@ export async function validateCertificatePath(x5cCertsPEM, trustAnchorsPEM = [])
|
|
|
11
10
|
// We have no trust anchors to chain back to, so skip path validation
|
|
12
11
|
return true;
|
|
13
12
|
}
|
|
14
|
-
const WebCrypto = await getWebCrypto();
|
|
15
13
|
// Prepare to work with x5c certs
|
|
16
14
|
const x5cCertsParsed = x5cCertsPEM.map((certPEM) => new X509Certificate(certPEM));
|
|
17
15
|
// Check for any expired or temporally invalid certs in x5c
|
|
@@ -64,50 +62,47 @@ export async function validateCertificatePath(x5cCertsPEM, trustAnchorsPEM = [])
|
|
|
64
62
|
if (validTrustAnchors.length === 0) {
|
|
65
63
|
throw new Error('No specified trust anchor was valid for verifying x5c');
|
|
66
64
|
}
|
|
67
|
-
// Try to verify x5c with each trust anchor
|
|
68
|
-
let
|
|
69
|
-
for (const anchor of
|
|
65
|
+
// Try to verify x5c with each valid trust anchor
|
|
66
|
+
let invalidCertificateChain = true;
|
|
67
|
+
for (const anchor of validTrustAnchors) {
|
|
70
68
|
try {
|
|
71
69
|
const x5cWithTrustAnchor = x5cCertsParsed.concat([anchor]);
|
|
72
|
-
|
|
70
|
+
const numUniqueCerts = new Set(x5cWithTrustAnchor.map((cert) => cert.toString('pem'))).size;
|
|
71
|
+
if (numUniqueCerts !== x5cWithTrustAnchor.length) {
|
|
73
72
|
throw new Error('Invalid certificate path: found duplicate certificates');
|
|
74
73
|
}
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
74
|
+
// Break apart x5c to try and build a valid cert chain
|
|
75
|
+
const x5cLeafCert = x5cCertsParsed[0];
|
|
76
|
+
let x5cIntermediates = [];
|
|
77
|
+
if (x5cCertsParsed.length > 1) {
|
|
78
|
+
x5cIntermediates = x5cCertsParsed.slice(1);
|
|
79
|
+
}
|
|
80
|
+
// Order of certs doesn't matter here but for readability
|
|
81
|
+
const chainBuilder = new X509ChainBuilder({ certificates: [...x5cIntermediates, anchor] });
|
|
82
|
+
// Cert chain should be, from index 0: leaf cert -> ...intermediates -> trust anchor
|
|
83
|
+
const chain = await chainBuilder.build(x5cLeafCert);
|
|
84
|
+
// Check if the chain contains (all of the certs in x5c) + (the trust anchor)
|
|
85
|
+
if (chain.length < numUniqueCerts) {
|
|
86
|
+
// Invalid cert chain, try the next trust anchor
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
// Check if the chain is to the trust anchor
|
|
90
|
+
if (chain[chain.length - 1].subject !== anchor.subject) {
|
|
91
|
+
// Invalid cert chain, try the next trust anchor
|
|
92
|
+
continue;
|
|
93
93
|
}
|
|
94
94
|
// If we successfully validated a path then there's no need to continue. Reset any existing
|
|
95
95
|
// errors that were thrown by earlier trust anchors
|
|
96
|
-
|
|
96
|
+
invalidCertificateChain = false;
|
|
97
97
|
break;
|
|
98
98
|
}
|
|
99
99
|
catch (err) {
|
|
100
|
-
|
|
101
|
-
invalidSubjectAndIssuerError = true;
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
throw new Error('Unexpected error while validating certificate path', { cause: err });
|
|
105
|
-
}
|
|
100
|
+
throw new Error('Unexpected error while validating certificate path', { cause: err });
|
|
106
101
|
}
|
|
107
102
|
}
|
|
108
103
|
// We tried multiple trust anchors and none of them worked
|
|
109
|
-
if (
|
|
110
|
-
throw new
|
|
104
|
+
if (invalidCertificateChain) {
|
|
105
|
+
throw new InvalidCertificatePath();
|
|
111
106
|
}
|
|
112
107
|
return true;
|
|
113
108
|
}
|
|
@@ -130,11 +125,10 @@ function assertCertIsWithinValidTimeWindow(certNotBefore, certNotAfter) {
|
|
|
130
125
|
throw new Error('Certificate is not yet valid or expired');
|
|
131
126
|
}
|
|
132
127
|
}
|
|
133
|
-
|
|
134
|
-
class InvalidSubjectAndIssuer extends Error {
|
|
128
|
+
class InvalidCertificatePath extends Error {
|
|
135
129
|
constructor() {
|
|
136
|
-
const message = '
|
|
130
|
+
const message = 'x5c could not be chained to any specified trust anchor';
|
|
137
131
|
super(message);
|
|
138
|
-
this.name = '
|
|
132
|
+
this.name = 'InvalidX5CChain';
|
|
139
133
|
}
|
|
140
134
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifyAttestationAndroidSafetyNet.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationAndroidSafetyNet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAWtF;;GAEG;AACH,wBAAsB,iCAAiC,CACrD,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"verifyAttestationAndroidSafetyNet.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationAndroidSafetyNet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAWtF;;GAEG;AACH,wBAAsB,iCAAiC,CACrD,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CA6IlB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifyAttestationPacked.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationPacked.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAYtF;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"verifyAttestationPacked.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationPacked.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAYtF;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CA0JlB"}
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateCertificatePath.d.ts","sourceRoot":"","sources":["../../src/helpers/validateCertificatePath.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validateCertificatePath.d.ts","sourceRoot":"","sources":["../../src/helpers/validateCertificatePath.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EAAE,EACrB,eAAe,GAAE,MAAM,EAAO,GAC7B,OAAO,CAAC,OAAO,CAAC,CAiHlB"}
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.validateCertificatePath = validateCertificatePath;
|
|
4
4
|
const x509_1 = require("@peculiar/x509");
|
|
5
5
|
const isCertRevoked_js_1 = require("./isCertRevoked.js");
|
|
6
|
-
const getWebCrypto_js_1 = require("./iso/isoCrypto/getWebCrypto.js");
|
|
7
6
|
/**
|
|
8
7
|
* Traverse an array of PEM certificates and ensure they form a proper chain
|
|
9
8
|
* @param x5cCertsPEM Typically the result of `x5c.map(convertASN1toPEM)`
|
|
@@ -14,7 +13,6 @@ async function validateCertificatePath(x5cCertsPEM, trustAnchorsPEM = []) {
|
|
|
14
13
|
// We have no trust anchors to chain back to, so skip path validation
|
|
15
14
|
return true;
|
|
16
15
|
}
|
|
17
|
-
const WebCrypto = await (0, getWebCrypto_js_1.getWebCrypto)();
|
|
18
16
|
// Prepare to work with x5c certs
|
|
19
17
|
const x5cCertsParsed = x5cCertsPEM.map((certPEM) => new x509_1.X509Certificate(certPEM));
|
|
20
18
|
// Check for any expired or temporally invalid certs in x5c
|
|
@@ -67,50 +65,47 @@ async function validateCertificatePath(x5cCertsPEM, trustAnchorsPEM = []) {
|
|
|
67
65
|
if (validTrustAnchors.length === 0) {
|
|
68
66
|
throw new Error('No specified trust anchor was valid for verifying x5c');
|
|
69
67
|
}
|
|
70
|
-
// Try to verify x5c with each trust anchor
|
|
71
|
-
let
|
|
72
|
-
for (const anchor of
|
|
68
|
+
// Try to verify x5c with each valid trust anchor
|
|
69
|
+
let invalidCertificateChain = true;
|
|
70
|
+
for (const anchor of validTrustAnchors) {
|
|
73
71
|
try {
|
|
74
72
|
const x5cWithTrustAnchor = x5cCertsParsed.concat([anchor]);
|
|
75
|
-
|
|
73
|
+
const numUniqueCerts = new Set(x5cWithTrustAnchor.map((cert) => cert.toString('pem'))).size;
|
|
74
|
+
if (numUniqueCerts !== x5cWithTrustAnchor.length) {
|
|
76
75
|
throw new Error('Invalid certificate path: found duplicate certificates');
|
|
77
76
|
}
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
77
|
+
// Break apart x5c to try and build a valid cert chain
|
|
78
|
+
const x5cLeafCert = x5cCertsParsed[0];
|
|
79
|
+
let x5cIntermediates = [];
|
|
80
|
+
if (x5cCertsParsed.length > 1) {
|
|
81
|
+
x5cIntermediates = x5cCertsParsed.slice(1);
|
|
82
|
+
}
|
|
83
|
+
// Order of certs doesn't matter here but for readability
|
|
84
|
+
const chainBuilder = new x509_1.X509ChainBuilder({ certificates: [...x5cIntermediates, anchor] });
|
|
85
|
+
// Cert chain should be, from index 0: leaf cert -> ...intermediates -> trust anchor
|
|
86
|
+
const chain = await chainBuilder.build(x5cLeafCert);
|
|
87
|
+
// Check if the chain contains (all of the certs in x5c) + (the trust anchor)
|
|
88
|
+
if (chain.length < numUniqueCerts) {
|
|
89
|
+
// Invalid cert chain, try the next trust anchor
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
// Check if the chain is to the trust anchor
|
|
93
|
+
if (chain[chain.length - 1].subject !== anchor.subject) {
|
|
94
|
+
// Invalid cert chain, try the next trust anchor
|
|
95
|
+
continue;
|
|
96
96
|
}
|
|
97
97
|
// If we successfully validated a path then there's no need to continue. Reset any existing
|
|
98
98
|
// errors that were thrown by earlier trust anchors
|
|
99
|
-
|
|
99
|
+
invalidCertificateChain = false;
|
|
100
100
|
break;
|
|
101
101
|
}
|
|
102
102
|
catch (err) {
|
|
103
|
-
|
|
104
|
-
invalidSubjectAndIssuerError = true;
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
throw new Error('Unexpected error while validating certificate path', { cause: err });
|
|
108
|
-
}
|
|
103
|
+
throw new Error('Unexpected error while validating certificate path', { cause: err });
|
|
109
104
|
}
|
|
110
105
|
}
|
|
111
106
|
// We tried multiple trust anchors and none of them worked
|
|
112
|
-
if (
|
|
113
|
-
throw new
|
|
107
|
+
if (invalidCertificateChain) {
|
|
108
|
+
throw new InvalidCertificatePath();
|
|
114
109
|
}
|
|
115
110
|
return true;
|
|
116
111
|
}
|
|
@@ -133,11 +128,10 @@ function assertCertIsWithinValidTimeWindow(certNotBefore, certNotAfter) {
|
|
|
133
128
|
throw new Error('Certificate is not yet valid or expired');
|
|
134
129
|
}
|
|
135
130
|
}
|
|
136
|
-
|
|
137
|
-
class InvalidSubjectAndIssuer extends Error {
|
|
131
|
+
class InvalidCertificatePath extends Error {
|
|
138
132
|
constructor() {
|
|
139
|
-
const message = '
|
|
133
|
+
const message = 'x5c could not be chained to any specified trust anchor';
|
|
140
134
|
super(message);
|
|
141
|
-
this.name = '
|
|
135
|
+
this.name = 'InvalidX5CChain';
|
|
142
136
|
}
|
|
143
137
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifyAttestationAndroidSafetyNet.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationAndroidSafetyNet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAWtF;;GAEG;AACH,wBAAsB,iCAAiC,CACrD,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"verifyAttestationAndroidSafetyNet.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationAndroidSafetyNet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAWtF;;GAEG;AACH,wBAAsB,iCAAiC,CACrD,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CA6IlB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifyAttestationPacked.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationPacked.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAYtF;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"verifyAttestationPacked.d.ts","sourceRoot":"","sources":["../../../src/registration/verifications/verifyAttestationPacked.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAYtF;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CA0JlB"}
|