"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InvalidPayloadError = exports.InvalidHeaderError = exports.InvalidLicenseSignatureError = exports.InvalidLicenseError = exports.LicenseEncodingError = exports.PublicKey = void 0;
exports.initPublicKey = initPublicKey;
exports.validateLicense = validateLicense;
const node_crypto_1 = require("node:crypto");
class PublicKey {
    pem;
    constructor(mode) {
        if (mode && (mode === 'test' || mode === 'development')) {
            this.pem = `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAO301jvpO12znGdK/Izrre518pgmQNk9hSMXf4wDMucM=
-----END PUBLIC KEY-----
`;
        }
        else {
            this.pem = `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAMtpzMn9YfS0fGaDLcAmYQx2OH8kVevwbNyQ1RIj5cvw=
-----END PUBLIC KEY-----
`;
        }
    }
    getKey() {
        return (0, node_crypto_1.createPublicKey)(this.pem);
    }
    toString() {
        return this.pem;
    }
}
exports.PublicKey = PublicKey;
class LicenseEncodingError extends TypeError {
}
exports.LicenseEncodingError = LicenseEncodingError;
class InvalidLicenseError extends TypeError {
}
exports.InvalidLicenseError = InvalidLicenseError;
class InvalidLicenseSignatureError extends InvalidLicenseError {
}
exports.InvalidLicenseSignatureError = InvalidLicenseSignatureError;
class InvalidHeaderError extends InvalidLicenseError {
}
exports.InvalidHeaderError = InvalidHeaderError;
class InvalidPayloadError extends InvalidLicenseError {
}
exports.InvalidPayloadError = InvalidPayloadError;
let publicKey;
function initPublicKey(mode) {
    publicKey = new PublicKey(mode);
}
function getPublicKey() {
    if (!publicKey) {
        publicKey = new PublicKey();
    }
    return publicKey;
}
function validateLicenseSignature(encodedLicense) {
    if (typeof encodedLicense !== 'string') {
        throw new LicenseEncodingError(`License must be a string; received ${typeof encodedLicense}: ${encodedLicense}`);
    }
    let licenseComponents;
    try {
        licenseComponents = encodedLicense.split('.');
    }
    catch (cause) {
        const error = new LicenseEncodingError(`Unable to split license into components; license must be a string with three dot-separated parts; got: ${encodedLicense}`);
        error.cause = cause;
        throw error;
    }
    if (licenseComponents.length !== 3) {
        throw new InvalidLicenseError(`License must have three dot-separated parts; got ${licenseComponents.length}`);
    }
    const [header, payload, signature] = licenseComponents;
    const pubKey = getPublicKey().getKey();
    const valid = (0, node_crypto_1.verify)(null, Buffer.from(header + '.' + payload, 'utf8'), pubKey, Buffer.from(signature, 'base64url'));
    if (!valid) {
        throw new InvalidLicenseSignatureError('License signature is invalid');
    }
    return {
        header: toJSON(header),
        payload: toJSON(payload),
        signature: toJSON(signature),
    };
    function toJSON(str) {
        return Buffer.from(str, 'base64url').toString('utf8');
    }
}
function validateLicenseHeader(header) {
    if (header?.typ !== 'Harper-License') {
        throw new InvalidHeaderError(`Invalid license header; typ must be 'Harper-License'; got: ${header?.typ}`);
    }
    if (header?.alg !== 'EdDSA') {
        throw new InvalidHeaderError(`Invalid license header; alg must be 'EdDSA'; got: ${header?.alg}`);
    }
}
function valid(schema, value) {
    if (schema.required) {
        return typeof value === schema.type;
    }
    return typeof value === 'undefined' || typeof value === schema.type;
}
function validateLicensePayload(payload) {
    const attrs = {
        id: { required: true, type: 'string' },
        region: { required: false, type: 'string' },
        expiration: { required: true, type: 'string' },
        level: { required: true, type: 'number' },
        reads: { required: true, type: 'number' },
        writes: { required: true, type: 'number' },
        readBytes: { required: true, type: 'number' },
        writeBytes: { required: true, type: 'number' },
        realTimeMessages: { required: true, type: 'number' },
        realTimeBytes: { required: true, type: 'number' },
        cpuTime: { required: true, type: 'number' },
        storage: { required: true, type: 'number' },
        autoRenew: { required: false, type: 'boolean' },
    };
    for (const attr in attrs) {
        const { required, type } = attrs[attr];
        const attrDesc = required ? `required attribute '${attr}'` : `optional attribute '${attr}', when present,`;
        if (!valid(attrs[attr], payload[attr])) {
            throw new InvalidPayloadError(`Invalid license payload; ${attrDesc} must be a ${type}; got: ${typeof payload[attr]}`);
        }
    }
}
function validateLicense(encodedLicense) {
    const { header: headerJSON, payload: payloadJSON } = validateLicenseSignature(encodedLicense);
    let header;
    try {
        header = JSON.parse(headerJSON);
    }
    catch (cause) {
        const error = new InvalidHeaderError();
        error.cause = cause;
        throw error;
    }
    validateLicenseHeader(header);
    let payload;
    try {
        payload = JSON.parse(payloadJSON);
    }
    catch (cause) {
        const error = new InvalidPayloadError();
        error.cause = cause;
        throw error;
    }
    validateLicensePayload(payload);
    return payload;
}
//# sourceMappingURL=validation.js.map