summaryrefslogtreecommitdiff
path: root/libs/libsignal/src/sender_key.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libsignal/src/sender_key.c')
-rw-r--r--libs/libsignal/src/sender_key.c285
1 files changed, 285 insertions, 0 deletions
diff --git a/libs/libsignal/src/sender_key.c b/libs/libsignal/src/sender_key.c
new file mode 100644
index 0000000000..31d7499499
--- /dev/null
+++ b/libs/libsignal/src/sender_key.c
@@ -0,0 +1,285 @@
+#include "sender_key.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "hkdf.h"
+#include "signal_protocol_internal.h"
+
+#define HASH_OUTPUT_SIZE 32
+
+struct sender_message_key {
+ signal_type_base base;
+ uint32_t iteration;
+ signal_buffer *iv;
+ signal_buffer *cipher_key;
+ signal_buffer *seed;
+ signal_context *global_context;
+};
+
+struct sender_chain_key {
+ signal_type_base base;
+ uint32_t iteration;
+ signal_buffer *chain_key;
+ signal_context *global_context;
+};
+
+static int sender_chain_key_get_derivative(signal_buffer **derivative, uint8_t seed, signal_buffer *key,
+ signal_context *global_context);
+
+int sender_message_key_create(sender_message_key **key,
+ uint32_t iteration, signal_buffer *seed,
+ signal_context *global_context)
+{
+ sender_message_key *result = 0;
+ int ret = 0;
+ ssize_t ret_size = 0;
+ hkdf_context *kdf = 0;
+ static const char info_material[] = "WhisperGroup";
+ uint8_t salt[HASH_OUTPUT_SIZE];
+ uint8_t *derivative = 0;
+
+ assert(global_context);
+
+ if(!seed) {
+ return SG_ERR_INVAL;
+ }
+
+ memset(salt, 0, sizeof(salt));
+
+ result = malloc(sizeof(sender_message_key));
+ if(!result) {
+ return SG_ERR_NOMEM;
+ }
+
+ SIGNAL_INIT(result, sender_message_key_destroy);
+
+ ret = hkdf_create(&kdf, 3, global_context);
+ if(ret < 0) {
+ goto complete;
+ }
+
+ ret_size = hkdf_derive_secrets(kdf, &derivative,
+ signal_buffer_data(seed), signal_buffer_len(seed),
+ salt, sizeof(salt),
+ (uint8_t *)info_material, sizeof(info_material) - 1, 48);
+ if(ret_size != 48) {
+ ret = (ret_size < 0) ? (int)ret_size : SG_ERR_UNKNOWN;
+ signal_log(global_context, SG_LOG_WARNING, "hkdf_derive_secrets failed");
+ goto complete;
+ }
+
+ result->iteration = iteration;
+
+ result->seed = signal_buffer_copy(seed);
+ if(!result->seed) {
+ ret = SG_ERR_NOMEM;
+ goto complete;
+ }
+
+ result->iv = signal_buffer_create(derivative, 16);
+ if(!result->iv) {
+ ret = SG_ERR_NOMEM;
+ goto complete;
+ }
+
+ result->cipher_key = signal_buffer_create(derivative + 16, 32);
+ if(!result->cipher_key) {
+ ret = SG_ERR_NOMEM;
+ goto complete;
+ }
+
+ result->global_context = global_context;
+
+complete:
+ SIGNAL_UNREF(kdf);
+ if(derivative) {
+ free(derivative);
+ }
+ if(ret < 0) {
+ SIGNAL_UNREF(result);
+ }
+ else {
+ ret = 0;
+ *key = result;
+ }
+ return ret;
+}
+
+uint32_t sender_message_key_get_iteration(sender_message_key *key)
+{
+ assert(key);
+ return key->iteration;
+}
+
+signal_buffer *sender_message_key_get_iv(sender_message_key *key)
+{
+ assert(key);
+ return key->iv;
+}
+
+signal_buffer *sender_message_key_get_cipher_key(sender_message_key *key)
+{
+ assert(key);
+ return key->cipher_key;
+}
+
+signal_buffer *sender_message_key_get_seed(sender_message_key *key)
+{
+ assert(key);
+ return key->seed;
+}
+
+void sender_message_key_destroy(signal_type_base *type)
+{
+ sender_message_key *key = (sender_message_key *)type;
+ signal_buffer_bzero_free(key->iv);
+ signal_buffer_bzero_free(key->cipher_key);
+ signal_buffer_bzero_free(key->seed);
+ free(key);
+}
+
+int sender_chain_key_create(sender_chain_key **key,
+ uint32_t iteration, signal_buffer *chain_key,
+ signal_context *global_context)
+{
+ sender_chain_key *result = 0;
+ int ret = 0;
+
+ assert(global_context);
+
+ if(!chain_key) {
+ return SG_ERR_INVAL;
+ }
+
+ result = malloc(sizeof(sender_chain_key));
+ if(!result) {
+ return SG_ERR_NOMEM;
+ }
+
+ SIGNAL_INIT(result, sender_chain_key_destroy);
+
+ result->iteration = iteration;
+
+ result->chain_key = signal_buffer_copy(chain_key);
+ if(!result->chain_key) {
+ ret = SG_ERR_NOMEM;
+ goto complete;
+ }
+
+ result->global_context = global_context;
+
+complete:
+ if(ret < 0) {
+ SIGNAL_UNREF(result);
+ }
+ else {
+ ret = 0;
+ *key = result;
+ }
+ return ret;
+}
+
+uint32_t sender_chain_key_get_iteration(sender_chain_key *key)
+{
+ assert(key);
+ return key->iteration;
+}
+
+int sender_chain_key_create_message_key(sender_chain_key *key, sender_message_key **message_key)
+{
+ static const uint8_t MESSAGE_KEY_SEED = 0x01;
+ int ret = 0;
+ signal_buffer *derivative = 0;
+ sender_message_key *result = 0;
+
+ assert(key);
+
+ ret = sender_chain_key_get_derivative(&derivative, MESSAGE_KEY_SEED, key->chain_key, key->global_context);
+ if(ret < 0) {
+ goto complete;
+ }
+
+ ret = sender_message_key_create(&result, key->iteration, derivative, key->global_context);
+
+complete:
+ signal_buffer_free(derivative);
+ if(ret >= 0) {
+ ret = 0;
+ *message_key = result;
+ }
+ return ret;
+}
+
+int sender_chain_key_create_next(sender_chain_key *key, sender_chain_key **next_key)
+{
+ static const uint8_t CHAIN_KEY_SEED = 0x02;
+ int ret = 0;
+ signal_buffer *derivative = 0;
+ sender_chain_key *result = 0;
+
+ assert(key);
+
+ ret = sender_chain_key_get_derivative(&derivative, CHAIN_KEY_SEED, key->chain_key, key->global_context);
+ if(ret < 0) {
+ goto complete;
+ }
+
+ ret = sender_chain_key_create(&result, key->iteration + 1, derivative, key->global_context);
+
+complete:
+ signal_buffer_free(derivative);
+ if(ret >= 0) {
+ ret = 0;
+ *next_key = result;
+ }
+ return ret;
+}
+
+signal_buffer *sender_chain_key_get_seed(sender_chain_key *key)
+{
+ assert(key);
+ return key->chain_key;
+}
+
+void sender_chain_key_destroy(signal_type_base *type)
+{
+ sender_chain_key *key = (sender_chain_key *)type;
+ signal_buffer_bzero_free(key->chain_key);
+ free(key);
+}
+
+int sender_chain_key_get_derivative(signal_buffer **derivative, uint8_t seed, signal_buffer *key,
+ signal_context *global_context)
+{
+ int result = 0;
+ signal_buffer *output_buffer = 0;
+ void *hmac_context = 0;
+
+ result = signal_hmac_sha256_init(global_context, &hmac_context,
+ signal_buffer_data(key), signal_buffer_len(key));
+ if(result < 0) {
+ goto complete;
+ }
+
+ result = signal_hmac_sha256_update(global_context, hmac_context, &seed, sizeof(seed));
+ if(result < 0) {
+ goto complete;
+ }
+
+ result = signal_hmac_sha256_final(global_context, hmac_context, &output_buffer);
+ if(result < 0) {
+ goto complete;
+ }
+
+complete:
+ signal_hmac_sha256_cleanup(global_context, hmac_context);
+
+ if(result < 0) {
+ signal_buffer_free(output_buffer);
+ }
+ else {
+ *derivative = output_buffer;
+ }
+ return result;
+}