src/crypto/index.js
function _maybeUploadOneTimeKeys(crypto) {
    // frequency with which to check & upload one-time keys
    const uploadPeriod = 1000 * 60; // one minute

    // max number of keys to upload at once
    // Creating keys can be an expensive operation so we limit the
    // number we generate in one go to avoid blocking the application
    // for too long.
    const maxKeysPerCycle = 5;

    if (crypto._oneTimeKeyCheckInProgress) {
        return;
    }

    const now = Date.now();
    if (crypto._lastOneTimeKeyCheck !== null &&
        now - crypto._lastOneTimeKeyCheck < uploadPeriod
       ) {
        // we've done a key upload recently.
        return;
    }

    crypto._lastOneTimeKeyCheck = now;

    // We need to keep a pool of one time public keys on the server so that
    // other devices can start conversations with us. But we can only store
    // a finite number of private keys in the olm Account object.
    // To complicate things further then can be a delay between a device
    // claiming a public one time key from the server and it sending us a
    // message. We need to keep the corresponding private key locally until
    // we receive the message.
    // But that message might never arrive leaving us stuck with duff
    // private keys clogging up our local storage.
    // So we need some kind of enginering compromise to balance all of
    // these factors.

    // Check how many keys we can store in the Account object.
    const maxOneTimeKeys = crypto._olmDevice.maxNumberOfOneTimeKeys();
    // Try to keep at most half that number on the server. This leaves the
    // rest of the slots free to hold keys that have been claimed from the
    // server but we haven't recevied a message for.
    // If we run out of slots when generating new keys then olm will
    // discard the oldest private keys first. This will eventually clean
    // out stale private keys that won't receive a message.
    const keyLimit = Math.floor(maxOneTimeKeys / 2);

    function uploadLoop(keyCount) {
        if (keyLimit <= keyCount) {
            // If we don't need to generate any more keys then we are done.
            return Promise.resolve();
        }

        const keysThisLoop = Math.min(keyLimit - keyCount, maxKeysPerCycle);

        // Ask olm to generate new one time keys, then upload them to synapse.
        return crypto._olmDevice.generateOneTimeKeys(keysThisLoop).then(() => {
            return _uploadOneTimeKeys(crypto);
        }).then((res) => {
            if (res.one_time_key_counts && res.one_time_key_counts.signed_curve25519) {
                // if the response contains a more up to date value use this
                // for the next loop
                return uploadLoop(res.one_time_key_counts.signed_curve25519);
            } else {
                throw new Error("response for uploading keys does not contain "
                              + "one_time_key_counts.signed_curve25519");
            }
        });
    }

    crypto._oneTimeKeyCheckInProgress = true;
    Promise.resolve().then(() => {
        if (crypto._oneTimeKeyCount !== undefined) {
            // We already have the current one_time_key count from a /sync response.
            // Use this value instead of asking the server for the current key count.
            return Promise.resolve(crypto._oneTimeKeyCount);
        }
        // ask the server how many keys we have
        return crypto._baseApis.uploadKeysRequest({}, {
            device_id: crypto._deviceId,
        }).then((res) => {
            return res.one_time_key_counts.signed_curve25519 || 0;
        });
    }).then((keyCount) => {
        // Start the uploadLoop with the current keyCount. The function checks if
        // we need to upload new keys or not.
        // If there are too many keys on the server then we don't need to
        // create any more keys.
        return uploadLoop(keyCount);
    }).catch((e) => {
        logger.error("Error uploading one-time keys", e.stack || e);
    }).finally(() => {
        // reset _oneTimeKeyCount to prevent start uploading based on old data.
        // it will be set again on the next /sync-response
        crypto._oneTimeKeyCount = undefined;
        crypto._oneTimeKeyCheckInProgress = false;
    }).done();
}
function _maybeUploadOneTimeKeys(crypto) {
    // frequency with which to check & upload one-time keys
    const uploadPeriod = 1000 * 60; // one minute

    // max number of keys to upload at once
    // Creating keys can be an expensive operation so we limit the
    // number we generate in one go to avoid blocking the application
    // for too long.
    const maxKeysPerCycle = 5;

    if (crypto._oneTimeKeyCheckInProgress) {
        return;
    }

    const now = Date.now();
    if (crypto._lastOneTimeKeyCheck !== null &&
        now - crypto._lastOneTimeKeyCheck < uploadPeriod
       ) {
        // we've done a key upload recently.
        return;
    }

    crypto._lastOneTimeKeyCheck = now;

    // We need to keep a pool of one time public keys on the server so that
    // other devices can start conversations with us. But we can only store
    // a finite number of private keys in the olm Account object.
    // To complicate things further then can be a delay between a device
    // claiming a public one time key from the server and it sending us a
    // message. We need to keep the corresponding private key locally until
    // we receive the message.
    // But that message might never arrive leaving us stuck with duff
    // private keys clogging up our local storage.
    // So we need some kind of enginering compromise to balance all of
    // these factors.

    // Check how many keys we can store in the Account object.
    const maxOneTimeKeys = crypto._olmDevice.maxNumberOfOneTimeKeys();
    // Try to keep at most half that number on the server. This leaves the
    // rest of the slots free to hold keys that have been claimed from the
    // server but we haven't recevied a message for.
    // If we run out of slots when generating new keys then olm will
    // discard the oldest private keys first. This will eventually clean
    // out stale private keys that won't receive a message.
    const keyLimit = Math.floor(maxOneTimeKeys / 2);

    function uploadLoop(keyCount) {
        if (keyLimit <= keyCount) {
            // If we don't need to generate any more keys then we are done.
            return Promise.resolve();
        }

        const keysThisLoop = Math.min(keyLimit - keyCount, maxKeysPerCycle);

        // Ask olm to generate new one time keys, then upload them to synapse.
        return crypto._olmDevice.generateOneTimeKeys(keysThisLoop).then(() => {
            return _uploadOneTimeKeys(crypto);
        }).then((res) => {
            if (res.one_time_key_counts && res.one_time_key_counts.signed_curve25519) {
                // if the response contains a more up to date value use this
                // for the next loop
                return uploadLoop(res.one_time_key_counts.signed_curve25519);
            } else {
                throw new Error("response for uploading keys does not contain "
                              + "one_time_key_counts.signed_curve25519");
            }
        });
    }

    crypto._oneTimeKeyCheckInProgress = true;
    Promise.resolve().then(() => {
        if (crypto._oneTimeKeyCount !== undefined) {
            // We already have the current one_time_key count from a /sync response.
            // Use this value instead of asking the server for the current key count.
            return Promise.resolve(crypto._oneTimeKeyCount);
        }
        // ask the server how many keys we have
        return crypto._baseApis.uploadKeysRequest({}, {
            device_id: crypto._deviceId,
        }).then((res) => {
            return res.one_time_key_counts.signed_curve25519 || 0;
        });
    }).then((keyCount) => {
        // Start the uploadLoop with the current keyCount. The function checks if
        // we need to upload new keys or not.
        // If there are too many keys on the server then we don't need to
        // create any more keys.
        return uploadLoop(keyCount);
    }).catch((e) => {
        logger.error("Error uploading one-time keys", e.stack || e);
    }).finally(() => {
        // reset _oneTimeKeyCount to prevent start uploading based on old data.
        // it will be set again on the next /sync-response
        crypto._oneTimeKeyCount = undefined;
        crypto._oneTimeKeyCheckInProgress = false;
    }).done();
}
function _maybeUploadOneTimeKeys ( crypto ) { // frequency with which to check & upload one-time keys const uploadPeriod = 1000 * 60 ; // one minute // max number of keys to upload at once // Creating keys can be an expensive operation so we limit the // number we generate in one go to avoid blocking the application // for too long. const maxKeysPerCycle = 5 ; if ( crypto . _oneTimeKeyCheckInProgress ) { return ; } const now = Date . now ( ) ; if ( crypto . _lastOneTimeKeyCheck !== null && now - crypto . _lastOneTimeKeyCheck < uploadPeriod ) { // we've done a key upload recently. return ; } crypto . _lastOneTimeKeyCheck = now ; // We need to keep a pool of one time public keys on the server so that // other devices can start conversations with us. But we can only store // a finite number of private keys in the olm Account object. // To complicate things further then can be a delay between a device // claiming a public one time key from the server and it sending us a // message. We need to keep the corresponding private key locally until // we receive the message. // But that message might never arrive leaving us stuck with duff // private keys clogging up our local storage. // So we need some kind of enginering compromise to balance all of // these factors. // Check how many keys we can store in the Account object. const maxOneTimeKeys = crypto . _olmDevice . maxNumberOfOneTimeKeys ( ) ; // Try to keep at most half that number on the server. This leaves the // rest of the slots free to hold keys that have been claimed from the // server but we haven't recevied a message for. // If we run out of slots when generating new keys then olm will // discard the oldest private keys first. This will eventually clean // out stale private keys that won't receive a message. const keyLimit = Math . floor ( maxOneTimeKeys / 2 ) ; function uploadLoop ( keyCount ) { if ( keyLimit <= keyCount ) { // If we don't need to generate any more keys then we are done. return Promise . resolve ( ) ; } const keysThisLoop = Math . min ( keyLimit - keyCount , maxKeysPerCycle ) ; // Ask olm to generate new one time keys, then upload them to synapse. return crypto . _olmDevice . generateOneTimeKeys ( keysThisLoop ) . then ( ( ) => { return _uploadOneTimeKeys ( crypto ) ; } ) . then ( ( res ) => { if ( res . one_time_key_counts && res . one_time_key_counts . signed_curve25519 ) { // if the response contains a more up to date value use this // for the next loop return uploadLoop ( res . one_time_key_counts . signed_curve25519 ) ; } else { throw new Error ( "response for uploading keys does not contain " + "one_time_key_counts.signed_curve25519" ) ; } } ) ; } crypto . _oneTimeKeyCheckInProgress = true ; Promise . resolve ( ) . then ( ( ) => { if ( crypto . _oneTimeKeyCount !== undefined ) { // We already have the current one_time_key count from a /sync response. // Use this value instead of asking the server for the current key count. return Promise . resolve ( crypto . _oneTimeKeyCount ) ; } // ask the server how many keys we have return crypto . _baseApis . uploadKeysRequest ( { } , { device_id : crypto . _deviceId , } ) . then ( ( res ) => { return res . one_time_key_counts . signed_curve25519 || 0 ; } ) ; } ) . then ( ( keyCount ) => { // Start the uploadLoop with the current keyCount. The function checks if // we need to upload new keys or not. // If there are too many keys on the server then we don't need to // create any more keys. return uploadLoop ( keyCount ) ; } ) . catch ( ( e ) => { logger . error ( "Error uploading one-time keys" , e . stack || e ) ; } ) . finally ( ( ) => { // reset _oneTimeKeyCount to prevent start uploading based on old data. // it will be set again on the next /sync-response crypto . _oneTimeKeyCount = undefined ; crypto . _oneTimeKeyCheckInProgress = false ; } ) . done ( ) ; }
check if it's time to upload one-time keys, and do so if so.
check if it s time to upload one - time keys and do so if so .