blob: 207ff19df7509a17d3b61a25cae59e5e8bfaf528 [file] [log] [blame] [raw]
#include "mini-crypt.h"
/*******************************************************************************
Setup
*/
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <ctype.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
#include <endian.h>
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define __BIG_ENDIAN__
#endif
#endif
/*****************************************************************************
Local functions used in the API.
*/
/* SHA-1 */
static void sha1_init(sha1_s* s);
static int sha1_write(sha1_s* s, const char* data, size_t len);
static char* sha1_result(sha1_s* s);
/* SHA-2 */
static void sha2_init(sha2_s* s, sha2_variant variant);
static int sha2_write(sha2_s* s, const char* data, size_t len);
static char* sha2_result(sha2_s* s);
/* Base64 */
static int base64_encode(char* target, const char* data, int len);
static int base64_decode(char* target, char* encoded, int base64_len);
/* Hex */
static int is_hex(const char* string, size_t length);
static int str2hex(char* target, const char* string, size_t length);
static int hex2str(char* target, char* hex, size_t length);
/* misc */
static fdump_s* fdump(const char* file_path, size_t size_limit);
static int xor_crypt(xor_key_s* key,
void* target,
const void* source,
size_t length);
/*****************************************************************************
The API gateway
*/
struct MiniCrypt__API___ MiniCrypt = {
/* SHA-1 */
.sha1_init = sha1_init,
.sha1_write = sha1_write,
.sha1_result = sha1_result,
/* SHA-2 */
.sha2_init = sha2_init,
.sha2_write = sha2_write,
.sha2_result = sha2_result,
/* Base64 */
.base64_encode = base64_encode,
.base64_decode = base64_decode,
/* Hex */
.is_hex = is_hex,
.str2hex = str2hex,
.hex2str = hex2str,
/* Misc */
.fdump = fdump,
.xor_crypt = xor_crypt,
};
/*****************************************************************************
Useful Macros
*/
/** 32Bit left rotation, inlined. */
#define left_rotate32(i, bits) (((i) << (bits)) | ((i) >> (32 - (bits))))
/** 32Bit right rotation, inlined. */
#define right_rotate32(i, bits) (((i) >> (bits)) | ((i) << (32 - (bits))))
/** 64Bit left rotation, inlined. */
#define left_rotate64(i, bits) (((i) << (bits)) | ((i) >> (64 - (bits))))
/** 64Bit right rotation, inlined. */
#define right_rotate64(i, bits) (((i) >> (bits)) | ((i) << (64 - (bits))))
/** unknown size element - left rotation, inlined. */
#define left_rotate(i, bits) (((i) << (bits)) | ((i) >> (sizeof((i)) - (bits))))
/** unknown size element - right rotation, inlined. */
#define right_rotate(i, bits) \
(((i) >> (bits)) | ((i) << (sizeof((i)) - (bits))))
/*******************************************************************************
SHA-1 hashing
*/
/**
Initialize/reset the SHA-1 object.
*/
static void sha1_init(sha1_s* s) {
memset(s, 0, sizeof(*s));
s->digest.i[0] = 0x67452301;
s->digest.i[1] = 0xefcdab89;
s->digest.i[2] = 0x98badcfe;
s->digest.i[3] = 0x10325476;
s->digest.i[4] = 0xc3d2e1f0;
s->initialized = 1;
}
/**
Process the buffer once full.
*/
static void sha1_process_buffer(sha1_s* s) {
uint32_t a = s->digest.i[0];
uint32_t b = s->digest.i[1];
uint32_t c = s->digest.i[2];
uint32_t d = s->digest.i[3];
uint32_t e = s->digest.i[4];
uint32_t t, w[80];
#define sha1_round(num) \
w[num] = s->buffer.i[(num)]; \
t = left_rotate32(a, 5) + e + w[num] + ((b & c) | ((~b) & d)) + 0x5A827999; \
e = d; \
d = c; \
c = left_rotate32(b, 30); \
b = a; \
a = t;
sha1_round(0);
sha1_round(1);
sha1_round(2);
sha1_round(3);
sha1_round(4);
sha1_round(5);
sha1_round(6);
sha1_round(7);
sha1_round(8);
sha1_round(9);
sha1_round(10);
sha1_round(11);
sha1_round(12);
sha1_round(13);
sha1_round(14);
sha1_round(15);
#undef sha1_round
#define sha1_round(i) \
w[i] = left_rotate32((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); \
t = left_rotate32(a, 5) + e + w[i] + ((b & c) | ((~b) & d)) + 0x5A827999; \
e = d; \
d = c; \
c = left_rotate32(b, 30); \
b = a; \
a = t;
sha1_round(16);
sha1_round(17);
sha1_round(18);
sha1_round(19);
#undef sha1_round
#define sha1_round(i) \
w[i] = left_rotate32((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); \
t = left_rotate32(a, 5) + e + w[i] + (b ^ c ^ d) + 0x6ED9EBA1; \
e = d; \
d = c; \
c = left_rotate32(b, 30); \
b = a; \
a = t;
sha1_round(20);
sha1_round(21);
sha1_round(22);
sha1_round(23);
sha1_round(24);
sha1_round(25);
sha1_round(26);
sha1_round(27);
sha1_round(28);
sha1_round(29);
sha1_round(30);
sha1_round(31);
sha1_round(32);
sha1_round(33);
sha1_round(34);
sha1_round(35);
sha1_round(36);
sha1_round(37);
sha1_round(38);
sha1_round(39);
#undef sha1_round
#define sha1_round(i) \
w[i] = left_rotate32((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); \
t = left_rotate32(a, 5) + e + w[i] + ((b & (c | d)) | (c & d)) + 0x8F1BBCDC; \
e = d; \
d = c; \
c = left_rotate32(b, 30); \
b = a; \
a = t;
sha1_round(40);
sha1_round(41);
sha1_round(42);
sha1_round(43);
sha1_round(44);
sha1_round(45);
sha1_round(46);
sha1_round(47);
sha1_round(48);
sha1_round(49);
sha1_round(50);
sha1_round(51);
sha1_round(52);
sha1_round(53);
sha1_round(54);
sha1_round(55);
sha1_round(56);
sha1_round(57);
sha1_round(58);
sha1_round(59);
#undef sha1_round
#define sha1_round(i) \
w[i] = left_rotate32((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); \
t = left_rotate32(a, 5) + e + w[i] + (b ^ c ^ d) + 0xCA62C1D6; \
e = d; \
d = c; \
c = left_rotate32(b, 30); \
b = a; \
a = t;
sha1_round(60);
sha1_round(61);
sha1_round(62);
sha1_round(63);
sha1_round(64);
sha1_round(65);
sha1_round(66);
sha1_round(67);
sha1_round(68);
sha1_round(69);
sha1_round(70);
sha1_round(71);
sha1_round(72);
sha1_round(73);
sha1_round(74);
sha1_round(75);
sha1_round(76);
sha1_round(77);
sha1_round(78);
sha1_round(79);
// store data
s->digest.i[4] += e;
s->digest.i[3] += d;
s->digest.i[2] += c;
s->digest.i[1] += b;
s->digest.i[0] += a;
}
/**
Add a single byte to the buffer and check the buffer's status.
*/
#ifdef __BIG_ENDIAN__
#define sha1_add_byte(s, byte) s->buffer.str[s->buffer_pos++] = byte;
#else
#define sha1_add_byte(s, byte) s->buffer.str[(s->buffer_pos++) ^ 3] = byte;
#endif
#define sha1_review_buffer(s) \
if (s->buffer_pos == 0) \
sha1_process_buffer(s);
/**
Write data to the buffer.
*/
static int sha1_write(sha1_s* s, const char* data, size_t len) {
if (!s || s->finalized)
return -1;
if (!s->initialized)
sha1_init(s);
// msg length is in bits, not bytes.
s->msg_length.i += (len << 3);
// add each byte to the sha1 hash's buffer... network byte issues apply
while (len--) {
sha1_add_byte(s, *(data++));
sha1_review_buffer(s);
}
return 0;
}
/**
Finalize the SHA-1 object and return the resulting hash.
*/
static char* sha1_result(sha1_s* s) {
// finalize the data if it wasn't finalized before
if (!s->finalized) {
// set the finalized flag
s->finalized = 1;
// pad the buffer
sha1_add_byte(s, 0x80);
// add 0 utill we reach the buffer's last 8 bytes (used for length data)
while (s->buffer_pos > 56) { // make sure we're not at the buffer's end
sha1_add_byte(s, 0);
}
sha1_review_buffer(s); // make sure the buffer isn't full
while (s->buffer_pos != 56) {
sha1_add_byte(s, 0);
}
// add the total length data (this will cause the buffer to be processed).
// this must the number in BITS, encoded as a BIG ENDIAN 64 bit number.
// the 3 in the shifting == x8, translating bytes into bits.
// every time we add a byte, only the last 8 bits are added.
#ifdef __BIG_ENDIAN__
// add length data, byte by byte
sha1_add_byte(s, s->msg_length.str[0]);
sha1_add_byte(s, s->msg_length.str[1]);
sha1_add_byte(s, s->msg_length.str[2]);
sha1_add_byte(s, s->msg_length.str[3]);
sha1_add_byte(s, s->msg_length.str[4]);
sha1_add_byte(s, s->msg_length.str[5]);
sha1_add_byte(s, s->msg_length.str[6]);
sha1_add_byte(s, s->msg_length.str[7]);
#else
// add length data, reverse byte order (little endian)
sha1_add_byte(s, s->msg_length.str[7]);
sha1_add_byte(s, s->msg_length.str[6]);
sha1_add_byte(s, s->msg_length.str[5]);
sha1_add_byte(s, s->msg_length.str[4]);
sha1_add_byte(s, s->msg_length.str[3]);
sha1_add_byte(s, s->msg_length.str[2]);
sha1_add_byte(s, s->msg_length.str[1]);
sha1_add_byte(s, s->msg_length.str[0]);
#endif
sha1_process_buffer(s);
// change back to little endian
// reverse byte order for each uint32 "word".
#ifndef __BIG_ENDIAN__
unsigned char t;
#define switch_bytes(i) \
t = s->digest.str[(i * 4)]; \
s->digest.str[(i * 4)] = s->digest.str[(i * 4) + 3]; \
s->digest.str[(i * 4) + 3] = t; \
t = s->digest.str[(i * 4) + 1]; \
s->digest.str[(i * 4) + 1] = s->digest.str[(i * 4) + 2]; \
s->digest.str[(i * 4) + 2] = t;
switch_bytes(0);
switch_bytes(1);
switch_bytes(2);
switch_bytes(3);
switch_bytes(4);
#undef switch_bytes
#endif
}
// fprintf(stderr, "result requested, in hex, is:");
// for (size_t i = 0; i < 20; i++)
// fprintf(stderr, "%02x", (unsigned int)(s->digest.str[i] & 0xFF));
// fprintf(stderr, "\r\n");
return s->digest.str;
}
/*******************************************************************************
SHA-2
*/
/* SHA-224 and SHA-256 constants */
static uint32_t sha2_256_words[] = {
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};
/* SHA-512 and friends constants */
static uint64_t sha2_512_words[] = {
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f,
0xe9b5dba58189dbbc, 0x3956c25bf348b538, 0x59f111f1b605d019,
0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242,
0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3,
0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, 0x2de92c6f592b0275,
0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f,
0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc,
0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6,
0x92722c851482353b, 0xa2bfe8a14cf10364, 0xa81a664bbc423001,
0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99,
0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb,
0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc,
0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915,
0xc67178f2e372532b, 0xca273eceea26619c, 0xd186b8c721c0c207,
0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba,
0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a,
0x5fcb6fab3ad6faec, 0x6c44198c4a475817};
/* Specific Macros for the SHA-2 processing */
#define Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & z))
#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define Eps0_32(x) \
(right_rotate32((x), 2) ^ right_rotate32((x), 13) ^ right_rotate32((x), 22))
#define Eps1_32(x) \
(right_rotate32((x), 6) ^ right_rotate32((x), 11) ^ right_rotate32((x), 25))
#define Omg0_32(x) \
(right_rotate32((x), 7) ^ right_rotate32((x), 18) ^ (((x) >> 3)))
#define Omg1_32(x) \
(right_rotate32((x), 17) ^ right_rotate32((x), 19) ^ (((x) >> 10)))
#define Eps0_64(x) \
(right_rotate64((x), 28) ^ right_rotate64((x), 34) ^ right_rotate64((x), 39))
#define Eps1_64(x) \
(right_rotate64((x), 14) ^ right_rotate64((x), 18) ^ right_rotate64((x), 41))
#define Omg0_64(x) \
(right_rotate64((x), 1) ^ right_rotate64((x), 8) ^ (((x) >> 7)))
#define Omg1_64(x) \
(right_rotate64((x), 19) ^ right_rotate64((x), 61) ^ (((x) >> 6)))
#ifdef __BIG_ENDIAN__
#define sha2_set_byte(p, pos, byte) (p)->buffer.str[(pos)] = byte;
#else
#define sha2_set_byte(p, pos, byte) \
(p)->buffer.str[(pos) ^ ((p)->type_512 ? 7 : 3)] = byte;
#endif
#define sha2_set_byte64(byte)
/**
Initialize/reset the SHA-2 object.
SHA-2 is actually a family of functions with different variants. When
initializing the SHA-2 container, you must select the variant you intend to
apply. The following are valid options (see the sha2_variant enum):
- SHA_512 (== 0)
- SHA_384
- SHA_512_224
- SHA_512_256
- SHA_256
- SHA_224
*/
static void sha2_init(sha2_s* s, sha2_variant variant) {
memset(s, 0, sizeof(*s));
if (variant == SHA_224) {
s->type_512 = 0;
s->digest.i32[0] = 0xc1059ed8;
s->digest.i32[1] = 0x367cd507;
s->digest.i32[2] = 0x3070dd17;
s->digest.i32[3] = 0xf70e5939;
s->digest.i32[4] = 0xffc00b31;
s->digest.i32[5] = 0x68581511;
s->digest.i32[6] = 0x64f98fa7;
s->digest.i32[7] = 0xbefa4fa4;
s->digest.str[32] = 0; // NULL.
} else if (variant == SHA_256) {
s->type_512 = 0;
s->digest.i32[0] = 0x6a09e667;
s->digest.i32[1] = 0xbb67ae85;
s->digest.i32[2] = 0x3c6ef372;
s->digest.i32[3] = 0xa54ff53a;
s->digest.i32[4] = 0x510e527f;
s->digest.i32[5] = 0x9b05688c;
s->digest.i32[6] = 0x1f83d9ab;
s->digest.i32[7] = 0x5be0cd19;
s->digest.str[32] = 0; // NULL.
} else if (variant == SHA_384) {
s->type_512 = 1;
s->digest.i64[0] = 0xcbbb9d5dc1059ed8;
s->digest.i64[1] = 0x629a292a367cd507;
s->digest.i64[2] = 0x9159015a3070dd17;
s->digest.i64[3] = 0x152fecd8f70e5939;
s->digest.i64[4] = 0x67332667ffc00b31;
s->digest.i64[5] = 0x8eb44a8768581511;
s->digest.i64[6] = 0xdb0c2e0d64f98fa7;
s->digest.i64[7] = 0x47b5481dbefa4fa4;
} else if (variant == SHA_512_224) {
s->type_512 = 1;
s->digest.i64[0] = 0x8c3d37c819544da2;
s->digest.i64[1] = 0x73e1996689dcd4d6;
s->digest.i64[2] = 0x1dfab7ae32ff9c82;
s->digest.i64[3] = 0x679dd514582f9fcf;
s->digest.i64[4] = 0x0f6d2b697bd44da8;
s->digest.i64[5] = 0x77e36f7304c48942;
s->digest.i64[6] = 0x3f9d85a86a1d36c8;
s->digest.i64[7] = 0x1112e6ad91d692a1;
} else if (variant == SHA_512_256) {
s->type_512 = 1;
s->digest.i64[0] = 0x22312194fc2bf72c;
s->digest.i64[1] = 0x9f555fa3c84c64c2;
s->digest.i64[2] = 0x2393b86b6f53b151;
s->digest.i64[3] = 0x963877195940eabd;
s->digest.i64[4] = 0x96283ee2a88effe3;
s->digest.i64[5] = 0xbe5e1e2553863992;
s->digest.i64[6] = 0x2b0199fc2c85b8aa;
s->digest.i64[7] = 0x0eb72ddc81c52ca2;
} else {
s->type_512 = 1;
s->digest.i64[0] = 0x6a09e667f3bcc908;
s->digest.i64[1] = 0xbb67ae8584caa73b;
s->digest.i64[2] = 0x3c6ef372fe94f82b;
s->digest.i64[3] = 0xa54ff53a5f1d36f1;
s->digest.i64[4] = 0x510e527fade682d1;
s->digest.i64[5] = 0x9b05688c2b3e6c1f;
s->digest.i64[6] = 0x1f83d9abfb41bd6b;
s->digest.i64[7] = 0x5be0cd19137e2179;
}
s->type = variant;
s->initialized = 1;
}
/**
Process the buffer once full.
*/
static void sha2_process_buffer(sha2_s* s) {
if (s->type_512) {
// process values for the 64bit words
uint64_t a = s->digest.i64[0];
uint64_t b = s->digest.i64[1];
uint64_t c = s->digest.i64[2];
uint64_t d = s->digest.i64[3];
uint64_t e = s->digest.i64[4];
uint64_t f = s->digest.i64[5];
uint64_t g = s->digest.i64[6];
uint64_t h = s->digest.i64[7];
uint64_t t1, t2, w[80];
size_t i = 0;
for (; i < 16; i++) {
w[i] = s->buffer.i64[i];
t1 = h + Eps1_64(e) + Ch(e, f, g) + sha2_512_words[i] + w[i];
t2 = Eps0_64(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
for (; i < 80; i++) {
w[i] = Omg1_64(w[i - 2]) + w[i - 7] + Omg0_64(w[i - 15]) + w[i - 16];
t1 = h + Eps1_64(e) + Ch(e, f, g) + sha2_512_words[i] + w[i];
t2 = Eps0_64(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
s->digest.i64[0] += a;
s->digest.i64[1] += b;
s->digest.i64[2] += c;
s->digest.i64[3] += d;
s->digest.i64[4] += e;
s->digest.i64[5] += f;
s->digest.i64[6] += g;
s->digest.i64[7] += h;
} else {
// process values for the 32bit words
uint32_t a = s->digest.i32[0];
uint32_t b = s->digest.i32[1];
uint32_t c = s->digest.i32[2];
uint32_t d = s->digest.i32[3];
uint32_t e = s->digest.i32[4];
uint32_t f = s->digest.i32[5];
uint32_t g = s->digest.i32[6];
uint32_t h = s->digest.i32[7];
uint32_t t1, t2, w[64];
size_t i = 0;
for (; i < 16; i++) {
w[i] = s->buffer.i32[i];
t1 = h + Eps1_32(e) + Ch(e, f, g) + sha2_256_words[i] + w[i];
t2 = Eps0_32(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
for (; i < 64; i++) {
w[i] = Omg1_32(w[i - 2]) + w[i - 7] + Omg0_32(w[i - 15]) + w[i - 16];
t1 = h + Eps1_32(e) + Ch(e, f, g) + sha2_256_words[i] + w[i];
t2 = Eps0_32(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
s->digest.i32[0] += a;
s->digest.i32[1] += b;
s->digest.i32[2] += c;
s->digest.i32[3] += d;
s->digest.i32[4] += e;
s->digest.i32[5] += f;
s->digest.i32[6] += g;
s->digest.i32[7] += h;
// reset buffer count
s->buffer_pos = 0;
}
}
/** Write data to be hashed by the SHA-2 object. */
static int sha2_write(sha2_s* s, const char* data, size_t len) {
if (!s || s->finalized)
return -1;
if (!s->initialized)
sha2_init(s, SHA_512);
// msg length is in up to 128 bits long...
s->msg_length.i += (__uint128_t)len << 3;
// add each byte to the sha1 hash's buffer... network byte issues apply
while (len--) {
// add a byte to the buffer, consider network byte order .
sha2_set_byte(s, s->buffer_pos, *(data++));
// update buffer position
++s->buffer_pos;
// review chunk (1024/512 bits) processing
if ((!s->type_512 && s->buffer_pos == 64) ||
(s->type_512 && s->buffer_pos == 0)) {
// s->buffer_pos wraps at 127 back to 0, so each 0 is the 1024 bits
// (128 bytes) chunk marker to be processed.
sha2_process_buffer(s);
}
}
return 0;
}
/**
Finalize the SHA-1 object and return the resulting hash.
*/
static char* sha2_result(sha2_s* s) {
// finalize the data if it wasn't finalized before
if (!s->finalized) {
// set the finalized flag
s->finalized = 1;
// start padding
sha2_set_byte(s, s->buffer_pos++, 0x80);
// pad the message
if (s->type_512)
while (s->buffer_pos != 112) {
if (!s->buffer_pos)
sha2_process_buffer(s);
sha2_set_byte(s, s->buffer_pos, 0);
++s->buffer_pos;
}
else
while (s->buffer_pos != 56) {
if (s->buffer_pos == 64)
sha2_process_buffer(s);
sha2_set_byte(s, s->buffer_pos, 0);
++s->buffer_pos;
}
// add the total length data (this will cause the buffer to be processed).
// this must the number in BITS, encoded as a BIG ENDIAN 64 bit number.
// the 3 in the shifting == x8, translating bytes into bits.
// every time we add a byte, only the last 8 bits are added.
#ifdef __BIG_ENDIAN__
// add length data, byte by byte.
// add length data, byte by byte
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[0]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[1]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[2]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[3]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[4]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[5]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[6]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[7]);
if (s->type_512) {
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[8]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[9]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[10]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[11]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[12]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[13]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[14]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[15]);
}
#else
// add length data, reverse byte order (little endian)
// fprintf(stderr, "The %s bytes are relevant\n",
// (s->msg_length.str[15] ? "last" : "first"));
if (s->type_512) {
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[15]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[14]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[13]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[12]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[11]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[10]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[9]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[8]);
}
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[7]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[6]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[5]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[4]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[3]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[2]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[1]);
sha2_set_byte(s, s->buffer_pos++, s->msg_length.str[0]);
#endif
sha2_process_buffer(s);
#ifndef __BIG_ENDIAN__
// change back to little endian
unsigned char t;
if (s->type_512) {
for (size_t i = 0; i < 64; i += 8) {
// reverse byte order for each uint64 "word".
for (size_t j = 0; j < 4; j++) {
t = s->digest.str[i + j]; // switch bytes
s->digest.str[i + j] = s->digest.str[i + (7 - j)];
s->digest.str[i + (7 - j)] = t;
}
}
} else {
for (size_t i = 0; i < 32; i += 4) {
// reverse byte order for each uint32 "word".
t = s->digest.str[i]; // switch first and last bytes
s->digest.str[i] = s->digest.str[i + 3];
s->digest.str[i + 3] = t;
t = s->digest.str[i + 1]; // switch median bytes
s->digest.str[i + 1] = s->digest.str[i + 2];
s->digest.str[i + 2] = t;
}
}
#endif
// set NULL bytes for SHA_224
if (s->type == SHA_224 || s->type == SHA_512_224)
s->digest.str[28] = 0;
// set NULL bytes for SHA_256
else if (s->type == SHA_512_256)
s->digest.str[32] = 0;
// set NULL bytes for SHA_384
else if (s->type == SHA_384)
s->digest.str[48] = 0;
}
// fprintf(stderr, "SHA-2 result requested, in hex, is:");
// for (size_t i = 0; i < (s->type_512 ? 64 : 32); i++)
// fprintf(stderr, "%02x", (unsigned int)(s->digest.str[i] & 0xFF));
// fprintf(stderr, "\r\n");
return s->digest.str;
}
/*******************************************************************************
SHA-3 TODO
*/
/*******************************************************************************
Base64 encoding/decoding
*/
/** the base64 encoding array */
static char base64_encodes[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
/**
a base64 decoding array
*/
static unsigned base64_decodes[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0,
0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
a union used for Base64 parsing
*/
union Base64Parser {
struct {
unsigned tail : 2;
unsigned data : 6;
} byte1;
struct {
unsigned prev : 8;
unsigned tail : 4;
unsigned head : 4;
} byte2;
struct {
unsigned prev : 16;
unsigned data : 6;
unsigned head : 2;
} byte3;
char bytes[3];
};
/**
This will encode a byte array (data) of a specified length (len) and
place
the encoded data into the target byte buffer (target). The target buffer
MUST have enough room for the expected data, including a terminating NULL
byte.
Base64 encoding always requires 4 bytes for each 3 bytes. Padding is added if
the raw data's length isn't devisable by 3.
Always assume the target buffer should have room enough for (len*4/3 + 5)
bytes.
Returns the number of bytes actually written to the target buffer
(including the Base64 required padding and excluding the NULL terminator).
A NULL terminator char is written to the target buffer.
*/
static int base64_encode(char* target, const char* data, int len) {
int written = 0;
// // optional implementation: allow a non writing, length computation.
// if (!target)
// return (len % 3) ? (((len + 3) / 3) * 4) : (len / 3);
// use a union to avoid padding issues.
union Base64Parser* section;
while (len >= 3) {
section = (void*)data;
target[0] = base64_encodes[section->byte1.data];
target[1] =
base64_encodes[(section->byte1.tail << 4) | (section->byte2.head)];
target[2] =
base64_encodes[(section->byte2.tail << 2) | (section->byte3.head)];
target[3] = base64_encodes[section->byte3.data];
target += 4;
data += 3;
len -= 3;
written += 4;
}
section = (void*)data;
if (len == 2) {
target[0] = base64_encodes[section->byte1.data];
target[1] =
base64_encodes[(section->byte1.tail << 4) | (section->byte2.head)];
target[2] = base64_encodes[section->byte2.tail << 2];
target[3] = '=';
target += 4;
written += 4;
} else if (len == 1) {
target[0] = base64_encodes[section->byte1.data];
target[1] = base64_encodes[section->byte1.tail << 4];
target[2] = '=';
target[3] = '=';
target += 4;
written += 4;
}
target[0] = 0; // NULL terminator
return written;
}
/**
This will decode a Base64 encoded string of a specified length (len) and
place the decoded data into the target byte buffer (target).
The target buffer MUST have enough room for the expected data.
A NULL byte will be appended to the target buffer. The function will return
the number of bytes written to the target buffer.
If the target buffer is NULL, the encoded string will be destructively edited
and the decoded data will be placed in the original string's buffer.
Base64 encoding always requires 4 bytes for each 3 bytes. Padding is added if
the raw data's length isn't devisable by 3. Hence, the target buffer should
be,
at least, `base64_len/4*3 + 3` long.
Returns the number of bytes actually written to the target buffer (excluding
the
NULL terminator byte).
*/
static int base64_decode(char* target, char* encoded, int base64_len) {
if (base64_len <= 0)
return -1;
if (!target)
target = encoded;
union Base64Parser section;
int written = 0;
// base64_encodes
// a struct that will be used to read the data.
while (base64_len >= 4) {
base64_len -= 4; // make sure we don't loop forever.
// copying the data allows us to write destructively to the same buffer
section.byte1.data = base64_decodes[(unsigned char)(*encoded)];
encoded++;
section.byte1.tail = (base64_decodes[(unsigned char)(*encoded)] >> 4);
section.byte2.head = base64_decodes[(unsigned char)(*encoded)];
encoded++;
section.byte2.tail = (base64_decodes[(unsigned char)(*encoded)] >> 2);
section.byte3.head = base64_decodes[(unsigned char)(*encoded)];
encoded++;
section.byte3.data = base64_decodes[(unsigned char)(*encoded)];
encoded++;
// write to the target buffer
*(target++) = section.bytes[0];
*(target++) = section.bytes[1];
*(target++) = section.bytes[2];
// count written bytes
written += section.bytes[2] ? 3 : section.bytes[1] ? 2 : 1;
}
// deal with the "tail" of the encoded stream
if (base64_len) {
// zero out data
section.bytes[0] = 0;
section.bytes[1] = 0;
section.bytes[2] = 0;
// byte 1 + 2 (2 might be padding)
section.byte1.data = base64_decodes[(unsigned char)*(encoded++)];
if (--base64_len) {
section.byte1.tail = base64_decodes[(unsigned char)(*encoded)] >> 4;
section.byte2.head = base64_decodes[(unsigned char)(*encoded)];
encoded++;
if (--base64_len) {
section.byte2.tail = base64_decodes[(unsigned char)(*encoded)] >> 4;
section.byte3.head = base64_decodes[(unsigned char)(*encoded)];
// --base64_len; // will always be 0 at this point (or it was 4)
}
}
// write to the target buffer
*(target++) = section.bytes[0];
if (section.bytes[1] || section.bytes[2])
*(target++) = section.bytes[1];
if (section.bytes[2])
*(target++) = section.bytes[2];
// count written bytes
written += section.bytes[2] ? 3 : section.bytes[1] ? 2 : 1;
}
*target = 0;
return written;
}
/*****************************************************************************
Hex Conversion
*/
#define hex2i(h) \
(((h) >= '0' && (h) <= '9') ? ((h) - '0') : (((h) | 32) - 'a' + 10))
#define i2hex(hi) (((hi) < 10) ? ('0' + (hi)) : ('A' + ((hi)-10)))
/**
Returns 1 if the string is HEX encoded (no non-valid hex values). Returns 0 if
it isn't.
White-Space is considered non-valid for this implementation.
*/
static int is_hex(const char* string, size_t length) {
// for (size_t i = 0; i < length; i++) {
// if (isxdigit(string[i]) == 0)
// return 0;
char c;
for (size_t i = 0; i < length; i++) {
c = string[i];
if ((!isspace(c)) &&
(c < '0' || c > 'z' ||
!((c >= 'a') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))))
return 0;
}
return 1;
}
/**
This will convert the string (byte stream) to a Hex string. This is not
cryptography, just conversion for pretty print.
The target buffer MUST have enough room for the expected data. The expected
data is double the length of the string + 1 byte for the NULL terminator byte.
A NULL byte will be appended to the target buffer. The function will return
the number of bytes written to the target buffer.
Returns the number of bytes actually written to the target buffer (excluding
the NULL terminator byte).
*/
static int str2hex(char* target, const char* string, size_t length) {
if (!target)
return -1;
size_t i = length;
target[(length << 1) + 1] = 0;
// go in reverse, so that target could be same as string.
while (i) {
--i;
target[(i << 1) + 1] = i2hex(string[i] & 0x0F);
target[(i << 1)] = i2hex(((uint8_t*)string)[i] >> 4);
}
return (length << 1);
}
/**
This will convert a Hex string to a byte string. This is not cryptography,
just conversion for pretty print.
The target buffer MUST have enough room for the expected data. The expected
data is half the length of the Hex string + 1 byte for the NULL terminator
byte.
A NULL byte will be appended to the target buffer. The function will return
the number of bytes written to the target buffer.
If the target buffer is NULL, the encoded string will be destructively edited
and the decoded data will be placed in the original string's buffer.
Returns the number of bytes actually written to the target buffer (excluding
the NULL terminator byte).
*/
static int hex2str(char* target, char* hex, size_t length) {
if (!target)
target = hex;
size_t i = 0;
size_t written = 0;
while (i + 1 < length) {
if (isspace(hex[i])) {
++i;
continue;
}
target[written] = (hex2i(hex[i]) << 4) | hex2i(hex[i + 1]);
++written;
i += 2;
}
if (i < length && !isspace(hex[i])) {
target[written] = hex2i(hex[i + 1]);
++written;
}
target[written] = 0;
return written;
}
#undef hex2i
#undef i2hex
/*****************************************************************************
Misc Helpers
*/
/**
Allocates memory and dumps the whole file into the memory allocated. Remember
to call `free` when done.
This function has some Unix specific properties that resolve links and user
folder referencing.
*/
static fdump_s* fdump(const char* file_path, size_t size_limit) {
struct stat f_data;
int file = -1;
fdump_s* container = NULL;
size_t file_path_len;
if (file_path == NULL || (file_path_len = strlen(file_path)) == 0 ||
file_path_len > PATH_MAX)
return NULL;
char real_public_path[PATH_MAX + 1];
real_public_path[PATH_MAX] = 0;
if (file_path[0] == '~' && getenv("HOME") && file_path_len <= PATH_MAX) {
strcpy(real_public_path, getenv("HOME"));
memcpy(real_public_path + strlen(real_public_path), file_path + 1,
file_path_len);
file_path = real_public_path;
}
if (stat(file_path, &f_data))
goto error;
if (size_limit == 0 || f_data.st_size < size_limit)
size_limit = f_data.st_size;
container = malloc(size_limit + sizeof(fdump_s));
container->length = size_limit;
if (!container)
goto error;
file = open(file_path, O_RDONLY);
if (file < 0)
goto error;
if (read(file, container->data, size_limit) < size_limit)
goto error;
close(file);
return container;
error:
if (container)
free(container), (container = NULL);
if (file >= 0)
close(file);
return 0;
}
/**
Uses an XOR key `xor_key_s` to encrypt / decrypt the data provided.
Encryption/decryption can be destructive (the target and the source can point
to the same object).
The key's `on_cycle` callback option should be utilized to er-calculate the
key every cycle. Otherwise, XOR encryption should be avoided.
*/
static int xor_crypt(xor_key_s* key,
void* target,
const void* source,
size_t length) {
if (!source || !key)
return -1;
if (!length)
return 0;
if (!target)
target = (void*)source;
if (key->on_cycle) {
/* loop to provide vector initialization when needed. */
while (key->position >= key->length) {
if (key->on_cycle(key))
return -1;
key->position -= key->length;
}
} else if (key->position >= key->length)
key->position = 0; /* no callback? no need for vector alterations. */
size_t i = 0;
/* start decryption */
while (length > i) {
while ((key->length - key->position >= 8) // we have 8 bytes for key.
&& ((i + 8) <= length) // we have 8 bytes for stream.
&& (((size_t)(target + i)) & 7) == 0 // target memory is aligned.
&& (((size_t)(source + i)) & 7) == 0 // source memory is aligned.
&& (((size_t)(key->key + key->position)) & 7) == 0 // key aligned.
) {
// fprintf(stderr, "XOR optimization used i= %lu, key pos = %lu.\n", i,
// key->position);
*((uint64_t*)(target + i)) =
*((uint64_t*)(source + i)) ^ *((uint64_t*)(key->key + key->position));
key->position += 8;
i += 8;
if (key->position < key->length)
continue;
if (key->on_cycle && key->on_cycle(key))
return -1;
key->position = 0;
}
// fprintf(stderr,
// "-- i= %lu, key pos = %lu, target %lu, source %lu , key %lu .\n",
// i,
// key->position, (((size_t)(target + i)) & 7),
// (((size_t)(source + i)) & 7), (((size_t)(key->key + i)) & 7));
if (i < length) {
// fprintf(stderr, "XOR single byte.\n");
*((uint8_t*)(target + i)) =
*((uint8_t*)(source + i)) ^ *((uint8_t*)(key->key + key->position));
++i;
++key->position;
if (key->position == key->length) {
if (key->on_cycle && key->on_cycle(key))
return -1;
key->position = 0;
}
}
}
return 0;
}