blob: 091163173006c9fa85bbcb63517e9e8098843f5b [file] [log] [blame] [raw]
#include "http-sha1-base64.h"
/*******************************************************************************
Setup
*/
#include <stdint.h>
#include <stdio.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
/*******************************************************************************
SHA-1 encoding
*/
/**
Bit rotation, inlined.
*/
#define left_rotate(i, bits) (((i) << (bits)) | ((i) >> (32 - (bits))))
/**
Initialize/reset the SHA-1 object.
*/
void sha1_init(sha1_s* 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->digest.str[20] = 0; // a NULL byte, if wanted
// s->initialized = 1;
s->finalized = 0;
s->buffer_pos = 0;
s->msg_length.i = 0;
}
/**
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;
for (int i = 0; i < 80; i++) {
if (i >= 16) {
// update the word value
t = s->buffer.i[i - 3] ^ s->buffer.i[i - 8] ^ s->buffer.i[i - 14] ^
s->buffer.i[i - 16];
s->buffer.i[i] = left_rotate(t, 1);
}
if (i < 20) {
t = ((b & c) | ((~b) & d)) + 0x5A827999;
} else if (i < 40) {
t = (b ^ c ^ d) + 0x6ED9EBA1;
} else if (i < 60) {
t = ((b & (c | d)) | (c & d)) + 0x8F1BBCDC;
} else {
t = (b ^ c ^ d) + 0xCA62C1D6;
}
t += left_rotate(a, 5) + e + s->buffer.i[i];
e = d;
d = c;
c = left_rotate(b, 30);
b = a;
a = t;
}
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.
*/
static int sha1_add_byte(sha1_s* s, unsigned char byte) {
// add a byte to the buffer, consider network byte order .
#ifdef __BIG_ENDIAN__
s->buffer.str[s->buffer_pos] = byte;
#else
s->buffer.str[s->buffer_pos ^ 3] = byte;
#endif
// update buffer position
s->buffer_pos++;
// review chunk (512 bits) processing
if (s->buffer_pos == 0) {
// s->buffer_pos wraps at 63 back to 0, so each 0 is the 512 bits
// (64 bytes) chunk marker to be processed.
sha1_process_buffer(s);
}
// returns the buffer's possition
return s->buffer_pos;
}
/**
Write data to the buffer.
*/
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++));
return 0;
}
/**
Finalize the SHA-1 object and return the resulting hash.
*/
char* sha1_result(sha1_s* s) {
// finalize the data if itw asn'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 (sha1_add_byte(s, 0) < 56)
;
// 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
#ifndef __BIG_ENDIAN__
// change back to little endian, if needed.
unsigned char i, t;
for (i = 0; i < 5; i++) {
t = s->digest.str[i * 5];
// reverse byte order for each uint32 "word".
s->digest.str[i * 5] = s->digest.str[i * 5 + 1];
s->digest.str[i * 5] = s->digest.str[i * 5 + 2];
s->digest.str[i * 5] = s->digest.str[i * 5 + 3];
s->digest.str[i * 5] = s->digest.str[i * 5 + 4];
s->digest.str[i * 5] = t;
}
#endif
}
// fprintf(stderr, "result requested, in hex, is %.8x%.8x%.8x%.8x%.8x\n",
// s->digest.i[0], s->digest.i[1], s->digest.i[2], s->digest.i[3],
// s->digest.i[4]);
return s->digest.str;
}
/*******************************************************************************
Base64
*/
/** 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.
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 + 4)
bytes.
Returns the number of bytes actually written to the target buffer
(including
the Base64 required padding and excluding a NULL terminator).
A NULL terminator char is NOT written to the target buffer.
*/
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] = '=';
} else if (len == 1) {
target[0] = base64_encodes[section->byte1.data];
target[1] = base64_encodes[section->byte1.tail << 4];
target[2] = '=';
target[3] = '=';
}
written += 4;
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 NOT 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. In this specific
case, a NULL byte will be appended to the decoded data.
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` long.
Returns the number of bytes actually written to the target buffer
(excluding a NULL terminator, which is only written if the target buffer is the
same as the data buffer).
*/
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;
}