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; } }