summaryrefslogtreecommitdiffhomepage
path: root/packages/server/src
diff options
context:
space:
mode:
authorMatthew Miller <matthew@millerti.me>2021-08-03 05:03:52 -0700
committerMatthew Miller <matthew@millerti.me>2021-08-03 05:03:52 -0700
commit8b0c56879bf85938cd2b31117e66c83ecea42356 (patch)
treeff0b6eea697f1700feb83a27ae42060c1cbcee34 /packages/server/src
parenta180355e09ef913c09eb8861e924e6493a653de1 (diff)
Update path validation to try each root cert
Diffstat (limited to 'packages/server/src')
-rw-r--r--packages/server/src/helpers/validateCertificatePath.ts48
1 files changed, 46 insertions, 2 deletions
diff --git a/packages/server/src/helpers/validateCertificatePath.ts b/packages/server/src/helpers/validateCertificatePath.ts
index 92403e6..d1a5230 100644
--- a/packages/server/src/helpers/validateCertificatePath.ts
+++ b/packages/server/src/helpers/validateCertificatePath.ts
@@ -10,8 +10,43 @@ const { crypto } = KJUR;
/**
* Traverse an array of PEM certificates and ensure they form a proper chain
* @param certificates Typically the result of `x5c.map(convertASN1toPEM)`
+ * @param rootCertificates Possible root certificates to complete the path
*/
-export default async function validateCertificatePath(certificates: string[]): Promise<boolean> {
+export default async function validateCertificatePath(
+ certificates: string[],
+ rootCertificates: string[] = [],
+): Promise<boolean> {
+ if (rootCertificates.length === 0) {
+ _validatePath(certificates);
+ return true;
+ }
+
+ let invalidSubjectAndIssuerError = false;
+ for (const rootCert of rootCertificates) {
+ try {
+ const certsWithRoot = certificates.concat([rootCert]);
+ await _validatePath(certsWithRoot);
+ // If we successfully validated a path then there's no need to continue
+ invalidSubjectAndIssuerError = false;
+ break;
+ } catch (err) {
+ if (err instanceof InvalidSubjectAndIssuer) {
+ invalidSubjectAndIssuerError = true;
+ } else {
+ throw err;
+ }
+ }
+ }
+
+ // We tried multiple root certs and none of them worked
+ if (invalidSubjectAndIssuerError) {
+ throw new InvalidSubjectAndIssuer();
+ }
+
+ return true;
+}
+
+async function _validatePath(certificates: string[]): Promise<boolean> {
if (new Set(certificates).size !== certificates.length) {
throw new Error('Invalid certificate path: found duplicate certificates');
}
@@ -50,7 +85,7 @@ export default async function validateCertificatePath(certificates: string[]): P
}
if (subjectCert.getIssuerString() !== issuerCert.getSubjectString()) {
- throw new Error('Invalid certificate path: subject issuer did not match issuer subject');
+ throw new InvalidSubjectAndIssuer();
}
const subjectCertStruct = ASN1HEX.getTLVbyList(subjectCert.hex, 0, [0]);
@@ -68,3 +103,12 @@ export default async function validateCertificatePath(certificates: string[]): P
return true;
}
+
+// Custom errors to help pass on certain errors
+class InvalidSubjectAndIssuer extends Error {
+ constructor() {
+ const message = 'Subject issuer did not match issuer subject';
+ super(message);
+ this.name = 'InvalidSubjectAndIssuer';
+ }
+}