"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.decrypt = exports.encrypt = exports.signJsonLDObject = exports.cryptPassword = exports.createPrivateAndPublicKeys = exports.comparePassword = exports.isJsonLDSignatureVerified = exports.buildDigest = exports.isHTTPSignatureVerified = exports.parseHTTPSignature = exports.isHTTPSignatureDigestValid = void 0;
const tslib_1 = require("tslib");
const bcrypt_1 = require("bcrypt");
const crypto_1 = require("crypto");
const lodash_1 = require("lodash");
const extra_utils_1 = require("../../shared/extra-utils");
const constants_1 = require("../initializers/constants");
const core_utils_1 = require("./core-utils");
const custom_jsonld_signature_1 = require("./custom-jsonld-signature");
const logger_1 = require("./logger");
const bcryptComparePromise = (0, core_utils_1.promisify2)(bcrypt_1.compare);
const bcryptGenSaltPromise = (0, core_utils_1.promisify1)(bcrypt_1.genSalt);
const bcryptHashPromise = (0, core_utils_1.promisify2)(bcrypt_1.hash);
const httpSignature = require('@peertube/http-signature');
function createPrivateAndPublicKeys() {
    logger_1.logger.info('Generating a RSA key...');
    return (0, core_utils_1.generateRSAKeyPairPromise)(constants_1.PRIVATE_RSA_KEY_SIZE);
}
exports.createPrivateAndPublicKeys = createPrivateAndPublicKeys;
function comparePassword(plainPassword, hashPassword) {
    if (!plainPassword)
        return Promise.resolve(false);
    return bcryptComparePromise(plainPassword, hashPassword);
}
exports.comparePassword = comparePassword;
function cryptPassword(password) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const salt = yield bcryptGenSaltPromise(constants_1.BCRYPT_SALT_SIZE);
        return bcryptHashPromise(password, salt);
    });
}
exports.cryptPassword = cryptPassword;
function isHTTPSignatureDigestValid(rawBody, req) {
    if (req.headers[constants_1.HTTP_SIGNATURE.HEADER_NAME] && req.headers['digest']) {
        return buildDigest(rawBody.toString()) === req.headers['digest'];
    }
    return true;
}
exports.isHTTPSignatureDigestValid = isHTTPSignatureDigestValid;
function isHTTPSignatureVerified(httpSignatureParsed, actor) {
    return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true;
}
exports.isHTTPSignatureVerified = isHTTPSignatureVerified;
function parseHTTPSignature(req, clockSkew) {
    const requiredHeaders = req.method === 'POST'
        ? ['(request-target)', 'host', 'digest']
        : ['(request-target)', 'host'];
    const parsed = httpSignature.parse(req, { clockSkew, headers: requiredHeaders });
    const parsedHeaders = parsed.params.headers;
    if (!parsedHeaders.includes('date') && !parsedHeaders.includes('(created)')) {
        throw new Error(`date or (created) must be included in signature`);
    }
    return parsed;
}
exports.parseHTTPSignature = parseHTTPSignature;
function isJsonLDSignatureVerified(fromActor, signedDocument) {
    if (signedDocument.signature.type === 'RsaSignature2017') {
        return isJsonLDRSA2017Verified(fromActor, signedDocument);
    }
    logger_1.logger.warn('Unknown JSON LD signature %s.', signedDocument.signature.type, signedDocument);
    return Promise.resolve(false);
}
exports.isJsonLDSignatureVerified = isJsonLDSignatureVerified;
function isJsonLDRSA2017Verified(fromActor, signedDocument) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const [documentHash, optionsHash] = yield Promise.all([
            createDocWithoutSignatureHash(signedDocument),
            createSignatureHash(signedDocument.signature)
        ]);
        const toVerify = optionsHash + documentHash;
        const verify = (0, crypto_1.createVerify)('RSA-SHA256');
        verify.update(toVerify, 'utf8');
        return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64');
    });
}
function signJsonLDObject(byActor, data) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const signature = {
            type: 'RsaSignature2017',
            creator: byActor.url,
            created: new Date().toISOString()
        };
        const [documentHash, optionsHash] = yield Promise.all([
            createDocWithoutSignatureHash(data),
            createSignatureHash(signature)
        ]);
        const toSign = optionsHash + documentHash;
        const sign = (0, crypto_1.createSign)('RSA-SHA256');
        sign.update(toSign, 'utf8');
        const signatureValue = sign.sign(byActor.privateKey, 'base64');
        Object.assign(signature, { signatureValue });
        return Object.assign(data, { signature });
    });
}
exports.signJsonLDObject = signJsonLDObject;
function buildDigest(body) {
    const rawBody = typeof body === 'string' ? body : JSON.stringify(body);
    return 'SHA-256=' + (0, extra_utils_1.sha256)(rawBody, 'base64');
}
exports.buildDigest = buildDigest;
function encrypt(str, secret) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const iv = yield (0, core_utils_1.randomBytesPromise)(constants_1.ENCRYPTION.IV);
        const key = yield (0, core_utils_1.scryptPromise)(secret, constants_1.ENCRYPTION.SALT, 32);
        const cipher = (0, crypto_1.createCipheriv)(constants_1.ENCRYPTION.ALGORITHM, key, iv);
        let encrypted = iv.toString(constants_1.ENCRYPTION.ENCODING) + ':';
        encrypted += cipher.update(str, 'utf8', constants_1.ENCRYPTION.ENCODING);
        encrypted += cipher.final(constants_1.ENCRYPTION.ENCODING);
        return encrypted;
    });
}
exports.encrypt = encrypt;
function decrypt(encryptedArg, secret) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        const [ivStr, encryptedStr] = encryptedArg.split(':');
        const iv = Buffer.from(ivStr, 'hex');
        const key = yield (0, core_utils_1.scryptPromise)(secret, constants_1.ENCRYPTION.SALT, 32);
        const decipher = (0, crypto_1.createDecipheriv)(constants_1.ENCRYPTION.ALGORITHM, key, iv);
        return decipher.update(encryptedStr, constants_1.ENCRYPTION.ENCODING, 'utf8') + decipher.final('utf8');
    });
}
exports.decrypt = decrypt;
function hashObject(obj) {
    return custom_jsonld_signature_1.jsonld.promises.normalize(obj, {
        safe: false,
        algorithm: 'URDNA2015',
        format: 'application/n-quads'
    }).then(res => (0, extra_utils_1.sha256)(res));
}
function createSignatureHash(signature) {
    const signatureCopy = (0, lodash_1.cloneDeep)(signature);
    Object.assign(signatureCopy, {
        '@context': [
            'https://w3id.org/security/v1',
            { RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' }
        ]
    });
    delete signatureCopy.type;
    delete signatureCopy.id;
    delete signatureCopy.signatureValue;
    return hashObject(signatureCopy);
}
function createDocWithoutSignatureHash(doc) {
    const docWithoutSignature = (0, lodash_1.cloneDeep)(doc);
    delete docWithoutSignature.signature;
    return hashObject(docWithoutSignature);
}
//# sourceMappingURL=peertube-crypto.js.map