Skip to content

Commit

Permalink
Merge pull request #409 from atsign-foundation/jt/atchops_rsa_generate
Browse files Browse the repository at this point in the history
feat: atchops_rsa_generate
  • Loading branch information
XavierChanth authored Sep 28, 2024
2 parents 13acfe4 + d0cbe62 commit a314efb
Show file tree
Hide file tree
Showing 5 changed files with 372 additions and 17 deletions.
8 changes: 8 additions & 0 deletions packages/atchops/include/atchops/rsa_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ int atchops_rsa_key_public_key_clone(const atchops_rsa_key_public_key *src, atch
*/
int atchops_rsa_key_private_key_clone(const atchops_rsa_key_private_key *src, atchops_rsa_key_private_key *dst);

/**
* @brief Generate a new RSA 2048 key pair (public and private)
*
* @param public_key the public key struct to populate, assumed to be allocated and initialized. initialized via atchops_rsa_key_public_key_init
* @param private_key the private key struct to populate, assumed to be allocated and initialized. initialized via atchops_rsa_key_private_key_init
*/
int atchops_rsa_key_generate(atchops_rsa_key_public_key *public_key, atchops_rsa_key_private_key *private_key);

/**
* @brief Populate a public key struct from a base64 string
*
Expand Down
6 changes: 0 additions & 6 deletions packages/atchops/src/rsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,9 +407,3 @@ exit: {
return ret;
}
}

int atchops_rsa_generate(atchops_rsa_key_public_key *public_key, atchops_rsa_key_private_key *private_key,
const unsigned int key_size) {
// TODO maybe also introduce `enum atchops_rsa_key_size` ?
return 1; // TODO: implement
}
241 changes: 234 additions & 7 deletions packages/atchops/src/rsa_key.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "atchops/rsa_key.h"
#include "atchops/base64.h"
#include "atchops/constants.h"
#include "atchops/mbedtls.h"
#include <atlogger/atlogger.h>
#include <stddef.h>
Expand All @@ -8,8 +9,6 @@

#define TAG "rsa_key"

#define BASE64_DECODED_KEY_BUFFER_SIZE 8192 // the max buffer size of a decoded RSA key

void atchops_rsa_key_public_key_init(atchops_rsa_key_public_key *public_key) {
/*
* 1. Validate arguments
Expand Down Expand Up @@ -177,6 +176,232 @@ int atchops_rsa_key_private_key_clone(const atchops_rsa_key_private_key *src, at
exit: { return ret; }
}

int atchops_rsa_key_generate(atchops_rsa_key_public_key *public_key, atchops_rsa_key_private_key *private_key) {
int ret = 1;

/*
* 1. Validate arguments
*/
if (public_key == NULL) {
ret = 1;
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "public_key is null\n");
return ret;
}

if (private_key == NULL) {
ret = 1;
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "private_key is null\n");
return ret;
}

/*
* 2. Variables
*/
mbedtls_entropy_context entropy;
mbedtls_entropy_init(&entropy);

mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ctr_drbg_init(&ctr_drbg);

mbedtls_pk_context pk;
mbedtls_pk_init(&pk);

const size_t public_key_base64_size = 1024; // 1024 bytes is sufficient size for a 2048 bit RSA key base64 encoded
char public_key_base64[public_key_base64_size];
memset(public_key_base64, 0, sizeof(char) * public_key_base64_size);

const size_t private_key_base64_size = 2048; // 2048 bytes is sufficient size for a 2048 bit RSA key base64 encoded
char private_key_base64[private_key_base64_size];
memset(private_key_base64, 0, sizeof(char) * private_key_base64_size);

unsigned char *private_key_non_base64 =
NULL; // holds the raw bytes of the 9 element SEQUENCE of the numbers (0, N, E, D, P, Q, DP, DQ, QP), free later

unsigned char *private_key_pkcs8 = NULL; // buffer for building the pkcs_8 formatted private key, free later
char *private_key_pkcs8_base64 = NULL; // to hold the base64-encoded pkcs 8 formatted private key, free later

const size_t temp_buf_size = 4096; // sufficient to hold a private RSA Key in format ----BEGIN ....
unsigned char temp_buf[temp_buf_size];

/*
* 3. Seed RNG
*/
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
(const unsigned char *)ATCHOPS_RNG_PERSONALIZATION,
strlen(ATCHOPS_RNG_PERSONALIZATION))) != 0) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to seed random number generator\n");
goto exit;
}

/*
* 4. Use MbedTLS to generate RSA key pair
*/
if ((ret = mbedtls_pk_setup(&pk, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA))) != 0) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to setup RSA key\n");
goto exit;
}

if ((ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(pk), mbedtls_ctr_drbg_random, &ctr_drbg, 2048, 65537)) != 0) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate RSA key\n");
goto exit;
}

/*
* 5. Write to public_key_base64 buffer
*/
memset(temp_buf, 0, sizeof(temp_buf));
if ((ret = mbedtls_pk_write_pubkey_pem(&pk, temp_buf, temp_buf_size)) != 0) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to write public key\n");
goto exit;
}

size_t public_key_base64_len = 0;
char *begin = strstr((char *)temp_buf, "-----BEGIN PUBLIC KEY-----");
char *end = strstr((char *)temp_buf, "-----END PUBLIC KEY-----");
if (begin != NULL && end != NULL) {

begin += strlen("-----BEGIN PUBLIC KEY-----");
while (*begin == '\n' || *begin == '\r' || *begin == ' ')
begin++;

for (char *src = begin, *dest = public_key_base64; src < end; ++src) {
if (*src != '\n' && *src != '\r') {
*dest++ = *src;
public_key_base64_len++;
}
}
}

/*
* 6. Write to private_key_base64 buffer (PKCS#8 format)
*/
memset(temp_buf, 0, sizeof(unsigned char) * temp_buf_size);
if ((ret = mbedtls_pk_write_key_pem(&pk, temp_buf, temp_buf_size)) != 0) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to write private key (PKCS#8 format)\n");
goto exit;
}

size_t private_key_base64_len = 0;
begin = strstr((char *)temp_buf, "-----BEGIN RSA PRIVATE KEY-----");
end = strstr((char *)temp_buf, "-----END RSA PRIVATE KEY-----");
if (begin != NULL && end != NULL) {
begin += strlen("-----BEGIN RSA PRIVATE KEY-----");
while (*begin == '\n' || *begin == '\r' || *begin == ' ')
begin++;

for (char *src = begin, *dest = private_key_base64; src < end; ++src) {
if (*src != '\n' && *src != '\r') {
*dest++ = *src;
private_key_base64_len++;
}
}
}

const size_t private_key_non_base64_size = atchops_base64_decoded_size(private_key_base64_len);
private_key_non_base64 = (unsigned char *)malloc(private_key_non_base64_size);
if (private_key_non_base64 == NULL) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory for private_key_non_base64\n");
goto exit;
}

size_t private_key_non_base64_len = 0;
if ((ret = atchops_base64_decode((const unsigned char *)private_key_base64, private_key_base64_len,
private_key_non_base64, private_key_non_base64_size, &private_key_non_base64_len)) !=
0) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to decode private key\n");
goto exit;
}

const size_t private_key_pkcs8_size = private_key_non_base64_len + 22;
private_key_pkcs8 = (unsigned char *)malloc(private_key_pkcs8_size);
if (private_key_pkcs8 == NULL) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory for private_key_pkcs8\n");
goto exit;
}
memset(private_key_pkcs8, 0, sizeof(unsigned char) * private_key_pkcs8_size);


// https://lapo.it/asn1js/ use this to debug
// PrivateKeyInfo SEQUENCE (3 elements)
private_key_pkcs8[0] = 0x30; // constructed sequence tag
private_key_pkcs8[1] = 0x82; // 8 --> 1000 0000 (1 in MSB means that it is long form) and 2 --> 0010 0000 (the next 2
// bytes are the length of data)
private_key_pkcs8[2] = (unsigned char)((private_key_pkcs8_size >> 8) & 0xFF);
private_key_pkcs8[3] = (unsigned char)(private_key_pkcs8_size & 0xFF);

// version INTEGER 0
private_key_pkcs8[4] = 0x02; // integer tag
private_key_pkcs8[5] = 0x01; // length of data
private_key_pkcs8[6] = 0x00; // data

// private key algorithm identifier
private_key_pkcs8[7] = 0x30; // constructed sequence tag
private_key_pkcs8[8] = 0x0D; // there are 2 elements in the sequence
private_key_pkcs8[9] = 0x06;
private_key_pkcs8[10] = 0x09;
private_key_pkcs8[11] = 0x2A;
private_key_pkcs8[12] = 0x86;
private_key_pkcs8[13] = 0x48;
private_key_pkcs8[14] = 0x86;
private_key_pkcs8[15] = 0xF7;
private_key_pkcs8[16] = 0x0D;
private_key_pkcs8[17] = 0x01;
private_key_pkcs8[18] = 0x01;
private_key_pkcs8[19] = 0x01;
private_key_pkcs8[20] = 0x05;
private_key_pkcs8[21] = 0x00;

// PrivateKey OCTET STRING
private_key_pkcs8[22] = 0x04; // octet string tag
private_key_pkcs8[23] = 0x82; // 8 --> 1000 0000 (1 in MSB means that it is long form) and 2 --> 0010 0000 (the next 2
// bytes are the length of data)
private_key_pkcs8[24] = (unsigned char)((private_key_non_base64_len >> 8) & 0xFF); // length of data
private_key_pkcs8[25] = (unsigned char)(private_key_non_base64_len & 0xFF); // length of data

memcpy(private_key_pkcs8 + 26, private_key_non_base64, private_key_non_base64_len);

const size_t private_key_base64_pkcs8_size = atchops_base64_encoded_size(private_key_non_base64_len);
private_key_pkcs8_base64 = (char *)malloc(private_key_base64_pkcs8_size);
if (private_key_pkcs8_base64 == NULL) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory for private_key_pkcs8_base64\n");
goto exit;
}
memset(private_key_pkcs8_base64, 0, sizeof(char) * private_key_base64_pkcs8_size);

size_t private_key_base64_pkcs8_len = 0;
if ((ret = atchops_base64_encode(private_key_pkcs8, 26 + private_key_non_base64_len, private_key_pkcs8_base64,
private_key_base64_pkcs8_size, &private_key_base64_pkcs8_len)) != 0) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to encode private key\n");
goto exit;
}

/*
* 7. Populate the atchops_rsa_key_public_key and atchops_rsa_key_private_key structs
*/

if ((ret = atchops_rsa_key_populate_public_key(public_key, (const char *)public_key_base64, public_key_base64_len)) !=
0) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to populate public key\n");
goto exit;
}

if ((ret = atchops_rsa_key_populate_private_key(private_key, (const char *)private_key_pkcs8_base64,
private_key_base64_pkcs8_len)) != 0) {
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to populate private key\n");
goto exit;
}

exit: {
mbedtls_pk_free(&pk);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
free(private_key_non_base64);
free(private_key_pkcs8_base64);
free(private_key_pkcs8);
return ret;
}
}

int atchops_rsa_key_populate_public_key(atchops_rsa_key_public_key *public_key, const char *public_key_base64,
const size_t public_key_base64_len) {
int ret = 1;
Expand Down Expand Up @@ -207,7 +432,7 @@ int atchops_rsa_key_populate_public_key(atchops_rsa_key_public_key *public_key,
*/
mbedtls_asn1_sequence *seq = NULL; // free later

const size_t dst_size = BASE64_DECODED_KEY_BUFFER_SIZE;
const size_t dst_size = 2048; // sufficient size for a 2048 bit RSA key
unsigned char dst[dst_size];
memset(dst, 0, sizeof(unsigned char) * dst_size);
size_t dst_len = 0;
Expand Down Expand Up @@ -305,7 +530,7 @@ int atchops_rsa_key_populate_private_key(atchops_rsa_key_private_key *private_ke
*/
mbedtls_asn1_sequence *seq = NULL; // free later

const size_t dst_size = BASE64_DECODED_KEY_BUFFER_SIZE;
const size_t dst_size = 4096; // sufficient size for a 2048 bit RSA private key
unsigned char dst[dst_size];
memset(dst, 0, sizeof(unsigned char) * dst_size);
size_t dst_len = 0;
Expand Down Expand Up @@ -395,7 +620,8 @@ exit: {
}

bool atchops_rsa_key_is_public_key_populated(const atchops_rsa_key_public_key *public_key) {
return atchops_rsa_key_public_key_is_n_initialized(public_key) && atchops_rsa_key_public_key_is_e_initialized(public_key);
return atchops_rsa_key_public_key_is_n_initialized(public_key) &&
atchops_rsa_key_public_key_is_e_initialized(public_key);
}

bool atchops_rsa_key_is_private_key_populated(const atchops_rsa_key_private_key *private_key) {
Expand Down Expand Up @@ -1058,7 +1284,8 @@ bool atchops_rsa_key_private_key_is_q_initialized(const atchops_rsa_key_private_
return private_key->q._is_value_initialized;
}

void atchops_rsa_key_private_key_set_q_initialized(atchops_rsa_key_private_key *private_key, const bool is_initialized) {
void atchops_rsa_key_private_key_set_q_initialized(atchops_rsa_key_private_key *private_key,
const bool is_initialized) {
/*
* 1. Validate arguments
*/
Expand All @@ -1074,7 +1301,7 @@ void atchops_rsa_key_private_key_set_q_initialized(atchops_rsa_key_private_key *
}

int atchops_rsa_key_private_key_set_q(atchops_rsa_key_private_key *private_key, const unsigned char *q,
const size_t q_len) {
const size_t q_len) {
int ret = 1;

/*
Expand Down
Loading

0 comments on commit a314efb

Please sign in to comment.