summaryrefslogtreecommitdiffhomepage
path: root/packages/server/src/helpers/validateCertificatePath.ts
diff options
context:
space:
mode:
authorMatthew Miller <matthew@millerti.me>2021-08-18 16:21:23 -0700
committerGitHub <noreply@github.com>2021-08-18 16:21:23 -0700
commit2f87d70bf348cf854c6ac215bd9d424502278da6 (patch)
tree45a387512e7c7097bb64036c881973b45c18ff31 /packages/server/src/helpers/validateCertificatePath.ts
parent3e1f9cf3d41f509a387d7cba3f44a7a93f90e07c (diff)
parent98cf4b68bc0cec016994d073afab5461627dfc96 (diff)
Merge pull request #138 from MasterKale/feat/configuration-service
feat/settings-service
Diffstat (limited to 'packages/server/src/helpers/validateCertificatePath.ts')
-rw-r--r--packages/server/src/helpers/validateCertificatePath.ts49
1 files changed, 47 insertions, 2 deletions
diff --git a/packages/server/src/helpers/validateCertificatePath.ts b/packages/server/src/helpers/validateCertificatePath.ts
index 92403e6..ae8a2fd 100644
--- a/packages/server/src/helpers/validateCertificatePath.ts
+++ b/packages/server/src/helpers/validateCertificatePath.ts
@@ -10,8 +10,44 @@ 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) {
+ // We have no root certs with which to create a full path, so skip path validation
+ // TODO: Is this going to be acceptable default behavior??
+ 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 +86,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 +104,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';
+ }
+}