Scott Haseley 196bb3b684 Now using static buffers (and protecting with a semaphore) for kernel secprintf.
We have to do this because we need secure output in kmalloc.
2016-03-07 15:05:58 -05:00

207 lines
4.9 KiB
C

// Beware, this code is shared between the kernel and userspace.
#ifdef _KERNEL
#include <types.h>
#include <lib.h>
#include <kern/errno.h>
#else
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#endif
#include <kern/secure.h>
#include "sha256.h"
// The full length (with null) of the hex string of a byte array
#define HEXLEN(a) 2*a+1
#define SHA256_BLOCK_SIZE 64
#define SHA256_OUTPUT_SIZE 32
// Keep this divisible by 4, or else change make_salt()
#define SALT_BYTES 8
// inner and outer padding for HMAC.
static const unsigned char ipad[SHA256_BLOCK_SIZE] = { [0 ... SHA256_BLOCK_SIZE-1] = 0x36 };
static const unsigned char opad[SHA256_BLOCK_SIZE] = { [0 ... SHA256_BLOCK_SIZE-1] = 0x5c };
// Hack for not having a userspace malloc until ASST3. We 'allocate' these statuc buffers.
// This works because the process single-threaded.
#define NUM_BUFFERS 4
#define BUFFER_LEN 1024
static char temp_buffers[NUM_BUFFERS][BUFFER_LEN];
static int buf_num = 0;
#ifndef _KERNEL
static int did_random = 0;
#define NSEC_PER_MSEC 1000000ULL
#define MSEC_PER_SEC 1000ULL
#endif
// Both userspace and the kernel are using the temp buffers now.
static void * _alloc(size_t size)
{
(void)size;
void *ptr = temp_buffers[buf_num];
buf_num++;
buf_num = buf_num % NUM_BUFFERS;
return ptr;
}
static void _free(void *ptr)
{
(void)ptr;
}
/*
* hamc_sha256 follows FIPS 198-1 HMAC using sha256.
* See http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf for details.
* NOTE: This is only thread-safe if called from within secprintf()!!!
*/
static int hmac_sha256(const char *msg, size_t msg_len, const char *key, size_t key_len,
unsigned char output[SHA256_OUTPUT_SIZE])
{
// We use a total of 320 bytes of array data on the stack
unsigned char k0[SHA256_BLOCK_SIZE];
// Steps 1-3. Anything less than 64 bytes gets 0s appended.
memset(k0, 0, SHA256_BLOCK_SIZE);
if (key_len <= SHA256_BLOCK_SIZE) {
memcpy(k0, key, key_len);
} else {
mbedtls_sha256((unsigned char *)key, key_len, k0, 0);
}
// Steps 4 and 7.
unsigned char k_ipad[SHA256_BLOCK_SIZE];
unsigned char k_opad[SHA256_BLOCK_SIZE];
int i;
for (i = 0; i < SHA256_BLOCK_SIZE; i++) {
k_ipad[i] = k0[i] ^ ipad[i];
k_opad[i] = k0[i] ^ opad[i];
}
// Step 5 (K0 xor ipad) || msg
// We have no idea how big the message is so we allocate this on the heap.
unsigned char *data = (unsigned char *)_alloc(msg_len + SHA256_BLOCK_SIZE);
if (!data)
return ENOMEM;
memcpy(data, k_ipad, SHA256_BLOCK_SIZE);
memcpy(data+SHA256_BLOCK_SIZE, msg, msg_len);
// Step6: H((k0 xor ipad) || msg)
unsigned char h1[SHA256_OUTPUT_SIZE];
mbedtls_sha256(data, msg_len + SHA256_BLOCK_SIZE, h1, 0);
_free(data);
// Step 8: (k0 xor opad) || H((k0 xor ipad) || msg)
unsigned char inner[SHA256_OUTPUT_SIZE + SHA256_BLOCK_SIZE];
memcpy(inner, k_opad, SHA256_BLOCK_SIZE);
memcpy(inner + SHA256_BLOCK_SIZE, h1, SHA256_OUTPUT_SIZE);
// Step 9: Finally, H((k0 xor opad) || H((k0 xor ipad) || msg))
mbedtls_sha256(inner, SHA256_OUTPUT_SIZE + SHA256_BLOCK_SIZE, output, 0);
return 0;
}
static inline char to_hex(int n)
{
return n < 10 ? '0'+n : 'a' + (n-10);
}
static void array_to_hex(unsigned char *a, size_t len, char *res)
{
size_t i, j;
j = 0;
for (i = 0; i < len; i++) {
res[j++] = to_hex(a[i] >> 4);
res[j++] = to_hex(a[i] & 0xF);
}
res[j] = '\0';
}
static void make_salt(char *salt_str)
{
#ifndef _KERNEL
if (!did_random) {
did_random = 1;
time_t sec;
unsigned long ns;
unsigned long long ms;
__time(&sec, &ns);
ms = (unsigned long long)sec * MSEC_PER_SEC;
ms += (ns / NSEC_PER_MSEC);
srandom((unsigned long)ms);
}
#endif
// Compute salt value
uint32_t salt[SALT_BYTES/sizeof(uint32_t)];
size_t i;
for (i = 0; i < SALT_BYTES/sizeof(uint32_t); i++)
{
salt[i] = random();
}
// Convert to hex string
array_to_hex((unsigned char *)salt, SALT_BYTES, salt_str);
}
int hmac(const char *msg, size_t msg_len, const char *key, size_t key_len,
char **hash_str)
{
*hash_str = _alloc(HEXLEN(SHA256_OUTPUT_SIZE));
if (!(*hash_str))
return ENOMEM;
// Hash it
unsigned char hash[SHA256_OUTPUT_SIZE];
int res = hmac_sha256(msg, msg_len, key, key_len, hash);
if (res)
return res;
// Convert to hex string
array_to_hex(hash, SHA256_OUTPUT_SIZE, *hash_str);
return 0;
}
// NOTE: This is only thread-safe if called from within secprintf()!!!
int hmac_salted(const char *msg, size_t msg_len, const char *key, size_t key_len,
char **hash_str, char **salt_str)
{
*salt_str = _alloc(HEXLEN(SALT_BYTES));
if (!(*salt_str))
return ENOMEM;
// Create the salt value
make_salt(*salt_str);
// Concatenate the key and salt, with the resulting string being null-terminated
size_t key2_len = key_len + HEXLEN(SALT_BYTES)-1;
char *key2 = (char *)_alloc(key2_len+1);
if (!key2)
return ENOMEM;
strcpy(key2, key);
strcpy(key2+key_len, *salt_str);
key2[key2_len] = '\0';
// Hash it
return hmac(msg, msg_len, key2, key2_len, hash_str);
}