71 lines
2.0 KiB
TypeScript
71 lines
2.0 KiB
TypeScript
import {
|
|
type Asn1Node,
|
|
decodeAsn1,
|
|
isConstructed,
|
|
UNIVERSAL_TAGS,
|
|
} from "../asn1/index.js";
|
|
import { X509_CODES, X509Error } from "../errors/error-codes.js";
|
|
import type { BasicConstraints } from "./extension.types.js";
|
|
|
|
function bad(reason: string): X509Error {
|
|
return new X509Error(
|
|
X509_CODES.EXTENSION_MALFORMED,
|
|
"error.x509.basic-constraints-malformed",
|
|
{ context: { reason } }
|
|
);
|
|
}
|
|
|
|
/** OID for the basic constraints extension. */
|
|
export const OID_BASIC_CONSTRAINTS = "2.5.29.19";
|
|
|
|
export function decodeBasicConstraints(value: Uint8Array): BasicConstraints {
|
|
const outer = decodeAsn1(value);
|
|
if (
|
|
!isConstructed(outer) ||
|
|
outer.tag.tagNumber !== UNIVERSAL_TAGS.SEQUENCE
|
|
) {
|
|
throw bad("not-sequence");
|
|
}
|
|
let cA = false;
|
|
let pathLenConstraint: number | undefined;
|
|
let idx = 0;
|
|
const first = outer.children[idx];
|
|
if (
|
|
first !== undefined &&
|
|
!isConstructed(first) &&
|
|
first.tag.tagNumber === UNIVERSAL_TAGS.BOOLEAN
|
|
) {
|
|
if (first.content.length !== 1) {
|
|
throw bad("bool-len");
|
|
}
|
|
cA = (first.content[0] as number) !== 0x00;
|
|
idx += 1;
|
|
}
|
|
const second = outer.children[idx];
|
|
if (
|
|
second !== undefined &&
|
|
!isConstructed(second) &&
|
|
second.tag.tagNumber === UNIVERSAL_TAGS.INTEGER
|
|
) {
|
|
if (second.content.length === 0 || second.content.length > 4) {
|
|
throw bad("pathlen-len");
|
|
}
|
|
let n = 0;
|
|
for (let i = 0; i < second.content.length; i++) {
|
|
n = (n << 8) | (second.content[i] as number);
|
|
}
|
|
if (n < 0) {
|
|
throw bad("pathlen-negative");
|
|
}
|
|
pathLenConstraint = n;
|
|
idx += 1;
|
|
}
|
|
if (idx !== outer.children.length) {
|
|
throw bad("extra-fields");
|
|
}
|
|
return pathLenConstraint === undefined ? { cA } : { cA, pathLenConstraint };
|
|
}
|
|
|
|
// Silence unused-import warning when only types are referenced here.
|
|
export type { Asn1Node };
|