src/Ratchet.js
function Ratchet(crypto) {
    const self = this;

    const hkdf = new HKDF(crypto);

    /**
     * Derive the main and sub ratchet states from the shared secrets derived from the handshake.
     *
     * @method
     * @param {number} sessionVersion
     * @param {Array.<ArrayBuffer>} agreements - an array of ArrayBuffers containing the shared secrets
     * @return {Promise.<Object, Error>} the root and chain keys
     */
    this.deriveInitialRootKeyAndChain = co.wrap(function*(sessionVersion, agreements) {
        var secrets = [];
        if (sessionVersion >= 3) {
            secrets.push(discontinuityBytes);
        }
        secrets = secrets.concat(agreements);

        var masterSecret = ArrayBufferUtils.concat(secrets);
        var derivedSecret = yield hkdf.deriveSecrets(masterSecret, whisperText,
            ProtocolConstants.rootKeyByteCount + ProtocolConstants.chainKeyByteCount);
        return {
            rootKey: derivedSecret.slice(0, ProtocolConstants.rootKeyByteCount),
            chain: new Chain(derivedSecret.slice(ProtocolConstants.rootKeyByteCount))
        };
    });

    /**
     * Derive the next main and sub ratchet states from the previous state.
     * <p>
     * This method "clicks" the Diffie-Hellman ratchet forwards.
     *
     * @method
     * @param {ArrayBuffer} rootKey - the current root key
     * @param {ArrayBuffer} theirEphemeralPublicKey - the receiving ephemeral/ratchet key
     * @param {ArrayBuffer} ourEphemeralPrivateKey - our current ephemeral/ratchet key
     * @return {Promise.<Object, Error>} the next root and chain keys
     */
    this.deriveNextRootKeyAndChain = co.wrap(function*(rootKey, theirEphemeralPublicKey, ourEphemeralPrivateKey) {
        var sharedSecret = yield crypto.calculateAgreement(theirEphemeralPublicKey, ourEphemeralPrivateKey);
        var derivedSecretBytes = yield hkdf.deriveSecretsWithSalt(sharedSecret, rootKey, whisperRatchet,
            ProtocolConstants.rootKeyByteCount + ProtocolConstants.chainKeyByteCount);
        return {
            rootKey: derivedSecretBytes.slice(0, ProtocolConstants.rootKeyByteCount),
            chain: new Chain(derivedSecretBytes.slice(ProtocolConstants.rootKeyByteCount))
        };
    });

    //
    /**
     * Derive the next sub ratchet state from the previous state.
     * <p>
     * This method "clicks" the hash iteration ratchet forwards.
     *
     * @method
     * @param {Chain} chain
     * @return {Promise.<void, Error>}
     */
    this.clickSubRatchet = co.wrap(function*(chain) {
        chain.index++;
        chain.key = yield deriveNextChainKey(chain.key);
    });

    /**
     * Derive the message keys from the chain key.
     *
     * @method
     * @param {ArrayBuffer} chainKey
     * @return {Promise.<object, Error>} an object containing the message keys.
     */
    this.deriveMessageKeys = co.wrap(function*(chainKey) {
        var messageKey = yield deriveMessageKey(chainKey);
        var keyMaterialBytes = yield hkdf.deriveSecrets(messageKey, whisperMessageKeys,
            ProtocolConstants.cipherKeyByteCount + ProtocolConstants.macKeyByteCount + ProtocolConstants.ivByteCount);
        var cipherKeyBytes = keyMaterialBytes.slice(0, ProtocolConstants.cipherKeyByteCount);
        var macKeyBytes = keyMaterialBytes.slice(ProtocolConstants.cipherKeyByteCount,
            ProtocolConstants.cipherKeyByteCount + ProtocolConstants.macKeyByteCount);
        var ivBytes = keyMaterialBytes.slice(ProtocolConstants.cipherKeyByteCount + ProtocolConstants.macKeyByteCount);
        return {
            cipherKey: cipherKeyBytes,
            macKey: macKeyBytes,
            iv: ivBytes
        };
    });

    var hmacByte = co.wrap(function*(key, byte) {
        return yield crypto.hmac(key, ArrayBufferUtils.fromByte(byte));
    });

    var deriveMessageKey = co.wrap(function*(chainKey) {
        return yield hmacByte(chainKey, messageKeySeed);
    });

    var deriveNextChainKey = co.wrap(function*(chainKey) {
        return yield hmacByte(chainKey, chainKeySeed);
    });
}
function Ratchet(crypto) {
    const self = this;

    const hkdf = new HKDF(crypto);

    /**
     * Derive the main and sub ratchet states from the shared secrets derived from the handshake.
     *
     * @method
     * @param {number} sessionVersion
     * @param {Array.<ArrayBuffer>} agreements - an array of ArrayBuffers containing the shared secrets
     * @return {Promise.<Object, Error>} the root and chain keys
     */
    this.deriveInitialRootKeyAndChain = co.wrap(function*(sessionVersion, agreements) {
        var secrets = [];
        if (sessionVersion >= 3) {
            secrets.push(discontinuityBytes);
        }
        secrets = secrets.concat(agreements);

        var masterSecret = ArrayBufferUtils.concat(secrets);
        var derivedSecret = yield hkdf.deriveSecrets(masterSecret, whisperText,
            ProtocolConstants.rootKeyByteCount + ProtocolConstants.chainKeyByteCount);
        return {
            rootKey: derivedSecret.slice(0, ProtocolConstants.rootKeyByteCount),
            chain: new Chain(derivedSecret.slice(ProtocolConstants.rootKeyByteCount))
        };
    });

    /**
     * Derive the next main and sub ratchet states from the previous state.
     * <p>
     * This method "clicks" the Diffie-Hellman ratchet forwards.
     *
     * @method
     * @param {ArrayBuffer} rootKey - the current root key
     * @param {ArrayBuffer} theirEphemeralPublicKey - the receiving ephemeral/ratchet key
     * @param {ArrayBuffer} ourEphemeralPrivateKey - our current ephemeral/ratchet key
     * @return {Promise.<Object, Error>} the next root and chain keys
     */
    this.deriveNextRootKeyAndChain = co.wrap(function*(rootKey, theirEphemeralPublicKey, ourEphemeralPrivateKey) {
        var sharedSecret = yield crypto.calculateAgreement(theirEphemeralPublicKey, ourEphemeralPrivateKey);
        var derivedSecretBytes = yield hkdf.deriveSecretsWithSalt(sharedSecret, rootKey, whisperRatchet,
            ProtocolConstants.rootKeyByteCount + ProtocolConstants.chainKeyByteCount);
        return {
            rootKey: derivedSecretBytes.slice(0, ProtocolConstants.rootKeyByteCount),
            chain: new Chain(derivedSecretBytes.slice(ProtocolConstants.rootKeyByteCount))
        };
    });

    //
    /**
     * Derive the next sub ratchet state from the previous state.
     * <p>
     * This method "clicks" the hash iteration ratchet forwards.
     *
     * @method
     * @param {Chain} chain
     * @return {Promise.<void, Error>}
     */
    this.clickSubRatchet = co.wrap(function*(chain) {
        chain.index++;
        chain.key = yield deriveNextChainKey(chain.key);
    });

    /**
     * Derive the message keys from the chain key.
     *
     * @method
     * @param {ArrayBuffer} chainKey
     * @return {Promise.<object, Error>} an object containing the message keys.
     */
    this.deriveMessageKeys = co.wrap(function*(chainKey) {
        var messageKey = yield deriveMessageKey(chainKey);
        var keyMaterialBytes = yield hkdf.deriveSecrets(messageKey, whisperMessageKeys,
            ProtocolConstants.cipherKeyByteCount + ProtocolConstants.macKeyByteCount + ProtocolConstants.ivByteCount);
        var cipherKeyBytes = keyMaterialBytes.slice(0, ProtocolConstants.cipherKeyByteCount);
        var macKeyBytes = keyMaterialBytes.slice(ProtocolConstants.cipherKeyByteCount,
            ProtocolConstants.cipherKeyByteCount + ProtocolConstants.macKeyByteCount);
        var ivBytes = keyMaterialBytes.slice(ProtocolConstants.cipherKeyByteCount + ProtocolConstants.macKeyByteCount);
        return {
            cipherKey: cipherKeyBytes,
            macKey: macKeyBytes,
            iv: ivBytes
        };
    });

    var hmacByte = co.wrap(function*(key, byte) {
        return yield crypto.hmac(key, ArrayBufferUtils.fromByte(byte));
    });

    var deriveMessageKey = co.wrap(function*(chainKey) {
        return yield hmacByte(chainKey, messageKeySeed);
    });

    var deriveNextChainKey = co.wrap(function*(chainKey) {
        return yield hmacByte(chainKey, chainKeySeed);
    });
}
function Ratchet ( crypto ) { const self = this ; const hkdf = new HKDF ( crypto ) ; /**
     * Derive the main and sub ratchet states from the shared secrets derived from the handshake.
     *
     * @method
     * @param {number} sessionVersion
     * @param {Array.<ArrayBuffer>} agreements - an array of ArrayBuffers containing the shared secrets
     * @return {Promise.<Object, Error>} the root and chain keys
     */ this . deriveInitialRootKeyAndChain = co . wrap ( function * ( sessionVersion , agreements ) { var secrets = [ ] ; if ( sessionVersion >= 3 ) { secrets . push ( discontinuityBytes ) ; } secrets = secrets . concat ( agreements ) ; var masterSecret = ArrayBufferUtils . concat ( secrets ) ; var derivedSecret = yield hkdf . deriveSecrets ( masterSecret , whisperText , ProtocolConstants . rootKeyByteCount + ProtocolConstants . chainKeyByteCount ) ; return { rootKey : derivedSecret . slice ( 0 , ProtocolConstants . rootKeyByteCount ) , chain : new Chain ( derivedSecret . slice ( ProtocolConstants . rootKeyByteCount ) ) } ; } ) ; /**
     * Derive the next main and sub ratchet states from the previous state.
     * <p>
     * This method "clicks" the Diffie-Hellman ratchet forwards.
     *
     * @method
     * @param {ArrayBuffer} rootKey - the current root key
     * @param {ArrayBuffer} theirEphemeralPublicKey - the receiving ephemeral/ratchet key
     * @param {ArrayBuffer} ourEphemeralPrivateKey - our current ephemeral/ratchet key
     * @return {Promise.<Object, Error>} the next root and chain keys
     */ this . deriveNextRootKeyAndChain = co . wrap ( function * ( rootKey , theirEphemeralPublicKey , ourEphemeralPrivateKey ) { var sharedSecret = yield crypto . calculateAgreement ( theirEphemeralPublicKey , ourEphemeralPrivateKey ) ; var derivedSecretBytes = yield hkdf . deriveSecretsWithSalt ( sharedSecret , rootKey , whisperRatchet , ProtocolConstants . rootKeyByteCount + ProtocolConstants . chainKeyByteCount ) ; return { rootKey : derivedSecretBytes . slice ( 0 , ProtocolConstants . rootKeyByteCount ) , chain : new Chain ( derivedSecretBytes . slice ( ProtocolConstants . rootKeyByteCount ) ) } ; } ) ; // /**
     * Derive the next sub ratchet state from the previous state.
     * <p>
     * This method "clicks" the hash iteration ratchet forwards.
     *
     * @method
     * @param {Chain} chain
     * @return {Promise.<void, Error>}
     */ this . clickSubRatchet = co . wrap ( function * ( chain ) { chain . index ++ ; chain . key = yield deriveNextChainKey ( chain . key ) ; } ) ; /**
     * Derive the message keys from the chain key.
     *
     * @method
     * @param {ArrayBuffer} chainKey
     * @return {Promise.<object, Error>} an object containing the message keys.
     */ this . deriveMessageKeys = co . wrap ( function * ( chainKey ) { var messageKey = yield deriveMessageKey ( chainKey ) ; var keyMaterialBytes = yield hkdf . deriveSecrets ( messageKey , whisperMessageKeys , ProtocolConstants . cipherKeyByteCount + ProtocolConstants . macKeyByteCount + ProtocolConstants . ivByteCount ) ; var cipherKeyBytes = keyMaterialBytes . slice ( 0 , ProtocolConstants . cipherKeyByteCount ) ; var macKeyBytes = keyMaterialBytes . slice ( ProtocolConstants . cipherKeyByteCount , ProtocolConstants . cipherKeyByteCount + ProtocolConstants . macKeyByteCount ) ; var ivBytes = keyMaterialBytes . slice ( ProtocolConstants . cipherKeyByteCount + ProtocolConstants . macKeyByteCount ) ; return { cipherKey : cipherKeyBytes , macKey : macKeyBytes , iv : ivBytes } ; } ) ; var hmacByte = co . wrap ( function * ( key , byte ) { return yield crypto . hmac ( key , ArrayBufferUtils . fromByte ( byte ) ) ; } ) ; var deriveMessageKey = co . wrap ( function * ( chainKey ) { return yield hmacByte ( chainKey , messageKeySeed ) ; } ) ; var deriveNextChainKey = co . wrap ( function * ( chainKey ) { return yield hmacByte ( chainKey , chainKeySeed ) ; } ) ; }
A utility class for performing the Axolotl ratcheting.

@param {Crypto} crypto
@constructor
A utility class for performing the Axolotl ratcheting .