From c16649b7c4b7c53dd4dd4309a0ab93f120d718c5 Mon Sep 17 00:00:00 2001 From: oskarvonephesos Date: Thu, 8 Apr 2021 17:45:26 +0000 Subject: [PATCH 1/5] add OAEP functionality --- Makefile | 6 +- rsa.c | 344 +++++++++++++++++++++++++++++++++++++++++++++++++----- sha-256.c | 219 ++++++++++++++++++++++++++++++++++ sha-256.h | 1 + 4 files changed, 541 insertions(+), 29 deletions(-) create mode 100644 sha-256.c create mode 100644 sha-256.h diff --git a/Makefile b/Makefile index 0e51012..bb86678 100644 --- a/Makefile +++ b/Makefile @@ -5,13 +5,13 @@ LIBS_PATH = -L. LDLIBS = $(LIBS_PATH) -lrsa -lm test: test.o librsa.a rsa.h - + gcc test.c sha-256.c -o test $(LDLIBS) librsa.a: rsa.o ar rc librsa.a rsa.o ranlib librsa.a -rsa.o: rsa.c rsa.h - gcc -c rsa.c +rsa.o: rsa.c rsa.h sha-256.c + gcc -c rsa.c sha-256.c .PHONY: clean, all diff --git a/rsa.c b/rsa.c index 42ca2fa..e1092ac 100644 --- a/rsa.c +++ b/rsa.c @@ -4,7 +4,7 @@ #include #include #include - +#include "sha-256.h" char buffer[1024]; const int MAX_DIGITS = 50; @@ -19,10 +19,260 @@ struct private_key_class{ long long modulus; long long exponent; }; +/* +This file has been augmented to include OAEP functions that scramble input messages in a predictable way before encrypting them, +thus maken chosen or known plaintext attacks much more challenging. +No important information should ever be encoded without them. +*/ +//this little function is necessary, because the original rsa implementation expects signed chars and the sha implementation outputs unsigned chars +//maybe this can be replaced +static char cabsc(uint8_t in){ + return in >= 128 ? in >> 1: in; +} +//necessary forward declarations to place all of the OAEP stuff at the top +long long *rsa_encrypt(const char *message, const unsigned long message_size, const struct public_key_class *pub); +char *rsa_decrypt(const long long *message, const unsigned long message_size, const struct private_key_class *pub); +/* +This function is described very well on wikipedia, but the short version is: +for every group of 32 bytes that is requested, apply sha256 to seed || counter (where || means concatenate; this is done while applying byte swapping) +*/ +static char* mgf1(const char* seed, const unsigned long seed_length, const unsigned long outlength){ + char* r = (char*)malloc(outlength); + char* processed_seed = (char*)malloc(seed_length +4); + memcpy(processed_seed, seed, seed_length); + int64_t processed = outlength; + int copy, i, j; + int counter = 0; + char* counterc =(char*) &counter; + while (processed>0){ + uint8_t hash[32]; + j = seed_length; + for (i=3; i>=0; i--){ + processed_seed[j++]=counterc[i]; + } + calc_sha_256(hash, processed_seed, (size_t) seed_length+4); + copy = processed > 32 ? 32: processed; + j = counter *32; + for (i=0; i=0; j--){ + r[k++]=charpointer[j]; + } + } + return r; + } + else { + printf("Machine is BIG-ENDIAN\n"); + return NULL; + } +} +static long long * osp2ill(char* message, const unsigned long message_size, const unsigned long num_octets){ + //test for endianess + unsigned short testshort = 0x01; + char* charpointer = (char*)&testshort; + if (*charpointer == 0x01){ + int i, j, k, l; + long long* r = (long long*)malloc(sizeof(long long)*message_size); + k=0; + charpointer = (char*) r; + char* messagepointer; + //reverse byte order adding zero padding for bytes not represented by num_octets + for (i=0; i= 0; j--){ + if (j <= 7 - num_octets) + charpointer[k++] = 0x00; + else + charpointer[k++] = messagepointer[--l]; + } + } + return r; + } + else { + printf("Machine is BIG-ENDIAN\n"); + return NULL; + } +} +static unsigned long getnumoctets(void* pub){ + struct public_key_class* key = (struct public_key_class*) pub; + //one octet can store 256 values, so num_octets must be log_256(modulus) + long long modulus = key->modulus; + unsigned long num_octets = 0; + while (modulus>0){ + modulus /=256; + num_octets++; + } + return num_octets; +} +/* +Explanation of the encoding scheme used in the OAEP functions. +|| refers to concatenation; ^ is bitwise XOR +M is the message (length m), A is a string of length k1 consisting of zeros, and B is a random string of length k2 + +MGF1 is a function, declared above, that takes in a string and produces a random but reproduceable string of a certain length + +M = M || A +M = M^MGF1(B, m+k1) +B = B^MGF1(M, k2) +M = M || B + +First, the string of zeros is added to M. +Then, M is xor'd with a string generated from B. +Then, B is xor'd with a string generated from resultant A. +Then, B is concatenated to A. + +We then proceed to encrypt / decrypt M as usual using RSA. The security benefit stems from the fact that an attacker has to decrypt +all of M to reverse the process described above (since any small change at the input of MGF1 will produce a completely different output). + + +*/ +char* rsa_oaep_encrypt(const char* message, unsigned long *message_size, const struct public_key_class *pub, const unsigned long k1, const unsigned long k2){ + //create random string of length k2 + char nonce[32]; + memset(nonce, '\0', 32); + snprintf(nonce, 32, "%d", rand()); + char* padding = mgf1(nonce, strlen(nonce), k2); + //expand padding to length k+1 message_size + char* rand_padding = mgf1(padding, k2, *message_size + k1); + //place message in new buffer and pad with zeros + char* r = (char*)malloc(*message_size + k1 +k2); + memset(r, '0', *message_size + k1); + memcpy(r, message, *message_size); + //xor message (+padding) with random string + int i, mylength = *message_size + k1; + for (i=0; i *length){ + return -1; + } + sprintf(&instring[j], "%lld ", rsa_raw[i]); + while(instring[j++]!=' ') + ; + } + *length = j; + return 0; +} +int b_string_to_raw(char* instring, uint32_t length, long long *rsa_raw, uint32_t* message_length){ + int i = 0, j=0; + while (1){ + if (i>= length) + break; + if (j> *message_length) + return -1; + sscanf(&instring[i], "%lld", &rsa_raw[j++]); + do{ + i++; + } + while (instring[i]!=' ' && i 0){ + if (e & 1){ + product = modmult(product, b, m); + } + b = modmult(b, b, m); + e >>= 1; + } + return product; +} +static long long modmult(long long a,long long b,long long mod) +{ + if (b<0) + b = -b; + if (a == 0 || b < mod / a) + return (a*b)%mod; + long long sum; + sum = 0; + while(b>0) + { + if(b&1) + sum = (sum + a) % mod; + a = (2*a) % mod; + b>>=1; + } + return sum; +} +/// @deprecated: this is unsafe +/* long long rsa_modExp(long long b, long long e, long long m) { if (b < 0 || e < 0 || m <= 0){ @@ -59,10 +344,10 @@ long long rsa_modExp(long long b, long long e, long long m) } } - +*/ // Calling this function will generate a public and private key and store them in the pointers -// it is given. -void rsa_gen_keys(struct public_key_class *pub, struct private_key_class *priv, char *PRIME_SOURCE_FILE) +// it is given. +void rsa_gen_keys(struct public_key_class *pub, struct private_key_class *priv, const char *PRIME_SOURCE_FILE) { FILE *primes_list; if(!(primes_list = fopen(PRIME_SOURCE_FILE, "r"))){ @@ -82,26 +367,26 @@ void rsa_gen_keys(struct public_key_class *pub, struct private_key_class *priv, } } while(feof(primes_list) == 0); - - + + // choose random primes from the list, store them as p,q long long p = 0; long long q = 0; - long long e = powl(2, 8) + 1; + long long e = (2 << 16) + 1;//powl(2, 8) + 1; long long d = 0; char prime_buffer[MAX_DIGITS]; long long max = 0; long long phi_max = 0; - + srand(time(NULL)); - + do{ // a and b are the positions of p and q in the list int a = (double)rand() * (prime_count+1) / (RAND_MAX+1.0); int b = (double)rand() * (prime_count+1) / (RAND_MAX+1.0); - + // here we find the prime at position a, store it as p rewind(primes_list); for(i=0; i < a + 1; i++){ @@ -110,8 +395,8 @@ void rsa_gen_keys(struct public_key_class *pub, struct private_key_class *priv, // } fgets(prime_buffer,sizeof(prime_buffer)-1, primes_list); } - p = atol(prime_buffer); - + p = atol(prime_buffer); + // here we find the prime at position b, store it as q rewind(primes_list); for(i=0; i < b + 1; i++){ @@ -120,22 +405,22 @@ void rsa_gen_keys(struct public_key_class *pub, struct private_key_class *priv, } fgets(prime_buffer,sizeof(prime_buffer)-1, primes_list); } - q = atol(prime_buffer); + q = atol(prime_buffer); max = p*q; phi_max = (p-1)*(q-1); } while(!(p && q) || (p == q) || (gcd(phi_max, e) != 1)); - + // Next, we need to choose a,b, so that a*max+b*e = gcd(max,e). We actually only need b - // here, and in keeping with the usual notation of RSA we'll call it d. We'd also like + // here, and in keeping with the usual notation of RSA we'll call it d. We'd also like // to make sure we get a representation of d as positive, hence the while loop. d = ExtEuclid(phi_max,e); while(d < 0){ d = d+phi_max; - } +} - printf("primes are %lld and %lld\n",(long long)p, (long long )q); + //printf("primes are %lld and %lld\n",(long long)p, (long long )q); // We now store the public / private keys in the appropriate structs pub->modulus = max; pub->exponent = e; @@ -145,7 +430,7 @@ void rsa_gen_keys(struct public_key_class *pub, struct private_key_class *priv, } -long long *rsa_encrypt(const char *message, const unsigned long message_size, +long long *rsa_encrypt(const char *message, const unsigned long message_size, const struct public_key_class *pub) { long long *encrypted = malloc(sizeof(long long)*message_size); @@ -156,14 +441,18 @@ long long *rsa_encrypt(const char *message, const unsigned long message_size, } long long i = 0; for(i=0; i < message_size; i++){ - encrypted[i] = rsa_modExp(message[i], pub->exponent, pub->modulus); + /*if (message[i]>=pub->modulus || message[i] < 0){ + printf("message out of range\n"); + }*/ + if ((encrypted[i] = rsa_mymodExp(message[i], pub->exponent, pub->modulus)) == -1) + return NULL; } return encrypted; } -char *rsa_decrypt(const long long *message, - const unsigned long message_size, +char *rsa_decrypt(const long long *message, + const unsigned long message_size, const struct private_key_class *priv) { if(message_size % sizeof(long long) != 0){ @@ -183,7 +472,10 @@ char *rsa_decrypt(const long long *message, // Now we go through each 8-byte chunk and decrypt it. long long i = 0; for(i=0; i < message_size/8; i++){ - temp[i] = rsa_modExp(message[i], priv->exponent, priv->modulus); + if ((temp[i] = rsa_mymodExp(message[i], priv->exponent, priv->modulus)) == -1){ + free(temp); + return NULL; + } } // The result should be a number in the char range, which gives back the original byte. // We put that into decrypted, then return. diff --git a/sha-256.c b/sha-256.c new file mode 100644 index 0000000..cd923ea --- /dev/null +++ b/sha-256.c @@ -0,0 +1,219 @@ +#include +#include + +#include "sha-256.h" + +#define CHUNK_SIZE 64 +#define TOTAL_LEN_LEN 8 + +/* + * ABOUT bool: this file does not use bool in order to be as pre-C99 compatible as possible. + */ + +/* + * Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are reproduced here. + * When useful for clarification, portions of the pseudo-code are reproduced here too. + */ + +/* + * Initialize array of round constants: + * (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311): + */ +static const uint32_t k[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +struct buffer_state { + const uint8_t * p; + size_t len; + size_t total_len; + int single_one_delivered; /* bool */ + int total_len_delivered; /* bool */ +}; + +static inline uint32_t right_rot(uint32_t value, unsigned int count) +{ + /* + * Defined behaviour in standard C for all count where 0 < count < 32, + * which is what we need here. + */ + return value >> count | value << (32 - count); +} + +static void init_buf_state(struct buffer_state * state, const void * input, size_t len) +{ + state->p = input; + state->len = len; + state->total_len = len; + state->single_one_delivered = 0; + state->total_len_delivered = 0; +} + +/* Return value: bool */ +static int calc_chunk(uint8_t chunk[CHUNK_SIZE], struct buffer_state * state) +{ + size_t space_in_chunk; + + if (state->total_len_delivered) { + return 0; + } + + if (state->len >= CHUNK_SIZE) { + memcpy(chunk, state->p, CHUNK_SIZE); + state->p += CHUNK_SIZE; + state->len -= CHUNK_SIZE; + return 1; + } + + memcpy(chunk, state->p, state->len); + chunk += state->len; + space_in_chunk = CHUNK_SIZE - state->len; + state->p += state->len; + state->len = 0; + + /* If we are here, space_in_chunk is one at minimum. */ + if (!state->single_one_delivered) { + *chunk++ = 0x80; + space_in_chunk -= 1; + state->single_one_delivered = 1; + } + + /* + * Now: + * - either there is enough space left for the total length, and we can conclude, + * - or there is too little space left, and we have to pad the rest of this chunk with zeroes. + * In the latter case, we will conclude at the next invokation of this function. + */ + if (space_in_chunk >= TOTAL_LEN_LEN) { + const size_t left = space_in_chunk - TOTAL_LEN_LEN; + size_t len = state->total_len; + int i; + memset(chunk, 0x00, left); + chunk += left; + + /* Storing of len * 8 as a big endian 64-bit without overflow. */ + chunk[7] = (uint8_t) (len << 3); + len >>= 5; + for (i = 6; i >= 0; i--) { + chunk[i] = (uint8_t) len; + len >>= 8; + } + state->total_len_delivered = 1; + } else { + memset(chunk, 0x00, space_in_chunk); + } + + return 1; +} + +/* + * Limitations: + * - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem + * for large data sizes. + * - SHA algorithms theoretically operate on bit strings. However, this implementation has no support + * for bit string lengths that are not multiples of eight, and it really operates on arrays of bytes. + * In particular, the len parameter is a number of bytes. + */ +void calc_sha_256(uint8_t hash[32], const void * input, size_t len) +{ + /* + * Note 1: All integers (expect indexes) are 32-bit unsigned integers and addition is calculated modulo 2^32. + * Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 = i = 63 + * Note 3: The compression function uses 8 working variables, a through h + * Note 4: Big-endian convention is used when expressing the constants in this pseudocode, + * and when parsing message block data from bytes to words, for example, + * the first word of the input message "abc" after padding is 0x61626380 + */ + + /* + * Initialize hash values: + * (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19): + */ + uint32_t h[] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; + unsigned i, j; + + /* 512-bit chunks is what we will operate on. */ + uint8_t chunk[64]; + + struct buffer_state state; + + init_buf_state(&state, input, len); + + while (calc_chunk(chunk, &state)) { + uint32_t ah[8]; + + const uint8_t *p = chunk; + + /* Initialize working variables to current hash value: */ + for (i = 0; i < 8; i++) + ah[i] = h[i]; + + /* Compression function main loop: */ + for (i = 0; i < 4; i++) { + /* + * The w-array is really w[64], but since we only need + * 16 of them at a time, we save stack by calculating + * 16 at a time. + * + * This optimization was not there initially and the + * rest of the comments about w[64] are kept in their + * initial state. + */ + + /* + * create a 64-entry message schedule array w[0..63] of 32-bit words + * (The initial values in w[0..63] don't matter, so many implementations zero them here) + * copy chunk into first 16 words w[0..15] of the message schedule array + */ + uint32_t w[16]; + + for (j = 0; j < 16; j++) { + if (i == 0) { + w[j] = (uint32_t) p[0] << 24 | (uint32_t) p[1] << 16 | + (uint32_t) p[2] << 8 | (uint32_t) p[3]; + p += 4; + } else { + /* Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: */ + const uint32_t s0 = right_rot(w[(j + 1) & 0xf], 7) ^ right_rot(w[(j + 1) & 0xf], 18) ^ (w[(j + 1) & 0xf] >> 3); + const uint32_t s1 = right_rot(w[(j + 14) & 0xf], 17) ^ right_rot(w[(j + 14) & 0xf], 19) ^ (w[(j + 14) & 0xf] >> 10); + w[j] = w[j] + s0 + w[(j + 9) & 0xf] + s1; + } + const uint32_t s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25); + const uint32_t ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]); + const uint32_t temp1 = ah[7] + s1 + ch + k[i << 4 | j] + w[j]; + const uint32_t s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22); + const uint32_t maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]); + const uint32_t temp2 = s0 + maj; + + ah[7] = ah[6]; + ah[6] = ah[5]; + ah[5] = ah[4]; + ah[4] = ah[3] + temp1; + ah[3] = ah[2]; + ah[2] = ah[1]; + ah[1] = ah[0]; + ah[0] = temp1 + temp2; + } + } + + /* Add the compressed chunk to the current hash value: */ + for (i = 0; i < 8; i++) + h[i] += ah[i]; + } + + /* Produce the final hash value (big-endian): */ + for (i = 0, j = 0; i < 8; i++) + { + hash[j++] = (uint8_t) (h[i] >> 24); + hash[j++] = (uint8_t) (h[i] >> 16); + hash[j++] = (uint8_t) (h[i] >> 8); + hash[j++] = (uint8_t) h[i]; + } +} diff --git a/sha-256.h b/sha-256.h new file mode 100644 index 0000000..47f06eb --- /dev/null +++ b/sha-256.h @@ -0,0 +1 @@ +void calc_sha_256(uint8_t hash[32], const void *input, size_t len); From ddf8aa0f2cce05fb72e0e5387bdd87b22095c7c5 Mon Sep 17 00:00:00 2001 From: oskarvonephesos Date: Thu, 8 Apr 2021 17:47:34 +0000 Subject: [PATCH 2/5] removed files relating to my project --- rsa.c | 49 +------------------------------------------------ rsa.h | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 51 deletions(-) diff --git a/rsa.c b/rsa.c index e1092ac..8b37909 100644 --- a/rsa.c +++ b/rsa.c @@ -223,54 +223,7 @@ char* rsa_oaep_decrypt(char* message, unsigned long *message_size, const struct free(encrypted); return decrypted; } -// B_CONNECT helper functions -int b_raw_to_hex(char* instring, uint32_t* length, long long *rsa_raw, uint32_t message_length){ - int i, j = 0; - memset(instring, '\0', *length); - for (i = 0; i < message_length; i++) { - instring += sprintf(instring, "%016llx", rsa_raw[i]); - } - *length = message_length * 16; - return 0; -} -int b_hex_to_raw(char* instring, uint32_t length, long long *rsa_raw, uint32_t* message_length){ - *message_length = length /2; - int i; - for (i=0; i < length/16; i++){ - sscanf(&instring[i*16], "%16llx", &rsa_raw[i]); - } - return 0; -} -int b_raw_to_string(char* instring, uint32_t* length, long long *rsa_raw, uint32_t message_length){ - int i, j = 0; - memset(instring, '\0', *length); - for (i=0; i < message_length; i++){ - if (j> *length){ - return -1; - } - sprintf(&instring[j], "%lld ", rsa_raw[i]); - while(instring[j++]!=' ') - ; - } - *length = j; - return 0; -} -int b_string_to_raw(char* instring, uint32_t length, long long *rsa_raw, uint32_t* message_length){ - int i = 0, j=0; - while (1){ - if (i>= length) - break; - if (j> *message_length) - return -1; - sscanf(&instring[i], "%lld", &rsa_raw[j++]); - do{ - i++; - } - while (instring[i]!=' ' && i Date: Sun, 18 Apr 2021 00:16:28 +0200 Subject: [PATCH 3/5] add gitignore & update input types (should be unsigned not signed) --- .gitignore | 3 ++ rsa.c | 151 +++++++++++++++++++++++++++-------------------------- rsa.h | 15 +++--- 3 files changed, 88 insertions(+), 81 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fae4548 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +test +*.a diff --git a/rsa.c b/rsa.c index 8b37909..c9f6451 100644 --- a/rsa.c +++ b/rsa.c @@ -30,20 +30,24 @@ static char cabsc(uint8_t in){ return in >= 128 ? in >> 1: in; } //necessary forward declarations to place all of the OAEP stuff at the top -long long *rsa_encrypt(const char *message, const unsigned long message_size, const struct public_key_class *pub); -char *rsa_decrypt(const long long *message, const unsigned long message_size, const struct private_key_class *pub); +long long *rsa_encrypt(const void *message, const unsigned long message_size, const struct public_key_class *pub); +uint8_t *rsa_decrypt(const long long *message, const unsigned long message_size, const struct private_key_class *pub); /* This function is described very well on wikipedia, but the short version is: for every group of 32 bytes that is requested, apply sha256 to seed || counter (where || means concatenate; this is done while applying byte swapping) */ -static char* mgf1(const char* seed, const unsigned long seed_length, const unsigned long outlength){ - char* r = (char*)malloc(outlength); - char* processed_seed = (char*)malloc(seed_length +4); +static uint8_t* mgf1(const void* seed, const unsigned long seed_length, const unsigned long outlength){ + uint8_t* r = (uint8_t*)malloc(outlength); + if (!r){ + fprintf(stderr, "MALLOC error in mgf1 with params seed_length %lu and outlength %lu", seed_length, outlength); + return NULL; + } + uint8_t* processed_seed = (uint8_t*)malloc(seed_length +4); memcpy(processed_seed, seed, seed_length); int64_t processed = outlength; int copy, i, j; int counter = 0; - char* counterc =(char*) &counter; + uint8_t* counterc =(uint8_t*) &counter; while (processed>0){ uint8_t hash[32]; j = seed_length; @@ -54,7 +58,7 @@ static char* mgf1(const char* seed, const unsigned long seed_length, const unsig copy = processed > 32 ? 32: processed; j = counter *32; for (i=0; i=0; j--){ - r[k++]=charpointer[j]; + if (num_octets > 7){ + fprintf(stderr, "Invalid num_octets: %lu\n", num_octets); + return NULL; } - } - return r; + int i, j, k; + uint8_t* r = (uint8_t*)malloc(num_octets*message_size); + if (!r){ + fprintf(stderr, "MALLOC error in ill2osp with params message_size: %lu, num_octets: %lu\n", message_size, num_octets); + return NULL; + } + k=0; + //reverse byte order ignoring all data beyond num_octets + for (i=0; i=0; j--){ + r[k++]=charpointer[j]; + } + } + return r; } else { - printf("Machine is BIG-ENDIAN\n"); + fprintf(stderr, "Machine is BIG-ENDIAN\n"); return NULL; } } -static long long * osp2ill(char* message, const unsigned long message_size, const unsigned long num_octets){ +static long long * osp2ill(void* message, const unsigned long message_size, const unsigned long num_octets){ //test for endianess unsigned short testshort = 0x01; - char* charpointer = (char*)&testshort; + uint8_t* charpointer = (uint8_t*)&testshort; if (*charpointer == 0x01){ - int i, j, k, l; - long long* r = (long long*)malloc(sizeof(long long)*message_size); - k=0; - charpointer = (char*) r; - char* messagepointer; - //reverse byte order adding zero padding for bytes not represented by num_octets - for (i=0; i= 0; j--){ - if (j <= 7 - num_octets) - charpointer[k++] = 0x00; - else - charpointer[k++] = messagepointer[--l]; + if (message_size % num_octets != 0){ + fprintf(stderr, "osp2ill error: Expected message size to be integer multiple of num octets.\nInstead found message_size: %lu and num_octets: %lu\n", message_size, num_octets); + return NULL; } - } - return r; + int i, j, k, l; + long long* r = (long long*)malloc(sizeof(long long)*message_size); + k=0; + charpointer = (uint8_t*) r; + uint8_t* messagepointer; + //reverse byte order adding zero padding for bytes not represented by num_octets + for (i=0; i= 0; j--){ + if (j <= 7 - num_octets) + charpointer[k++] = 0x00; + else + charpointer[k++] = messagepointer[--l]; + } + } + return r; } else { - printf("Machine is BIG-ENDIAN\n"); + fprintf(stderr, "Machine is BIG-ENDIAN\n"); return NULL; } } @@ -147,16 +163,16 @@ all of M to reverse the process described above (since any small change at the i */ -char* rsa_oaep_encrypt(const char* message, unsigned long *message_size, const struct public_key_class *pub, const unsigned long k1, const unsigned long k2){ +void* rsa_oaep_encrypt(const void* message, unsigned long *message_size, const struct public_key_class *pub, const unsigned long k1, const unsigned long k2){ //create random string of length k2 char nonce[32]; memset(nonce, '\0', 32); snprintf(nonce, 32, "%d", rand()); - char* padding = mgf1(nonce, strlen(nonce), k2); + uint8_t* padding = mgf1((void*)nonce, strlen(nonce), k2); //expand padding to length k+1 message_size - char* rand_padding = mgf1(padding, k2, *message_size + k1); + uint8_t* rand_padding = mgf1(padding, k2, *message_size + k1); //place message in new buffer and pad with zeros - char* r = (char*)malloc(*message_size + k1 +k2); + uint8_t* r = (uint8_t*)malloc(*message_size + k1 +k2); memset(r, '0', *message_size + k1); memcpy(r, message, *message_size); //xor message (+padding) with random string @@ -165,7 +181,7 @@ char* rsa_oaep_encrypt(const char* message, unsigned long *message_size, const s r[i] ^= rand_padding[i]; } //compress scrambled message to k2 length and xor padding with that scramble - char* last_scramble = mgf1(r, mylength, k2); + uint8_t* last_scramble = mgf1(r, mylength, k2); for (i=0; i=pub->modulus || message[i] < 0){ printf("message out of range\n"); }*/ - if ((encrypted[i] = rsa_mymodExp(message[i], pub->exponent, pub->modulus)) == -1) + if ((encrypted[i] = rsa_modExp(message_converted[i], pub->exponent, pub->modulus)) == -1) return NULL; } return encrypted; } - -char *rsa_decrypt(const long long *message, +uint8_t *rsa_decrypt(const long long *message, const unsigned long message_size, const struct private_key_class *priv) { @@ -413,11 +428,9 @@ char *rsa_decrypt(const long long *message, "Error: message_size is not divisible by %d, so cannot be output of rsa_encrypt\n", (int)sizeof(long long)); return NULL; } - // We allocate space to do the decryption (temp) and space for the output as a char array - // (decrypted) - char *decrypted = malloc(message_size/sizeof(long long)); - char *temp = malloc(message_size); - if((decrypted == NULL) || (temp == NULL)){ + // We allocate space for the output as a char array + uint8_t *decrypted = malloc(message_size/sizeof(long long)); + if(decrypted == NULL){ fprintf(stderr, "Error: Heap allocation failed.\n"); return NULL; @@ -425,16 +438,8 @@ char *rsa_decrypt(const long long *message, // Now we go through each 8-byte chunk and decrypt it. long long i = 0; for(i=0; i < message_size/8; i++){ - if ((temp[i] = rsa_mymodExp(message[i], priv->exponent, priv->modulus)) == -1){ - free(temp); - return NULL; - } + decrypted[i] = rsa_modExp(message[i], priv->exponent, priv->modulus); } // The result should be a number in the char range, which gives back the original byte. - // We put that into decrypted, then return. - for(i=0; i < message_size/8; i++){ - decrypted[i] = temp[i]; - } - free(temp); return decrypted; } diff --git a/rsa.h b/rsa.h index b7efa44..7b16036 100644 --- a/rsa.h +++ b/rsa.h @@ -5,9 +5,7 @@ // This is the header file for the library librsaencrypt.a -// Change this line to the file you'd like to use as a source of primes. -// The format of the file should be one prime per line. -char *PRIME_SOURCE_FILE = "primes.txt"; +#define PRIME_SOURCE_FILE "primes.txt" struct public_key_class{ @@ -23,18 +21,18 @@ struct private_key_class{ // This function generates public and private keys, then stores them in the structures you // provide pointers to. The 3rd argument should be the text PRIME_SOURCE_FILE to have it use // the location specified above in this header. -void rsa_gen_keys(struct public_key_class *pub, struct private_key_class *priv, const char *PRIME_SOURCE_FILE); +void rsa_gen_keys(struct public_key_class *pub, struct private_key_class *priv, const char *PSF); // This function will encrypt the data pointed to by message. It returns a pointer to a heap // array containing the encrypted data, or NULL upon failure. This pointer should be freed when // you are finished. The encrypted data will be 8 times as large as the original data. -long long *rsa_encrypt(const char *message, const unsigned long message_size, const struct public_key_class *pub); +long long *rsa_encrypt(const void *message, const unsigned long message_size, const struct public_key_class *pub); // This function will decrypt the data pointed to by message. It returns a pointer to a heap // array containing the decrypted data, or NULL upon failure. This pointer should be freed when // you are finished. The variable message_size is the size in bytes of the encrypted message. // The decrypted data will be 1/8th the size of the encrypted data. -char *rsa_decrypt(const long long *message, const unsigned long message_size, const struct private_key_class *pub); +uint8_t *rsa_decrypt(const long long *message, const unsigned long message_size, const struct private_key_class *pub); /* @@ -45,7 +43,8 @@ which depends on the keys. A rough estimate is: rsa_oaep_encrypt of a buffer of rsa_oaep_decrypt of a buffer of size m with k1 and k2 will produce a buffer of size (m - k1 - k2)/5. I have included an explanation of the encryption scheme at the appropriate place in the .c file, but wikipedia has an excellent explanation. */ -char* rsa_oaep_encrypt(const char* message, unsigned long *message_size, const struct public_key_class *pub, const unsigned long k1, const unsigned long k2); -char* rsa_oaep_decrypt(const char* message, unsigned long *message_size, const struct private_key_class *priv, const unsigned long k1, const unsigned long k2); +void* rsa_oaep_encrypt(const void* message, unsigned long *message_size, const struct public_key_class *pub, const unsigned long k1, const unsigned long k2); +void* rsa_oaep_decrypt(void* message, unsigned long *message_size, const struct private_key_class *priv, const unsigned long k1, const unsigned long k2); + #endif From 29354e8ec449d88a42673ad41af9dd660145119f Mon Sep 17 00:00:00 2001 From: oskarvonephesos Date: Sun, 18 Apr 2021 00:17:08 +0200 Subject: [PATCH 4/5] performance update: modExp is now approx. 10 times as fast --- rsa.c | 75 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/rsa.c b/rsa.c index c9f6451..25a8513 100644 --- a/rsa.c +++ b/rsa.c @@ -26,7 +26,7 @@ No important information should ever be encoded without them. */ //this little function is necessary, because the original rsa implementation expects signed chars and the sha implementation outputs unsigned chars //maybe this can be replaced -static char cabsc(uint8_t in){ +static inline char cabsc(uint8_t in){ return in >= 128 ? in >> 1: in; } //necessary forward declarations to place all of the OAEP stuff at the top @@ -261,28 +261,30 @@ static long long ExtEuclid(long long a, long long b) return y; } static long long modmult(long long a,long long b,long long mod); -static long long rsa_mymodExp(long long b, long long e, long long m) +static inline long long modmult(long long a,long long b,long long mod) { - if (b<0) - b = -b; - long long product; - product = 1; - b = b % m; - while ( e > 0){ - if (e & 1){ - product = modmult(product, b, m); - } - b = modmult(b, b, m); - e >>= 1; + // this is necessary since we will be dividing by a + if (a == 0 ){ + return 0; + } + register long long product = a * b; + //if multiplication does not overflow, we can use it + if (product / a == b){ + return product % mod; + } + // if a % 2 == 1 i. e. a >> 1 is not a / 2 + if ( a & 1 ) { + product = modmult((a>>1), b, mod); + if ((product << 1) > product ){ + return ((( product << 1 ) % mod ) + b) % mod; } - return product; -} -static long long modmult(long long a,long long b,long long mod) -{ - if (b<0) - b = -b; - if (a == 0 || b < mod / a) - return (a*b)%mod; + } + //implicit else + product = modmult((a >> 1), b, mod); + if ((product << 1) > product){ + return (product << 1) % mod ; + } + //implicit else: this is about 10x slower than the code above, but it will not overflow long long sum; sum = 0; while(b>0) @@ -294,25 +296,22 @@ static long long modmult(long long a,long long b,long long mod) } return sum; } -/// @deprecated: this is unsafe -/* -long long rsa_modExp(long long b, long long e, long long m) +static long long rsa_modExp(long long b, long long e, long long m) { - if (b < 0 || e < 0 || m <= 0){ - exit(1); - } - b = b % m; - if(e == 0) return 1; - if(e == 1) return b; - if( e % 2 == 0){ - return ( rsa_modExp(b * b % m, e/2, m) % m ); - } - if( e % 2 == 1){ - return ( b * rsa_modExp(b, (e-1), m) % m ); - } - + if (b<0) + b = -b; + long long product; + product = 1; + b = b % m; + while ( e > 0){ + if (e & 1){ + product = modmult(product, b, m); + } + b = modmult(b, b, m); + e >>= 1; + } + return product; } -*/ // Calling this function will generate a public and private key and store them in the pointers // it is given. void rsa_gen_keys(struct public_key_class *pub, struct private_key_class *priv, const char *PRIME_SOURCE_FILE) From 9de08fb916064f24ca9127c6c892529c90559a84 Mon Sep 17 00:00:00 2001 From: oskarvonephesos <46396332+oskarvonephesos@users.noreply.github.com> Date: Sun, 18 Apr 2021 00:36:48 +0200 Subject: [PATCH 5/5] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2ed469e..612839b 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,4 @@ Detailed descriptions of these functions are provided in the header file rsa.h. I make no claim that any good encyrption practices are used here. Probably don't use this for any production purposes. Created by Andrew Kiluk +This version edited by oskarvonephesos to include OAEP functionality among other things.