diff --git a/examples/desktop/repl/src/main.c b/examples/desktop/repl/src/main.c index c1a1749d..db75d710 100644 --- a/examples/desktop/repl/src/main.c +++ b/examples/desktop/repl/src/main.c @@ -217,4 +217,4 @@ static int start_repl_loop(atclient *atclient, repl_args *repl_args) { atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_INFO, "Exiting REPL loop...\n"); ret = 0; exit: { return ret; } -} \ No newline at end of file +} diff --git a/packages/atclient/include/atclient/atkeys.h b/packages/atclient/include/atclient/atkeys.h index 8eedd767..c3271dec 100644 --- a/packages/atclient/include/atclient/atkeys.h +++ b/packages/atclient/include/atclient/atkeys.h @@ -162,4 +162,8 @@ int atclient_atkeys_populate_from_path(atclient_atkeys *atkeys, const char *path */ int atclient_atkeys_populate_from_string(atclient_atkeys *atkeys, const char *file_string); +int atclient_atkeys_write_to_atkeys_file(atclient_atkeys *atkeys, atclient_atkeys_file *atkeys_file); + +int atclient_atkeys_write_to_path(atclient_atkeys *atkeys, const char *path); + #endif diff --git a/packages/atclient/include/atclient/atkeys_file.h b/packages/atclient/include/atclient/atkeys_file.h index 1a3c2949..5426f68e 100644 --- a/packages/atclient/include/atclient/atkeys_file.h +++ b/packages/atclient/include/atclient/atkeys_file.h @@ -63,6 +63,14 @@ int atclient_atkeys_file_from_path(atclient_atkeys_file *atkeys_file, const char */ int atclient_atkeys_file_from_string(atclient_atkeys_file *atkeys_file, const char *file_string); +/** + * @brief Write the struct to a file. + * + * @param atkeys_file the struct to be written to the file, assumed to be NON-NULL and initialized with atclient_atkeys_file_init + * @param path Example "$HOME/.atsign/keys/@alice_key.atKeys" + */ +int atclient_atkeys_file_write_to_path(atclient_atkeys_file *atkeys_file, const char *path); + /** * @brief Free the struct of any memory that was allocated during its lifetime * diff --git a/packages/atclient/src/atkeys.c b/packages/atclient/src/atkeys.c index 22fd7d5b..efd732bf 100644 --- a/packages/atclient/src/atkeys.c +++ b/packages/atclient/src/atkeys.c @@ -1,4 +1,5 @@ #include "atclient/atkeys.h" +#include "atclient/atkeys_file.h" #include "atlogger/atlogger.h" #include #include @@ -812,6 +813,197 @@ exit: { } } +int atclient_atkeys_write_to_atkeys_file(atclient_atkeys *atkeys, atclient_atkeys_file *atkeys_file) { + int ret = 1; + + /* + * 1. Validate arguments + */ + if (atkeys == NULL) { + ret = 1; + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "atkeys is NULL\n"); + return ret; + } + + // mandatory field that constitutes an atSign's atkeys + if (!atclient_atkeys_is_encrypt_private_key_base64_initialized(atkeys)) { + ret = 1; + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "encrypt private key is not initialized\n"); + return ret; + } + + // mandatory field that constitutes an atSign's atkeys + if (!atclient_atkeys_is_encrypt_public_key_base64_initialized(atkeys)) { + ret = 1; + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "encrypt public key is not initialized\n"); + return ret; + } + + // mandatory field that constitutes an atSign's atkeys + if (!atclient_atkeys_is_pkam_private_key_base64_initialized(atkeys)) { + ret = 1; + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "pkam private key is not initialized\n"); + return ret; + } + + // mandatory field that constitutes an atSign's atkeys + if (!atclient_atkeys_is_pkam_public_key_base64_initialized(atkeys)) { + ret = 1; + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "pkam public key is not initialized\n"); + return ret; + } + + // mandatory field that constitutes an atSign's atkeys + if (!atclient_atkeys_is_self_encryption_key_base64_initialized(atkeys)) { + ret = 1; + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "self encryption key is not initialized\n"); + return ret; + } + + if (atkeys_file == NULL) { + ret = 1; + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "atkeys_file is NULL\n"); + return ret; + } + + /* + * 2. Variables + */ + const size_t iv_size = ATCHOPS_IV_BUFFER_SIZE; + unsigned char iv[iv_size]; + + const size_t self_encryption_key_size = ATCHOPS_AES_256 / 8; + unsigned char self_encryption_key[self_encryption_key_size]; + + const size_t rsa_key_encrypted_size = atchops_base64_encoded_size( + strlen(atkeys->pkam_private_key_base64)); // use private key as the largest buffer size + unsigned char rsa_key_encrypted[rsa_key_encrypted_size]; + size_t rsa_key_encrypted_len = 0; + + /* + * 3. Prepare self encryption key for use + */ + if ((ret = atchops_base64_decode((unsigned char *)atkeys->self_encryption_key_base64, + strlen(atkeys->self_encryption_key_base64), self_encryption_key, + self_encryption_key_size, NULL)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "base64 decode self encryption key: %d\n", ret); + goto exit; + } + + /* + * 4. Encrypt and write to atkeys file + */ + + // 4a. pkam public key + memset(iv, 0, sizeof(unsigned char) * iv_size); // Use legacy IV + memset(rsa_key_encrypted, 0, sizeof(unsigned char) * rsa_key_encrypted_size); + if ((ret = atchops_aes_ctr_encrypt(self_encryption_key, ATCHOPS_AES_256, iv, + (unsigned char *)atkeys->pkam_public_key_base64, + strlen(atkeys->pkam_public_key_base64), rsa_key_encrypted, rsa_key_encrypted_size, + &rsa_key_encrypted_len)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "encrypt pkam public key: %d\n", ret); + goto exit; + } + + if ((ret = atclient_atkeys_file_set_aes_pkam_public_key_str(atkeys_file, (const char *)rsa_key_encrypted, + rsa_key_encrypted_len)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "set aes pkam public key str: %d\n", ret); + goto exit; + } + + // 4b. pkam private key + memset(iv, 0, sizeof(unsigned char) * iv_size); // Use legacy IV + memset(rsa_key_encrypted, 0, sizeof(unsigned char) * rsa_key_encrypted_size); + if ((ret = atchops_aes_ctr_encrypt(self_encryption_key, ATCHOPS_AES_256, iv, + (unsigned char *)atkeys->pkam_private_key_base64, + strlen(atkeys->pkam_private_key_base64), rsa_key_encrypted, rsa_key_encrypted_size, + &rsa_key_encrypted_len)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "encrypt pkam private key: %d\n", ret); + goto exit; + } + + if ((ret = atclient_atkeys_file_set_aes_pkam_private_key_str(atkeys_file, (const char *)rsa_key_encrypted, + rsa_key_encrypted_len)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "set aes pkam private key str: %d\n", ret); + goto exit; + } + + // 4c. encrypt public key + memset(iv, 0, sizeof(unsigned char) * iv_size); // Use legacy IV + memset(rsa_key_encrypted, 0, sizeof(unsigned char) * rsa_key_encrypted_size); + if ((ret = atchops_aes_ctr_encrypt(self_encryption_key, ATCHOPS_AES_256, iv, + (unsigned char *)atkeys->encrypt_public_key_base64, + strlen(atkeys->encrypt_public_key_base64), rsa_key_encrypted, + rsa_key_encrypted_size, &rsa_key_encrypted_len)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "encrypt encrypt public key: %d\n", ret); + goto exit; + } + + if ((ret = atclient_atkeys_file_set_aes_encrypt_public_key_str(atkeys_file, (const char *)rsa_key_encrypted, + rsa_key_encrypted_len)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "set aes encrypt public key str: %d\n", ret); + goto exit; + } + + // 4d. encrypt private key + memset(iv, 0, sizeof(unsigned char) * iv_size); // Use legacy IV + memset(rsa_key_encrypted, 0, sizeof(unsigned char) * rsa_key_encrypted_size); + if ((ret = atchops_aes_ctr_encrypt(self_encryption_key, ATCHOPS_AES_256, iv, + (unsigned char *)atkeys->encrypt_private_key_base64, + strlen(atkeys->encrypt_private_key_base64), rsa_key_encrypted, + rsa_key_encrypted_size, &rsa_key_encrypted_len)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "encrypt encrypt private key: %d\n", ret); + goto exit; + } + + if ((ret = atclient_atkeys_file_set_aes_encrypt_private_key_str(atkeys_file, (const char *)rsa_key_encrypted, + rsa_key_encrypted_len)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "set aes encrypt private key str: %d\n", ret); + goto exit; + } + + // 4e. self encryption key + if ((ret = atclient_atkeys_file_set_self_encryption_key_str(atkeys_file, atkeys->self_encryption_key_base64)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "set self encryption key str: %d\n", ret); + goto exit; + } + + // 4f. enrollment id (optional) + if (atclient_atkeys_is_enrollment_id_initialized(atkeys)) { + if ((ret = atclient_atkeys_file_set_enrollment_id_str(atkeys_file, atkeys->enrollment_id)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "set enrollment id str: %d\n", ret); + goto exit; + } + } + + ret = 0; + +exit: { return ret; } +} + +int atclient_atkeys_write_to_path(atclient_atkeys *atkeys, const char *path) { + int ret = 1; + + atclient_atkeys_file atkeys_file; + atclient_atkeys_file_init(&atkeys_file); + + if ((ret = atclient_atkeys_write_to_atkeys_file(atkeys, &atkeys_file)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "atclient_atkeys_write_to_atkeys_file: %d\n", ret); + goto exit; + } + + if ((ret = atclient_atkeys_file_write_to_path(&atkeys_file, path)) != 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "atclient_atkeys_file_to_path: %d\n", ret); + goto exit; + } + + ret = 0; +exit: { + atclient_atkeys_file_free(&atkeys_file); + return ret; +} +} + static bool is_pkam_public_key_base64_initialized(atclient_atkeys *atkeys) { return atkeys->_initialized_fields[ATCLIENT_ATKEYS_PKAM_PUBLIC_KEY_INDEX] & ATCLIENT_ATKEYS_PKAM_PUBLIC_KEY_INITIALIZED; diff --git a/packages/atclient/src/atkeys_file.c b/packages/atclient/src/atkeys_file.c index d37724b0..eb65de4e 100644 --- a/packages/atclient/src/atkeys_file.c +++ b/packages/atclient/src/atkeys_file.c @@ -46,7 +46,9 @@ static int set_self_encryption_key_str(atclient_atkeys_file *atkeys_file, const static int set_enrollment_id_str(atclient_atkeys_file *atkeys_file, const char *enrollment_id_str, const size_t enrollment_id_str_len); -void atclient_atkeys_file_init(atclient_atkeys_file *atkeys_file) { memset(atkeys_file, 0, sizeof(atclient_atkeys_file)); } +void atclient_atkeys_file_init(atclient_atkeys_file *atkeys_file) { + memset(atkeys_file, 0, sizeof(atclient_atkeys_file)); +} int atclient_atkeys_file_from_path(atclient_atkeys_file *atkeys_file, const char *path) { int ret = 1; @@ -152,6 +154,115 @@ exit: { } } +int atclient_atkeys_file_write_to_path(atclient_atkeys_file *atkeys_file, const char *path) { + int ret = 1; + + /* + * 1. Validate arguments + */ + if (atkeys_file == NULL) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "atkeys_file is NULL\n"); + return ret; + } + + if (!atclient_atkeysfile_is_aes_encrypt_private_key_str_initialized(atkeys_file)) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "aes_encrypt_private_key_str is not initialized\n"); + return ret; + } + + if (!atclient_atkeysfile_is_aes_encrypt_public_key_str_initialized(atkeys_file)) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "aes_encrypt_public_key_str is not initialized\n"); + return ret; + } + + if (!atclient_atkeysfile_is_aes_pkam_private_key_str_initialized(atkeys_file)) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "aes_pkam_private_key_str is not initialized\n"); + return ret; + } + + if (!atclient_atkeysfile_is_aes_pkam_public_key_str_initialized(atkeys_file)) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "aes_pkam_public_key_str is not initialized\n"); + return ret; + } + + if (!atclient_atkeysfile_is_self_encryption_key_str_initialized(atkeys_file)) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "self_encryption_key_str is not initialized\n"); + return ret; + } + + if (path == NULL) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "path is NULL\n"); + return ret; + } + + /* + * 2. Variables + */ + + cJSON *root = NULL; // free later + char *json_str = NULL; // free later + + root = cJSON_CreateObject(); + if (root == NULL) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "cJSON_CreateObject failed\n"); + goto exit; + } + + if (is_aes_pkam_public_key_str_initialized(atkeys_file)) { + cJSON_AddStringToObject(root, "aesPkamPublicKey", atkeys_file->aes_pkam_public_key_str); + } + + if (is_aes_pkam_private_key_str_initialized(atkeys_file)) { + cJSON_AddStringToObject(root, "aesPkamPrivateKey", atkeys_file->aes_pkam_private_key_str); + } + + if (is_aes_encrypt_public_key_str_initialized(atkeys_file)) { + cJSON_AddStringToObject(root, "aesEncryptPublicKey", atkeys_file->aes_encrypt_public_key_str); + } + + if (is_aes_encrypt_private_key_str_initialized(atkeys_file)) { + cJSON_AddStringToObject(root, "aesEncryptPrivateKey", atkeys_file->aes_encrypt_private_key_str); + } + + if (is_self_encryption_key_str_initialized(atkeys_file)) { + cJSON_AddStringToObject(root, "selfEncryptionKey", atkeys_file->self_encryption_key_str); + } + + if (is_enrollment_id_str_initialized(atkeys_file)) { + cJSON_AddStringToObject(root, "enrollmentId", atkeys_file->enrollment_id_str); + } + + json_str = cJSON_Print(root); + if (json_str == NULL) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "cJSON_Print failed\n"); + goto exit; + } + + FILE *file = fopen(path, "w"); + if (file == NULL) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "fopen failed\n"); + goto exit; + } + + const size_t bytes_written = fwrite(json_str, 1, strlen(json_str), file); + fclose(file); + if (bytes_written == 0) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "fwrite failed\n"); + goto exit; + } + + ret = 0; +exit: { + if(json_str != NULL) { + free(json_str); + } + if(root != NULL) { + cJSON_Delete(root); + } + return ret; +} +} + void atclient_atkeys_file_free(atclient_atkeys_file *atkeys_file) { unset_aes_pkam_public_key_str(atkeys_file); unset_aes_pkam_private_key_str(atkeys_file); @@ -186,8 +297,8 @@ bool atclient_atkeys_file_is_enrollment_id_str_initialized(atclient_atkeys_file } int atclient_atkeys_file_set_aes_pkam_public_key_str(atclient_atkeys_file *atkeys_file, - const char *aes_pkam_public_key_str, - const size_t aes_pkam_public_key_str_len) { + const char *aes_pkam_public_key_str, + const size_t aes_pkam_public_key_str_len) { int ret = 1; if (aes_pkam_public_key_str == NULL) { @@ -218,8 +329,8 @@ exit: { return ret; } } int atclient_atkeys_file_set_aes_pkam_private_key_str(atclient_atkeys_file *atkeys_file, - const char *aes_pkam_private_key_str, - const size_t aes_pkam_private_key_str_len) { + const char *aes_pkam_private_key_str, + const size_t aes_pkam_private_key_str_len) { int ret = 1; if (aes_pkam_private_key_str == NULL) { @@ -250,8 +361,8 @@ exit: { return ret; } } int atclient_atkeys_file_set_aes_encrypt_public_key_str(atclient_atkeys_file *atkeys_file, - const char *aes_encrypt_public_key_str, - const size_t aes_encrypt_public_key_str_len) { + const char *aes_encrypt_public_key_str, + const size_t aes_encrypt_public_key_str_len) { int ret = 1; if (aes_encrypt_public_key_str == NULL) { @@ -283,8 +394,8 @@ exit: { return ret; } } int atclient_atkeys_file_set_aes_encrypt_private_key_str(atclient_atkeys_file *atkeys_file, - const char *aes_encrypt_private_key_str, - const size_t aes_encrypt_private_key_str_len) { + const char *aes_encrypt_private_key_str, + const size_t aes_encrypt_private_key_str_len) { int ret = 1; if (aes_encrypt_private_key_str == NULL) { @@ -316,8 +427,8 @@ exit: { return ret; } } int atclient_atkeys_file_set_self_encryption_key_str(atclient_atkeys_file *atkeys_file, - const char *self_encryption_key_str, - const size_t self_encryption_key_str_len) { + const char *self_encryption_key_str, + const size_t self_encryption_key_str_len) { int ret = 1; if (self_encryption_key_str == NULL) { @@ -348,7 +459,7 @@ exit: { return ret; } } int atclient_atkeys_file_set_enrollment_id_str(atclient_atkeys_file *atkeys_file, const char *enrollment_id_str, - const size_t enrollment_id_str_len) { + const size_t enrollment_id_str_len) { int ret = 1; if (enrollment_id_str == NULL) {