"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SubscriberKeyExchange = void 0;
const utils_1 = require("@streamr/utils");
const tsyringe_1 = require("tsyringe");
const uuid_1 = require("uuid");
const Authentication_1 = require("../Authentication");
const Config_1 = require("../Config");
const NetworkNodeFacade_1 = require("../NetworkNodeFacade");
const StreamRegistry_1 = require("../contracts/StreamRegistry");
const GroupKeyRequest_1 = require("../protocol/GroupKeyRequest");
const GroupKeyResponse_1 = require("../protocol/GroupKeyResponse");
const MessageID_1 = require("../protocol/MessageID");
const StreamMessage_1 = require("../protocol/StreamMessage");
const oldStreamMessageBinaryUtils_1 = require("../protocol/oldStreamMessageBinaryUtils");
const messageChain_1 = require("../publish/messageChain");
const MessageSigner_1 = require("../signature/MessageSigner");
const SignatureValidator_1 = require("../signature/SignatureValidator");
const Subscriber_1 = require("../subscribe/Subscriber");
const LoggerFactory_1 = require("../utils/LoggerFactory");
const promises_1 = require("../utils/promises");
const utils_2 = require("../utils/utils");
const validateStreamMessage_1 = require("../utils/validateStreamMessage");
const GroupKey_1 = require("./GroupKey");
const LocalGroupKeyStore_1 = require("./LocalGroupKeyStore");
const RSAKeyPair_1 = require("./RSAKeyPair");
const MAX_PENDING_REQUEST_COUNT = 50000; // just some limit, we can tweak the number if needed
/*
 * Sends group key requests and receives group key responses
 */
let SubscriberKeyExchange = class SubscriberKeyExchange {
    rsaKeyPair;
    pendingRequests = new utils_2.MaxSizedSet(MAX_PENDING_REQUEST_COUNT);
    networkNodeFacade;
    streamRegistry;
    signatureValidator;
    messageSigner;
    store;
    subscriber;
    authentication;
    logger;
    ensureStarted;
    requestGroupKey;
    constructor(networkNodeFacade, streamRegistry, signatureValidator, messageSigner, store, subscriber, config, authentication, loggerFactory) {
        this.networkNodeFacade = networkNodeFacade;
        this.streamRegistry = streamRegistry;
        this.signatureValidator = signatureValidator;
        this.messageSigner = messageSigner;
        this.store = store;
        this.subscriber = subscriber;
        this.authentication = authentication;
        this.logger = loggerFactory.createLogger(module);
        this.ensureStarted = (0, promises_1.pOnce)(async () => {
            this.rsaKeyPair = await RSAKeyPair_1.RSAKeyPair.create(config.encryption.rsaKeyLength);
            networkNodeFacade.addMessageListener((msg) => this.onMessage(msg));
            this.logger.debug('Started');
        });
        this.requestGroupKey = (0, promises_1.withThrottling)((groupKeyId, publisherId, streamPartId) => {
            return this.doRequestGroupKey(groupKeyId, publisherId, streamPartId);
        }, config.encryption.maxKeyRequestsPerSecond);
    }
    async doRequestGroupKey(groupKeyId, publisherId, streamPartId) {
        await this.ensureStarted();
        const requestId = (0, uuid_1.v4)();
        const request = await this.createRequest(groupKeyId, streamPartId, publisherId, this.rsaKeyPair.getPublicKey(), requestId);
        await this.networkNodeFacade.broadcast(request);
        this.pendingRequests.add(requestId);
        this.logger.debug('Sent group key request (waiting for response)', {
            groupKeyId,
            requestId,
            publisherId
        });
    }
    async createRequest(groupKeyId, streamPartId, publisherId, rsaPublicKey, requestId) {
        const requestContent = new GroupKeyRequest_1.GroupKeyRequest({
            recipient: publisherId,
            requestId,
            rsaPublicKey,
            groupKeyIds: [groupKeyId],
        });
        const erc1271contract = this.subscriber.getERC1271ContractAddress(streamPartId);
        return this.messageSigner.createSignedMessage({
            messageId: new MessageID_1.MessageID(utils_1.StreamPartIDUtils.getStreamID(streamPartId), utils_1.StreamPartIDUtils.getStreamPartition(streamPartId), Date.now(), 0, erc1271contract === undefined ? await this.authentication.getUserId() : (0, utils_1.toUserId)(erc1271contract), (0, messageChain_1.createRandomMsgChainId)()),
            content: (0, oldStreamMessageBinaryUtils_1.convertGroupKeyRequestToBytes)(requestContent),
            contentType: StreamMessage_1.ContentType.BINARY,
            messageType: StreamMessage_1.StreamMessageType.GROUP_KEY_REQUEST,
            encryptionType: StreamMessage_1.EncryptionType.NONE,
        }, erc1271contract === undefined ? StreamMessage_1.SignatureType.SECP256K1 : StreamMessage_1.SignatureType.ERC_1271);
    }
    async onMessage(msg) {
        if (GroupKeyResponse_1.GroupKeyResponse.is(msg)) {
            try {
                const { requestId, recipient, encryptedGroupKeys } = (0, oldStreamMessageBinaryUtils_1.convertBytesToGroupKeyResponse)(msg.content);
                if (await this.isAssignedToMe(msg.getStreamPartID(), recipient, requestId)) {
                    this.logger.debug('Handle group key response', { requestId });
                    this.pendingRequests.delete(requestId);
                    await (0, validateStreamMessage_1.validateStreamMessage)(msg, this.streamRegistry, this.signatureValidator);
                    await Promise.all(encryptedGroupKeys.map(async (encryptedKey) => {
                        const key = GroupKey_1.GroupKey.decryptRSAEncrypted(encryptedKey, this.rsaKeyPair.getPrivateKey());
                        await this.store.set(key.id, msg.getPublisherId(), key.data);
                    }));
                }
            }
            catch (err) {
                this.logger.debug('Failed to handle group key response', { err });
            }
        }
    }
    async isAssignedToMe(streamPartId, recipientId, requestId) {
        if (this.pendingRequests.has(requestId)) {
            const authenticatedUser = await this.authentication.getUserId();
            const erc1271Contract = this.subscriber.getERC1271ContractAddress(streamPartId);
            return (recipientId === authenticatedUser) || ((erc1271Contract !== undefined) && (recipientId === (0, utils_1.toUserId)(erc1271Contract)));
        }
        return false;
    }
};
exports.SubscriberKeyExchange = SubscriberKeyExchange;
exports.SubscriberKeyExchange = SubscriberKeyExchange = __decorate([
    (0, tsyringe_1.scoped)(tsyringe_1.Lifecycle.ContainerScoped),
    __param(1, (0, tsyringe_1.inject)((0, tsyringe_1.delay)(() => StreamRegistry_1.StreamRegistry))),
    __param(6, (0, tsyringe_1.inject)(Config_1.ConfigInjectionToken)),
    __param(7, (0, tsyringe_1.inject)(Authentication_1.AuthenticationInjectionToken)),
    __metadata("design:paramtypes", [NetworkNodeFacade_1.NetworkNodeFacade,
        StreamRegistry_1.StreamRegistry,
        SignatureValidator_1.SignatureValidator,
        MessageSigner_1.MessageSigner,
        LocalGroupKeyStore_1.LocalGroupKeyStore,
        Subscriber_1.Subscriber, Object, Object, LoggerFactory_1.LoggerFactory])
], SubscriberKeyExchange);
//# sourceMappingURL=SubscriberKeyExchange.js.map