Example #1
0
int hkdf_derive_secrets_nosalt(unsigned char* out, enum hkdf_msg_ver_t offset,
		const unsigned char* in, const size_t inlen,
		const unsigned char* info, const size_t infolen,
		const unsigned outlen)
{
	unsigned char salt[HASH_OUTSZ];
	sodium_memzero(salt, sizeof salt);
	return hkdf_derive_secrets(out, offset, in, inlen, salt, sizeof salt, info, infolen, outlen);
}
int ratchet_root_key_create_chain(ratchet_root_key *root_key,
        ratchet_root_key **new_root_key, ratchet_chain_key **new_chain_key,
        ec_public_key *their_ratchet_key,
        ec_private_key *our_ratchet_key_private)
{
    static const char key_info[] = "WhisperRatchet";
    int result = 0;
    ssize_t result_size = 0;
    uint8_t *shared_secret = 0;
    size_t shared_secret_len = 0;
    uint8_t *derived_secret = 0;
    ratchet_root_key *new_root_key_result = 0;
    ratchet_chain_key *new_chain_key_result = 0;

    if(!their_ratchet_key || !our_ratchet_key_private) {
        return SG_ERR_INVAL;
    }

    result = curve_calculate_agreement(&shared_secret, their_ratchet_key, our_ratchet_key_private);
    if(result < 0) {
        signal_log(root_key->global_context, SG_LOG_WARNING, "curve_calculate_agreement failed");
        goto complete;
    }
    shared_secret_len = (size_t)result;

    result_size = hkdf_derive_secrets(root_key->kdf, &derived_secret,
            shared_secret, shared_secret_len,
            root_key->key, root_key->key_len,
            (uint8_t *)key_info, sizeof(key_info) - 1,
            DERIVED_ROOT_SECRETS_SIZE);
    if(result_size < 0) {
        result = (int)result_size;
        signal_log(root_key->global_context, SG_LOG_WARNING, "hkdf_derive_secrets failed");
        goto complete;
    }
    else if(result_size != DERIVED_ROOT_SECRETS_SIZE) {
        result = SG_ERR_UNKNOWN;
        signal_log(root_key->global_context, SG_LOG_WARNING, "hkdf_derive_secrets size mismatch");
        goto complete;
    }

    result = ratchet_root_key_create(&new_root_key_result, root_key->kdf,
            derived_secret, 32,
            root_key->global_context);
    if(result < 0) {
        signal_log(root_key->global_context, SG_LOG_WARNING, "ratchet_root_key_create failed");
        goto complete;
    }

    result = ratchet_chain_key_create(&new_chain_key_result, root_key->kdf,
            derived_secret + 32, 32, 0,
            root_key->global_context);
    if(result < 0) {
        signal_log(root_key->global_context, SG_LOG_WARNING, "ratchet_chain_key_create failed");
        goto complete;
    }

complete:
    if(shared_secret) {
        free(shared_secret);
    }
    if(derived_secret) {
        free(derived_secret);
    }
    if(result < 0) {
        if(new_root_key_result) {
            SIGNAL_UNREF(new_root_key_result);
        }
        if(new_chain_key_result) {
            SIGNAL_UNREF(new_chain_key_result);
        }
        return result;
    }
    else {
        *new_root_key = new_root_key_result;
        *new_chain_key = new_chain_key_result;
        return 0;
    }
}
int ratcheting_session_calculate_derived_keys(ratchet_root_key **root_key, ratchet_chain_key **chain_key,
        uint8_t *secret, size_t secret_len, signal_context *global_context)
{
    int result = 0;
    ssize_t result_size = 0;
    hkdf_context *kdf = 0;
    ratchet_root_key *root_key_result = 0;
    ratchet_chain_key *chain_key_result = 0;
    uint8_t *output = 0;
    uint8_t salt[HASH_OUTPUT_SIZE];
    static const char key_info[] = "WhisperText";

    result = hkdf_create(&kdf, 3, global_context);
    if(result < 0) {
        goto complete;
    }

    memset(salt, 0, sizeof(salt));

    result_size = hkdf_derive_secrets(kdf, &output,
            secret, secret_len,
            salt, sizeof(salt),
            (uint8_t *)key_info, sizeof(key_info) - 1, 64);
    if(result_size != 64) {
        result = SG_ERR_UNKNOWN;
        goto complete;
    }

    result = ratchet_root_key_create(&root_key_result, kdf, output, 32, global_context);
    if(result < 0) {
        goto complete;
    }

    result = ratchet_chain_key_create(&chain_key_result, kdf, output + 32, 32, 0, global_context);
    if(result < 0) {
        goto complete;
    }

complete:
    if(kdf) {
        SIGNAL_UNREF(kdf);
    }
    if(output) {
        free(output);
    }

    if(result < 0) {
        if(root_key_result) {
            SIGNAL_UNREF(root_key_result);
        }
        if(chain_key_result) {
            SIGNAL_UNREF(chain_key_result);
        }
    }
    else {
        *root_key = root_key_result;
        *chain_key = chain_key_result;
    }

    return result;
}
int ratchet_chain_key_get_message_keys(ratchet_chain_key *chain_key, ratchet_message_keys *message_keys)
{
    static const uint8_t message_key_seed = 0x01;
    static const char key_material_seed[] = "WhisperMessageKeys";
    uint8_t salt[HASH_OUTPUT_SIZE];
    int result = 0;
    ssize_t result_size = 0;
    uint8_t *input_key_material = 0;
    size_t input_key_material_len = 0;
    uint8_t *key_material_data = 0;
    size_t key_material_data_len = 0;

    memset(message_keys, 0, sizeof(ratchet_message_keys));

    result_size = ratchet_chain_key_get_base_material(chain_key, &input_key_material, &message_key_seed, sizeof(message_key_seed));
    if(result_size < 0) {
        result = (int)result_size;
        signal_log(chain_key->global_context, SG_LOG_WARNING, "ratchet_chain_key_get_base_material failed");
        goto complete;
    }
    input_key_material_len = (size_t)result_size;

    memset(salt, 0, sizeof(salt));

    result_size = hkdf_derive_secrets(chain_key->kdf,
            &key_material_data,
            input_key_material, input_key_material_len,
            salt, sizeof(salt),
            (uint8_t *)key_material_seed, sizeof(key_material_seed) - 1,
            DERIVED_MESSAGE_SECRETS_SIZE);
    if(result_size < 0) {
        result = (int)result_size;
        signal_log(chain_key->global_context, SG_LOG_WARNING, "hkdf_derive_secrets failed");
        goto complete;
    }
    key_material_data_len = (size_t)result_size;

    if(key_material_data_len != RATCHET_CIPHER_KEY_LENGTH + RATCHET_MAC_KEY_LENGTH + RATCHET_IV_LENGTH) {
        signal_log(chain_key->global_context, SG_LOG_WARNING,
                "key_material_data length mismatch: %d != %d",
                key_material_data_len, (RATCHET_CIPHER_KEY_LENGTH + RATCHET_MAC_KEY_LENGTH + RATCHET_IV_LENGTH));
        result = SG_ERR_UNKNOWN;
        goto complete;
    }

    memcpy(message_keys->cipher_key, key_material_data, RATCHET_CIPHER_KEY_LENGTH);
    memcpy(message_keys->mac_key, key_material_data + RATCHET_CIPHER_KEY_LENGTH, RATCHET_MAC_KEY_LENGTH);
    memcpy(message_keys->iv, key_material_data + RATCHET_CIPHER_KEY_LENGTH + RATCHET_MAC_KEY_LENGTH, RATCHET_IV_LENGTH);
    message_keys->counter = chain_key->index;

complete:
    if(input_key_material) {
        free(input_key_material);
    }
    if(key_material_data) {
        free(key_material_data);
    }
    if(result < 0) {
        return result;
    }
    else {
        return 0;
    }
}