node-opcua/node-opcua-crypto

View on GitHub
packages/node-opcua-crypto/source/explore_certificate_revocation_list.ts

Summary

Maintainability
C
1 day
Test Coverage
// ---------------------------------------------------------------------------------------------------------------------
// node-opcua-crypto
// ---------------------------------------------------------------------------------------------------------------------
// Copyright (c) 2014-2022 - Etienne Rossignon - etienne.rossignon (at) gadz.org
// Copyright (c) 2022-2024 - Sterfive.com
// ---------------------------------------------------------------------------------------------------------------------
//
// This  project is licensed under the terms of the MIT license.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so,  subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ---------------------------------------------------------------------------------------------------------------------

import {
    _readStruct,
    readTag,
    _readBitString,
    AlgorithmIdentifier,
    _readAlgorithmIdentifier,
    _readSignatureValue,
    _readSignatureValueBin,
    BlockInfo,
    _readObjectIdentifier,
    DirectoryName,
    _readValue,
    _readTime,
    _readLongIntegerValue,
    formatBuffer2DigitHexWithColum,
    _getBlock,
    _readDirectoryName,
    _findBlockAtIndex,
    _readIntegerValue,
    TagType,
} from "./asn1.js";
import { CertificateRevocationList } from "./common.js";
import { makeSHA1Thumbprint, convertPEMtoDER } from "./crypto_utils.js";

export type Version = string;
export type Name = string;
export type CertificateSerialNumber = string;
export type Extensions = Record<string, unknown>;
export interface RevokedCertificate {
    userCertificate: CertificateSerialNumber;
    revocationDate: Date;
    crlEntryExtensions?: Extensions;
}
export interface TBSCertList {
    version?: Version; //OPTIONAL; // must be 2
    signature: AlgorithmIdentifier;
    issuer: Name;
    issuerFingerprint: string; // 00:AA:BB:etc ...
    thisUpdate: Date;
    nextUpdate?: Date; //             Time OPTIONAL,
    revokedCertificates: RevokedCertificate[];
    //    crlExtensions[0]  EXPLICIT Extensions OPTIONAL
}
export interface CertificateRevocationListInfo {
    tbsCertList: TBSCertList;
    signatureAlgorithm: AlgorithmIdentifier;
    signatureValue: Buffer;
}

export function readNameForCrl(buffer: Buffer, block: BlockInfo): DirectoryName {
    return _readDirectoryName(buffer, block);
}

function _readTbsCertList(buffer: Buffer, blockInfo: BlockInfo): TBSCertList {
    const blocks = _readStruct(buffer, blockInfo);

    const hasOptionalVersion = blocks[0].tag === TagType.INTEGER;

    if (hasOptionalVersion) {
        const version = _readIntegerValue(buffer, blocks[0]);
        const signature = _readAlgorithmIdentifier(buffer, blocks[1]);
        const issuer = readNameForCrl(buffer, blocks[2]);
        const issuerFingerprint = formatBuffer2DigitHexWithColum(makeSHA1Thumbprint(_getBlock(buffer, blocks[2])));

        const thisUpdate = _readTime(buffer, blocks[3]);
        const nextUpdate = _readTime(buffer, blocks[4]);

        const revokedCertificates: RevokedCertificate[] = [];

        if (blocks[5] && blocks[5].tag < 0x80) {
            const list = _readStruct(buffer, blocks[5]);
            for (const r of list) {
                // sometime blocks[5] doesn't exits .. in this case
                const rr = _readStruct(buffer, r);
                const userCertificate = formatBuffer2DigitHexWithColum(_readLongIntegerValue(buffer, rr[0]));
                const revocationDate = _readTime(buffer, rr[1]);
                revokedCertificates.push({
                    revocationDate,
                    userCertificate,
                });
            }
        }

        const ext0 = _findBlockAtIndex(blocks, 0);
        return { issuer, issuerFingerprint, thisUpdate, nextUpdate, signature, revokedCertificates } as TBSCertList;
    } else {
        const signature = _readAlgorithmIdentifier(buffer, blocks[0]);
        const issuer = readNameForCrl(buffer, blocks[1]);
        const issuerFingerprint = formatBuffer2DigitHexWithColum(makeSHA1Thumbprint(_getBlock(buffer, blocks[1])));

        const thisUpdate = _readTime(buffer, blocks[2]);
        const nextUpdate = _readTime(buffer, blocks[3]);

        const revokedCertificates: RevokedCertificate[] = [];

        if (blocks[4] && blocks[4].tag < 0x80) {
            const list = _readStruct(buffer, blocks[4]);
            for (const r of list) {
                // sometime blocks[5] doesn't exits .. in this case
                const rr = _readStruct(buffer, r);
                const userCertificate = formatBuffer2DigitHexWithColum(_readLongIntegerValue(buffer, rr[0]));
                const revocationDate = _readTime(buffer, rr[1]);
                revokedCertificates.push({
                    revocationDate,
                    userCertificate,
                });
            }
        }
        return { issuer, issuerFingerprint, thisUpdate, nextUpdate, signature, revokedCertificates } as TBSCertList;
    }
}
// see https://tools.ietf.org/html/rfc5280

export function exploreCertificateRevocationList(crl: CertificateRevocationList): CertificateRevocationListInfo {
    const blockInfo = readTag(crl, 0);
    const blocks = _readStruct(crl, blockInfo);
    const tbsCertList = _readTbsCertList(crl, blocks[0]);
    const signatureAlgorithm = _readAlgorithmIdentifier(crl, blocks[1]);
    const signatureValue = _readSignatureValueBin(crl, blocks[2]);
    return { tbsCertList, signatureAlgorithm, signatureValue };
}