commit e15daed8c0b6fc2fb821bace0493925cdb9e30cd Author: lolo859 Date: Tue Mar 31 22:15:00 2026 +0200 First commit, Vystem v0.1 diff --git a/Blastproof/blastproof.dsc b/Blastproof/blastproof.dsc new file mode 100644 index 0000000..a64ade0 --- /dev/null +++ b/Blastproof/blastproof.dsc @@ -0,0 +1,31 @@ +[Defines] + DSC_SPECIFICATION = 0x0001001C + PLATFORM_GUID = 040262ee-aebc-11f0-ba2c-10ffe08423a6 + PLATFORM_VERSION = 0.1 + PLATFORM_NAME = BlastproofEnv + SKUID_IDENTIFIER = DEFAULT + SUPPORTED_ARCHITECTURES = X64 + BUILD_TARGETS = RELEASE + +[Components] + Blastproof/src/main.inf + +[LibraryClasses] + UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + StackCheckLib|MdePkg/Library/StackCheckLib/StackCheckLib.inf + StackCheckFailureHookLib|MdePkg/Library/StackCheckFailureHookLibNull/StackCheckFailureHookLibNull.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + +[Packages] + MdePkg/MdePkg.dec diff --git a/Blastproof/bootanim/bootanim.cpp b/Blastproof/bootanim/bootanim.cpp new file mode 100644 index 0000000..64f0751 --- /dev/null +++ b/Blastproof/bootanim/bootanim.cpp @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: MPL-2.0 +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; +namespace fs=filesystem; +struct point { + long double x; + long double y; +}; +struct pixel { + uint16_t x; + uint16_t y; +}; +const point AP0={0,0}; +const point AP1={0.35,0.05}; +const point AP2={0.65,1.0}; +const point AP3={1,1}; +const point BP0={0,0}; +const point BP1={0.1,1.5}; +const point BP2={0.3,1.0}; +const point BP3={1,1}; +long double bezier_1d(long double p0,long double p1,long double p2,long double p3,long double t) { + long double u=1.0-t; + return (u*u*u)*p0+3*(u*u)*t*p1+3*u*(t*t)*p2+(t*t*t)*p3; +} +long double find_t(long double p0,long double p1,long double p2,long double p3,long double x_search) { + long double low=0.0; + long double high=1.0; + long double mid; + for (int i=0;i<20;i++) { + mid=(low+high)/2.0; + long double x_mid=bezier_1d(p0,p1,p2,p3,mid); + if (x_mid apply_bezier(point start,point p0,point p1,point p2,point p3,point end,int frames) { + point vect; + vect.x=end.x-start.x; + vect.y=end.y-start.y; + vector out; + out.reserve(frames); + for (int frame=0;frame& white_pixels) { + vector img(w*h*3,0); + for (auto& p:white_pixels) { + if (p.xmax) return max; + return (uint16_t)v; +} +int main (int argc,char **argv) { + if (argc!=5) { + cout<<"[Bootanim] Error: invalid argument."< logopixels(true_w*true_h,0); + int offset_x=(true_w-w_image)/2; + int offset_y=(true_h-h_image)/2; + for (int y=0;y=0 && dst_x=0 && dst_y endco; + for (int i=0;i127) { + point p; + p.x=i%true_w; + p.y=i/true_w; + endco.push_back(p); + } + } + random_device rd; + mt19937 gen(rd()); + vector randco; + randco.reserve(endco.size()); + uniform_real_distribution dist_angle(0.0L,2.0L*M_PI); + uniform_real_distribution dist_r1(0.0L,min((long double)true_w,(long double)true_h)/2); + uniform_real_distribution dist_r2(min((long double)true_w,(long double)true_h)/2,(max((long double)true_w,(long double)true_h)+300)/2); + uniform_int_distribution dist_c(0,1); + long double cx=(long double)true_w/2.0; + long double cy=(long double)true_h/2.0; + for (int i=0;i> codata(301); + for (int i=0;i> tempdata1; + vector> tempdata2; + tempdata1.reserve(endco.size()); + tempdata2.reserve(endco.size()); + for (int i=0;i> pixeldata(81); + for (int i=0;i<80;i++) { + for (int y=0;y data(pixeldata.size()*pixeldata[0].size()*sizeof(pixel)); + size_t offset=0; + for (size_t i=0;i=width-1 || px.x==0) { + px.x=0xFFFF; + } + if (px.y>=height-1 || px.y==0) { + px.y=0xFFFF; + } + memcpy(&data[offset],&px,sizeof(pixel)); + offset+=sizeof(pixel); + } + } + ofstream file("bootanim.bin",ios::binary); + unsigned char magic[8]={'B','o','o','t','A','n','i','m'}; + file.write((char*)magic,8); + vector header(4); + header[0]=width; + header[1]=height; + header[2]=pixeldata.size(); + header[3]=pixeldata[0].size(); + vector headerdata(4*8); + memcpy(headerdata.data(),header.data(),headerdata.size()); + file.write((char *)headerdata.data(),headerdata.size()); + file.write((char *)data.data(),data.size()); + file.close(); + cout<<"[Bootanim] Successfully build boot animation as bootanim.bin."< +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#if defined(_MSC_VER) || defined(__SYMBIAN32__) +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two ints fits in a signed short, 0 on overflow. +static int stbi__mul2shorts_valid(int a, int b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { + h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + stbi_uc x = stbi__get8(j->s); + while (x == 0xff) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + j->marker = stbi__skip_jpeg_junk_at_end(j); + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); + } else { + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); + } + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + int hit_zeof_once; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + if (!a->hit_zeof_once) { + // This is the first time we hit eof, insert 16 extra padding btis + // to allow us to keep going; if we actually consume any of them + // though, that is invalid data. This is caught later. + a->hit_zeof_once = 1; + a->num_bits += 16; // add 16 implicit zero bits + } else { + // We already inserted our extra 16 padding bits and are again + // out, this stream is actually prematurely terminated. + return -1; + } + } else { + stbi__fill_bits(a); + } + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + if (a->hit_zeof_once && a->num_bits < 16) { + // The first time we hit zeof, we inserted 16 extra zero bits into our bit + // buffer so the decoder can just do its speculative decoding. But if we + // actually consumed any of those bits (which is the case when num_bits < 16), + // the stream actually read past the end so it is malformed. + return stbi__err("unexpected end","Corrupt PNG"); + } + return 1; + } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (len > a->zout_end - zout) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + a->hit_zeof_once = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filter used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub +}; + +static int stbi__paeth(int a, int b, int c) +{ + // This formulation looks very different from the reference in the PNG spec, but is + // actually equivalent and has favorable data dependencies and admits straightforward + // generation of branch-free code, which helps performance significantly. + int thresh = c*3 - (a + b); + int lo = a < b ? a : b; + int hi = a < b ? b : a; + int t0 = (hi <= thresh) ? lo : c; + int t1 = (thresh <= lo) ? hi : t0; + return t1; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// adds an extra all-255 alpha channel +// dest == src is legal +// img_n must be 1 or 3 +static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) +{ + int i; + // must process data backwards since we allow dest==src + if (img_n == 1) { + for (i=x-1; i >= 0; --i) { + dest[i*2+1] = 255; + dest[i*2+0] = src[i]; + } + } else { + STBI_ASSERT(img_n == 3); + for (i=x-1; i >= 0; --i) { + dest[i*4+3] = 255; + dest[i*4+2] = src[i*3+2]; + dest[i*4+1] = src[i*3+1]; + dest[i*4+0] = src[i*3+0]; + } + } +} + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16 ? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + stbi_uc *filter_buf; + int all_ok = 1; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + // note: error exits here don't need to clean up a->out individually, + // stbi__do_png always does on error. + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG"); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + // Allocate two scan lines worth of filter workspace buffer. + filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0); + if (!filter_buf) return stbi__err("outofmem", "Out of memory"); + + // Filtering for low-bit-depth images + if (depth < 8) { + filter_bytes = 1; + width = img_width_bytes; + } + + for (j=0; j < y; ++j) { + // cur/prior filter buffers alternate + stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes; + stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes; + stbi_uc *dest = a->out + stride*j; + int nk = width * filter_bytes; + int filter = *raw++; + + // check filter type + if (filter > 4) { + all_ok = stbi__err("invalid filter","Corrupt PNG"); + break; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // perform actual filtering + switch (filter) { + case STBI__F_none: + memcpy(cur, raw, nk); + break; + case STBI__F_sub: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); + break; + case STBI__F_up: + for (k = 0; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + case STBI__F_avg: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); + break; + case STBI__F_paeth: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes])); + break; + case STBI__F_avg_first: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); + break; + } + + raw += nk; + + // expand decoded bits in cur to dest, also adding an extra alpha channel if desired + if (depth < 8) { + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + stbi_uc *in = cur; + stbi_uc *out = dest; + stbi_uc inb = 0; + stbi__uint32 nsmp = x*img_n; + + // expand bits to bytes first + if (depth == 4) { + for (i=0; i < nsmp; ++i) { + if ((i & 1) == 0) inb = *in++; + *out++ = scale * (inb >> 4); + inb <<= 4; + } + } else if (depth == 2) { + for (i=0; i < nsmp; ++i) { + if ((i & 3) == 0) inb = *in++; + *out++ = scale * (inb >> 6); + inb <<= 2; + } + } else { + STBI_ASSERT(depth == 1); + for (i=0; i < nsmp; ++i) { + if ((i & 7) == 0) inb = *in++; + *out++ = scale * (inb >> 7); + inb <<= 1; + } + } + + // insert alpha=255 values if desired + if (img_n != out_n) + stbi__create_png_alpha_expand8(dest, dest, x, img_n); + } else if (depth == 8) { + if (img_n == out_n) + memcpy(dest, cur, x*img_n); + else + stbi__create_png_alpha_expand8(dest, cur, x, img_n); + } else if (depth == 16) { + // convert the image data from big-endian to platform-native + stbi__uint16 *dest16 = (stbi__uint16*)dest; + stbi__uint32 nsmp = x*img_n; + + if (img_n == out_n) { + for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) + *dest16 = (cur[0] << 8) | cur[1]; + } else { + STBI_ASSERT(img_n+1 == out_n); + if (img_n == 1) { + for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = 0xffff; + } + } else { + STBI_ASSERT(img_n == 3); + for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = (cur[2] << 8) | cur[3]; + dest16[2] = (cur[4] << 8) | cur[5]; + dest16[3] = 0xffff; + } + } + } + } + } + + STBI_FREE(filter_buf); + if (!all_ok) return 0; + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + } + // even with SCAN_header, have to scan to see if we have a tRNS + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } + if (z->depth == 16) { + for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning + tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n && k < 3; ++k) + tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } + + if (req_comp && req_comp != s->img_n) { + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/Blastproof/config/bp_template.conf b/Blastproof/config/bp_template.conf new file mode 100644 index 0000000..6cb8088 --- /dev/null +++ b/Blastproof/config/bp_template.conf @@ -0,0 +1,48 @@ +# Blastproof config +# WARNING : DO NOT PUT ANY NON-ASCII CHAR IN THIS FILE + +# Serial port +# Define if the system is allowed to use the serial port +serial_port_enabled=true +# Define if system is allowed to push debug information on the serial port +serial_port_debuging=true +# Define if system is allowed to push errors on the serial port +serial_port_erroring=true + +# Graphics +# Define the font to use +font=bitra-ascii-medium.fbm +# Define whether or not the boot animation should be played +disable_boot_animation=true +# Set the following both to 0 use highest resolution available +# Define the screen default horizontal resolution +default_horizontal_resolution=1920 +# Define the screen default vertical resolution +default_vertical_resolution=1080 + +# InitFS +# Define the type GUID used for InitFS +# Warning : DO NOT modify this. It's common to all InitFS installation +initfs_partition_type_guid=8362b434-d825-11f0-a68f-10ffe08423a6 +# Define the InitFS partition GUID. Automatically generated by build script +initfs_partition_guid=UUID_GENERATION_NEEDED_INITFS +# Define the type GUID used SignSyst +# Warning : DO NOT modify this. It's common to all InitFS installation +signsyst_partition_type_guid=da0048b4-d826-11f0-b877-10ffe08423a6 +# Define the SignSyst partition GUID. Automatically generated by build script +signsyst_partition_guid=UUID_GENERATION_NEEDED_SIGNSYST + +# Shelter +# All the following elements are passed to the kernel +# Define the log level, value can range from 0 to 6 +kernel_log_level=0 +# Define wheither or not the kernel should test and benchmark all of his subsystems at boot time +kernel_test_benchmark=true +# Define the number of iterations for tests and benchmarks, must be between 0 and 10000 +kernel_bench_iterations=10000 +# Define if the log subsystem should output on the serial port +kernel_log_disable_serial_port=false +# Define if the serial port should be usable +kernel_disable_serial_port=false +# Define the size of the ring buffer for logging information. Value is in 4KB pages, ranging from 0 (ring logging disabled) to 65535 +kernel_log_ring_size=2048 diff --git a/Blastproof/fontgen/chars/0x21.png b/Blastproof/fontgen/chars/0x21.png new file mode 100644 index 0000000..cbbab20 Binary files /dev/null and b/Blastproof/fontgen/chars/0x21.png differ diff --git a/Blastproof/fontgen/chars/0x22.png b/Blastproof/fontgen/chars/0x22.png new file mode 100644 index 0000000..5090a78 Binary files /dev/null and b/Blastproof/fontgen/chars/0x22.png differ diff --git a/Blastproof/fontgen/chars/0x23.png b/Blastproof/fontgen/chars/0x23.png new file mode 100644 index 0000000..4366179 Binary files /dev/null and b/Blastproof/fontgen/chars/0x23.png differ diff --git a/Blastproof/fontgen/chars/0x24.png b/Blastproof/fontgen/chars/0x24.png new file mode 100644 index 0000000..747fe3c Binary files /dev/null and b/Blastproof/fontgen/chars/0x24.png differ diff --git a/Blastproof/fontgen/chars/0x25.png b/Blastproof/fontgen/chars/0x25.png new file mode 100644 index 0000000..a4e4a8b Binary files /dev/null and b/Blastproof/fontgen/chars/0x25.png differ diff --git a/Blastproof/fontgen/chars/0x26.png b/Blastproof/fontgen/chars/0x26.png new file mode 100644 index 0000000..5bc5887 Binary files /dev/null and b/Blastproof/fontgen/chars/0x26.png differ diff --git a/Blastproof/fontgen/chars/0x27.png b/Blastproof/fontgen/chars/0x27.png new file mode 100644 index 0000000..aa621b1 Binary files /dev/null and b/Blastproof/fontgen/chars/0x27.png differ diff --git a/Blastproof/fontgen/chars/0x28.png b/Blastproof/fontgen/chars/0x28.png new file mode 100644 index 0000000..9c947e7 Binary files /dev/null and b/Blastproof/fontgen/chars/0x28.png differ diff --git a/Blastproof/fontgen/chars/0x29.png b/Blastproof/fontgen/chars/0x29.png new file mode 100644 index 0000000..3cdbf3f Binary files /dev/null and b/Blastproof/fontgen/chars/0x29.png differ diff --git a/Blastproof/fontgen/chars/0x2A.png b/Blastproof/fontgen/chars/0x2A.png new file mode 100644 index 0000000..df6cd71 Binary files /dev/null and b/Blastproof/fontgen/chars/0x2A.png differ diff --git a/Blastproof/fontgen/chars/0x2B.png b/Blastproof/fontgen/chars/0x2B.png new file mode 100644 index 0000000..8c3aeea Binary files /dev/null and b/Blastproof/fontgen/chars/0x2B.png differ diff --git a/Blastproof/fontgen/chars/0x2C.png b/Blastproof/fontgen/chars/0x2C.png new file mode 100644 index 0000000..bcb6461 Binary files /dev/null and b/Blastproof/fontgen/chars/0x2C.png differ diff --git a/Blastproof/fontgen/chars/0x2D.png b/Blastproof/fontgen/chars/0x2D.png new file mode 100644 index 0000000..cd383f2 Binary files /dev/null and b/Blastproof/fontgen/chars/0x2D.png differ diff --git a/Blastproof/fontgen/chars/0x2E.png b/Blastproof/fontgen/chars/0x2E.png new file mode 100644 index 0000000..bbce983 Binary files /dev/null and b/Blastproof/fontgen/chars/0x2E.png differ diff --git a/Blastproof/fontgen/chars/0x2F.png b/Blastproof/fontgen/chars/0x2F.png new file mode 100644 index 0000000..5b89167 Binary files /dev/null and b/Blastproof/fontgen/chars/0x2F.png differ diff --git a/Blastproof/fontgen/chars/0x30.png b/Blastproof/fontgen/chars/0x30.png new file mode 100644 index 0000000..2334503 Binary files /dev/null and b/Blastproof/fontgen/chars/0x30.png differ diff --git a/Blastproof/fontgen/chars/0x31.png b/Blastproof/fontgen/chars/0x31.png new file mode 100644 index 0000000..fed6200 Binary files /dev/null and b/Blastproof/fontgen/chars/0x31.png differ diff --git a/Blastproof/fontgen/chars/0x32.png b/Blastproof/fontgen/chars/0x32.png new file mode 100644 index 0000000..f447dec Binary files /dev/null and b/Blastproof/fontgen/chars/0x32.png differ diff --git a/Blastproof/fontgen/chars/0x33.png b/Blastproof/fontgen/chars/0x33.png new file mode 100644 index 0000000..9463239 Binary files /dev/null and b/Blastproof/fontgen/chars/0x33.png differ diff --git a/Blastproof/fontgen/chars/0x34.png b/Blastproof/fontgen/chars/0x34.png new file mode 100644 index 0000000..79f1067 Binary files /dev/null and b/Blastproof/fontgen/chars/0x34.png differ diff --git a/Blastproof/fontgen/chars/0x35.png b/Blastproof/fontgen/chars/0x35.png new file mode 100644 index 0000000..6ce52a5 Binary files /dev/null and b/Blastproof/fontgen/chars/0x35.png differ diff --git a/Blastproof/fontgen/chars/0x36.png b/Blastproof/fontgen/chars/0x36.png new file mode 100644 index 0000000..e54a76a Binary files /dev/null and b/Blastproof/fontgen/chars/0x36.png differ diff --git a/Blastproof/fontgen/chars/0x37.png b/Blastproof/fontgen/chars/0x37.png new file mode 100644 index 0000000..9540f2a Binary files /dev/null and b/Blastproof/fontgen/chars/0x37.png differ diff --git a/Blastproof/fontgen/chars/0x38.png b/Blastproof/fontgen/chars/0x38.png new file mode 100644 index 0000000..5e432c6 Binary files /dev/null and b/Blastproof/fontgen/chars/0x38.png differ diff --git a/Blastproof/fontgen/chars/0x39.png b/Blastproof/fontgen/chars/0x39.png new file mode 100644 index 0000000..f2f1f08 Binary files /dev/null and b/Blastproof/fontgen/chars/0x39.png differ diff --git a/Blastproof/fontgen/chars/0x3A.png b/Blastproof/fontgen/chars/0x3A.png new file mode 100644 index 0000000..cbb3689 Binary files /dev/null and b/Blastproof/fontgen/chars/0x3A.png differ diff --git a/Blastproof/fontgen/chars/0x3B.png b/Blastproof/fontgen/chars/0x3B.png new file mode 100644 index 0000000..a1e257b Binary files /dev/null and b/Blastproof/fontgen/chars/0x3B.png differ diff --git a/Blastproof/fontgen/chars/0x3C.png b/Blastproof/fontgen/chars/0x3C.png new file mode 100644 index 0000000..c1c4fd8 Binary files /dev/null and b/Blastproof/fontgen/chars/0x3C.png differ diff --git a/Blastproof/fontgen/chars/0x3D.png b/Blastproof/fontgen/chars/0x3D.png new file mode 100644 index 0000000..3298e24 Binary files /dev/null and b/Blastproof/fontgen/chars/0x3D.png differ diff --git a/Blastproof/fontgen/chars/0x3E.png b/Blastproof/fontgen/chars/0x3E.png new file mode 100644 index 0000000..aac9a91 Binary files /dev/null and b/Blastproof/fontgen/chars/0x3E.png differ diff --git a/Blastproof/fontgen/chars/0x3F.png b/Blastproof/fontgen/chars/0x3F.png new file mode 100644 index 0000000..9cb9650 Binary files /dev/null and b/Blastproof/fontgen/chars/0x3F.png differ diff --git a/Blastproof/fontgen/chars/0x40.png b/Blastproof/fontgen/chars/0x40.png new file mode 100644 index 0000000..83feed0 Binary files /dev/null and b/Blastproof/fontgen/chars/0x40.png differ diff --git a/Blastproof/fontgen/chars/0x41.png b/Blastproof/fontgen/chars/0x41.png new file mode 100644 index 0000000..17f5cdd Binary files /dev/null and b/Blastproof/fontgen/chars/0x41.png differ diff --git a/Blastproof/fontgen/chars/0x42.png b/Blastproof/fontgen/chars/0x42.png new file mode 100644 index 0000000..2bb0a28 Binary files /dev/null and b/Blastproof/fontgen/chars/0x42.png differ diff --git a/Blastproof/fontgen/chars/0x43.png b/Blastproof/fontgen/chars/0x43.png new file mode 100644 index 0000000..5a237a0 Binary files /dev/null and b/Blastproof/fontgen/chars/0x43.png differ diff --git a/Blastproof/fontgen/chars/0x44.png b/Blastproof/fontgen/chars/0x44.png new file mode 100644 index 0000000..40304aa Binary files /dev/null and b/Blastproof/fontgen/chars/0x44.png differ diff --git a/Blastproof/fontgen/chars/0x45.png b/Blastproof/fontgen/chars/0x45.png new file mode 100644 index 0000000..6dab581 Binary files /dev/null and b/Blastproof/fontgen/chars/0x45.png differ diff --git a/Blastproof/fontgen/chars/0x46.png b/Blastproof/fontgen/chars/0x46.png new file mode 100644 index 0000000..c34f249 Binary files /dev/null and b/Blastproof/fontgen/chars/0x46.png differ diff --git a/Blastproof/fontgen/chars/0x47.png b/Blastproof/fontgen/chars/0x47.png new file mode 100644 index 0000000..92302df Binary files /dev/null and b/Blastproof/fontgen/chars/0x47.png differ diff --git a/Blastproof/fontgen/chars/0x48.png b/Blastproof/fontgen/chars/0x48.png new file mode 100644 index 0000000..e346ff9 Binary files /dev/null and b/Blastproof/fontgen/chars/0x48.png differ diff --git a/Blastproof/fontgen/chars/0x49.png b/Blastproof/fontgen/chars/0x49.png new file mode 100644 index 0000000..cb34383 Binary files /dev/null and b/Blastproof/fontgen/chars/0x49.png differ diff --git a/Blastproof/fontgen/chars/0x4A.png b/Blastproof/fontgen/chars/0x4A.png new file mode 100644 index 0000000..426d1b5 Binary files /dev/null and b/Blastproof/fontgen/chars/0x4A.png differ diff --git a/Blastproof/fontgen/chars/0x4B.png b/Blastproof/fontgen/chars/0x4B.png new file mode 100644 index 0000000..4c76699 Binary files /dev/null and b/Blastproof/fontgen/chars/0x4B.png differ diff --git a/Blastproof/fontgen/chars/0x4C.png b/Blastproof/fontgen/chars/0x4C.png new file mode 100644 index 0000000..419c4e4 Binary files /dev/null and b/Blastproof/fontgen/chars/0x4C.png differ diff --git a/Blastproof/fontgen/chars/0x4D.png b/Blastproof/fontgen/chars/0x4D.png new file mode 100644 index 0000000..6a2e6bb Binary files /dev/null and b/Blastproof/fontgen/chars/0x4D.png differ diff --git a/Blastproof/fontgen/chars/0x4E.png b/Blastproof/fontgen/chars/0x4E.png new file mode 100644 index 0000000..43b50d2 Binary files /dev/null and b/Blastproof/fontgen/chars/0x4E.png differ diff --git a/Blastproof/fontgen/chars/0x4F.png b/Blastproof/fontgen/chars/0x4F.png new file mode 100644 index 0000000..1e4da91 Binary files /dev/null and b/Blastproof/fontgen/chars/0x4F.png differ diff --git a/Blastproof/fontgen/chars/0x50.png b/Blastproof/fontgen/chars/0x50.png new file mode 100644 index 0000000..87ddcba Binary files /dev/null and b/Blastproof/fontgen/chars/0x50.png differ diff --git a/Blastproof/fontgen/chars/0x51.png b/Blastproof/fontgen/chars/0x51.png new file mode 100644 index 0000000..804d5ba Binary files /dev/null and b/Blastproof/fontgen/chars/0x51.png differ diff --git a/Blastproof/fontgen/chars/0x52.png b/Blastproof/fontgen/chars/0x52.png new file mode 100644 index 0000000..e5e1cd0 Binary files /dev/null and b/Blastproof/fontgen/chars/0x52.png differ diff --git a/Blastproof/fontgen/chars/0x53.png b/Blastproof/fontgen/chars/0x53.png new file mode 100644 index 0000000..dcd1aa4 Binary files /dev/null and b/Blastproof/fontgen/chars/0x53.png differ diff --git a/Blastproof/fontgen/chars/0x54.png b/Blastproof/fontgen/chars/0x54.png new file mode 100644 index 0000000..aa67174 Binary files /dev/null and b/Blastproof/fontgen/chars/0x54.png differ diff --git a/Blastproof/fontgen/chars/0x55.png b/Blastproof/fontgen/chars/0x55.png new file mode 100644 index 0000000..7a2edee Binary files /dev/null and b/Blastproof/fontgen/chars/0x55.png differ diff --git a/Blastproof/fontgen/chars/0x56.png b/Blastproof/fontgen/chars/0x56.png new file mode 100644 index 0000000..d2fe7cd Binary files /dev/null and b/Blastproof/fontgen/chars/0x56.png differ diff --git a/Blastproof/fontgen/chars/0x57.png b/Blastproof/fontgen/chars/0x57.png new file mode 100644 index 0000000..7bf14df Binary files /dev/null and b/Blastproof/fontgen/chars/0x57.png differ diff --git a/Blastproof/fontgen/chars/0x58.png b/Blastproof/fontgen/chars/0x58.png new file mode 100644 index 0000000..2528cd3 Binary files /dev/null and b/Blastproof/fontgen/chars/0x58.png differ diff --git a/Blastproof/fontgen/chars/0x59.png b/Blastproof/fontgen/chars/0x59.png new file mode 100644 index 0000000..22366a1 Binary files /dev/null and b/Blastproof/fontgen/chars/0x59.png differ diff --git a/Blastproof/fontgen/chars/0x5A.png b/Blastproof/fontgen/chars/0x5A.png new file mode 100644 index 0000000..b1dc98e Binary files /dev/null and b/Blastproof/fontgen/chars/0x5A.png differ diff --git a/Blastproof/fontgen/chars/0x5B.png b/Blastproof/fontgen/chars/0x5B.png new file mode 100644 index 0000000..2c18463 Binary files /dev/null and b/Blastproof/fontgen/chars/0x5B.png differ diff --git a/Blastproof/fontgen/chars/0x5C.png b/Blastproof/fontgen/chars/0x5C.png new file mode 100644 index 0000000..69e2ce8 Binary files /dev/null and b/Blastproof/fontgen/chars/0x5C.png differ diff --git a/Blastproof/fontgen/chars/0x5D.png b/Blastproof/fontgen/chars/0x5D.png new file mode 100644 index 0000000..2ee2d11 Binary files /dev/null and b/Blastproof/fontgen/chars/0x5D.png differ diff --git a/Blastproof/fontgen/chars/0x5E.png b/Blastproof/fontgen/chars/0x5E.png new file mode 100644 index 0000000..848cc8e Binary files /dev/null and b/Blastproof/fontgen/chars/0x5E.png differ diff --git a/Blastproof/fontgen/chars/0x5F.png b/Blastproof/fontgen/chars/0x5F.png new file mode 100644 index 0000000..da8de1d Binary files /dev/null and b/Blastproof/fontgen/chars/0x5F.png differ diff --git a/Blastproof/fontgen/chars/0x60.png b/Blastproof/fontgen/chars/0x60.png new file mode 100644 index 0000000..893ec4e Binary files /dev/null and b/Blastproof/fontgen/chars/0x60.png differ diff --git a/Blastproof/fontgen/chars/0x61.png b/Blastproof/fontgen/chars/0x61.png new file mode 100644 index 0000000..c6a651b Binary files /dev/null and b/Blastproof/fontgen/chars/0x61.png differ diff --git a/Blastproof/fontgen/chars/0x62.png b/Blastproof/fontgen/chars/0x62.png new file mode 100644 index 0000000..428a4db Binary files /dev/null and b/Blastproof/fontgen/chars/0x62.png differ diff --git a/Blastproof/fontgen/chars/0x63.png b/Blastproof/fontgen/chars/0x63.png new file mode 100644 index 0000000..677683f Binary files /dev/null and b/Blastproof/fontgen/chars/0x63.png differ diff --git a/Blastproof/fontgen/chars/0x64.png b/Blastproof/fontgen/chars/0x64.png new file mode 100644 index 0000000..e8bcd24 Binary files /dev/null and b/Blastproof/fontgen/chars/0x64.png differ diff --git a/Blastproof/fontgen/chars/0x65.png b/Blastproof/fontgen/chars/0x65.png new file mode 100644 index 0000000..aa84624 Binary files /dev/null and b/Blastproof/fontgen/chars/0x65.png differ diff --git a/Blastproof/fontgen/chars/0x66.png b/Blastproof/fontgen/chars/0x66.png new file mode 100644 index 0000000..2bca455 Binary files /dev/null and b/Blastproof/fontgen/chars/0x66.png differ diff --git a/Blastproof/fontgen/chars/0x67.png b/Blastproof/fontgen/chars/0x67.png new file mode 100644 index 0000000..22da58c Binary files /dev/null and b/Blastproof/fontgen/chars/0x67.png differ diff --git a/Blastproof/fontgen/chars/0x68.png b/Blastproof/fontgen/chars/0x68.png new file mode 100644 index 0000000..6412356 Binary files /dev/null and b/Blastproof/fontgen/chars/0x68.png differ diff --git a/Blastproof/fontgen/chars/0x69.png b/Blastproof/fontgen/chars/0x69.png new file mode 100644 index 0000000..35838ea Binary files /dev/null and b/Blastproof/fontgen/chars/0x69.png differ diff --git a/Blastproof/fontgen/chars/0x6A.png b/Blastproof/fontgen/chars/0x6A.png new file mode 100644 index 0000000..b739243 Binary files /dev/null and b/Blastproof/fontgen/chars/0x6A.png differ diff --git a/Blastproof/fontgen/chars/0x6B.png b/Blastproof/fontgen/chars/0x6B.png new file mode 100644 index 0000000..6246bcc Binary files /dev/null and b/Blastproof/fontgen/chars/0x6B.png differ diff --git a/Blastproof/fontgen/chars/0x6C.png b/Blastproof/fontgen/chars/0x6C.png new file mode 100644 index 0000000..1c716b8 Binary files /dev/null and b/Blastproof/fontgen/chars/0x6C.png differ diff --git a/Blastproof/fontgen/chars/0x6D.png b/Blastproof/fontgen/chars/0x6D.png new file mode 100644 index 0000000..fc56a85 Binary files /dev/null and b/Blastproof/fontgen/chars/0x6D.png differ diff --git a/Blastproof/fontgen/chars/0x6E.png b/Blastproof/fontgen/chars/0x6E.png new file mode 100644 index 0000000..dc05ac3 Binary files /dev/null and b/Blastproof/fontgen/chars/0x6E.png differ diff --git a/Blastproof/fontgen/chars/0x6F.png b/Blastproof/fontgen/chars/0x6F.png new file mode 100644 index 0000000..e716bfd Binary files /dev/null and b/Blastproof/fontgen/chars/0x6F.png differ diff --git a/Blastproof/fontgen/chars/0x70.png b/Blastproof/fontgen/chars/0x70.png new file mode 100644 index 0000000..7c3ec19 Binary files /dev/null and b/Blastproof/fontgen/chars/0x70.png differ diff --git a/Blastproof/fontgen/chars/0x71.png b/Blastproof/fontgen/chars/0x71.png new file mode 100644 index 0000000..19ec5bb Binary files /dev/null and b/Blastproof/fontgen/chars/0x71.png differ diff --git a/Blastproof/fontgen/chars/0x72.png b/Blastproof/fontgen/chars/0x72.png new file mode 100644 index 0000000..5b31d57 Binary files /dev/null and b/Blastproof/fontgen/chars/0x72.png differ diff --git a/Blastproof/fontgen/chars/0x73.png b/Blastproof/fontgen/chars/0x73.png new file mode 100644 index 0000000..19fb8fe Binary files /dev/null and b/Blastproof/fontgen/chars/0x73.png differ diff --git a/Blastproof/fontgen/chars/0x74.png b/Blastproof/fontgen/chars/0x74.png new file mode 100644 index 0000000..1d1c12e Binary files /dev/null and b/Blastproof/fontgen/chars/0x74.png differ diff --git a/Blastproof/fontgen/chars/0x75.png b/Blastproof/fontgen/chars/0x75.png new file mode 100644 index 0000000..441ee8c Binary files /dev/null and b/Blastproof/fontgen/chars/0x75.png differ diff --git a/Blastproof/fontgen/chars/0x76.png b/Blastproof/fontgen/chars/0x76.png new file mode 100644 index 0000000..e422c0c Binary files /dev/null and b/Blastproof/fontgen/chars/0x76.png differ diff --git a/Blastproof/fontgen/chars/0x77.png b/Blastproof/fontgen/chars/0x77.png new file mode 100644 index 0000000..3f77cd1 Binary files /dev/null and b/Blastproof/fontgen/chars/0x77.png differ diff --git a/Blastproof/fontgen/chars/0x78.png b/Blastproof/fontgen/chars/0x78.png new file mode 100644 index 0000000..eb23df8 Binary files /dev/null and b/Blastproof/fontgen/chars/0x78.png differ diff --git a/Blastproof/fontgen/chars/0x79.png b/Blastproof/fontgen/chars/0x79.png new file mode 100644 index 0000000..648ab2a Binary files /dev/null and b/Blastproof/fontgen/chars/0x79.png differ diff --git a/Blastproof/fontgen/chars/0x7A.png b/Blastproof/fontgen/chars/0x7A.png new file mode 100644 index 0000000..1a41174 Binary files /dev/null and b/Blastproof/fontgen/chars/0x7A.png differ diff --git a/Blastproof/fontgen/chars/0x7B.png b/Blastproof/fontgen/chars/0x7B.png new file mode 100644 index 0000000..7b67921 Binary files /dev/null and b/Blastproof/fontgen/chars/0x7B.png differ diff --git a/Blastproof/fontgen/chars/0x7C.png b/Blastproof/fontgen/chars/0x7C.png new file mode 100644 index 0000000..c6fde3c Binary files /dev/null and b/Blastproof/fontgen/chars/0x7C.png differ diff --git a/Blastproof/fontgen/chars/0x7D.png b/Blastproof/fontgen/chars/0x7D.png new file mode 100644 index 0000000..f55e4e5 Binary files /dev/null and b/Blastproof/fontgen/chars/0x7D.png differ diff --git a/Blastproof/fontgen/chars/0x7E.png b/Blastproof/fontgen/chars/0x7E.png new file mode 100644 index 0000000..e0246f1 Binary files /dev/null and b/Blastproof/fontgen/chars/0x7E.png differ diff --git a/Blastproof/fontgen/chars/0xFF.png b/Blastproof/fontgen/chars/0xFF.png new file mode 100644 index 0000000..489945b Binary files /dev/null and b/Blastproof/fontgen/chars/0xFF.png differ diff --git a/Blastproof/fontgen/fontgen.cpp b/Blastproof/fontgen/fontgen.cpp new file mode 100644 index 0000000..b19d709 --- /dev/null +++ b/Blastproof/fontgen/fontgen.cpp @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: MPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" +using namespace std; +namespace fs=filesystem; +struct color { + uint8_t r,g,b; +}; +struct pixel { + bool shade_1; + bool check_1; + bool shade_2; + bool check_2; + bool shade_3; + bool check_3; + bool shade_4; + bool enabled; +}; +struct character { + uint32_t codepoint; + vector data; +}; +struct fbm_header { + uint8_t sig[3]={'F','B','M'}; + uint8_t encoding; + uint32_t charnum; + uint8_t width; + uint8_t height; +}; +uint32_t charname_to_uint32(string charname) { + uint32_t value=0; + stringstream ss; + ss<>value; + int shift=(8-charname.size())*4; + value<<=shift; + return value; +} +int get_shade_from_color(const color &c,const color gradient[16]) { + for (int i=0;i<16;++i) { + if (gradient[i].r==c.r && gradient[i].g==c.g && gradient[i].b==c.b) { + return i; + } + } + return -1; +} +uint8_t pixel_to_byte(pixel p) { + uint8_t out=0x00; + out|=(uint8_t)p.shade_1<<7; + out|=(uint8_t)p.check_1<<6; + out|=(uint8_t)p.shade_2<<5; + out|=(uint8_t)p.check_2<<4; + out|=(uint8_t)p.shade_3<<3; + out|=(uint8_t)p.check_3<<2; + out|=(uint8_t)p.shade_4<<1; + out|=(uint8_t)p.enabled; + return out; +} +map> chars_data; +vector known_encoding={"ascii","utf8","utf16"}; +int main(int argc,char **argv) { + if (argc!=5) { + cout<<"[Fontgen] Error: wrong amount of arguments."<(bg))) { + cout<<"[Fontgen] Error: invalid background color."<(ft))) { + cout<<"[Fontgen] Error: invalid font color."<(std::stoi(bg_color.substr(1,2),nullptr,16)); + start.g=static_cast(std::stoi(bg_color.substr(3,2),nullptr,16)); + start.b=static_cast(std::stoi(bg_color.substr(5,2),nullptr,16)); + end.r=static_cast(std::stoi(ft_color.substr(1,2),nullptr,16)); + end.g=static_cast(std::stoi(ft_color.substr(3,2),nullptr,16)); + end.b=static_cast(std::stoi(ft_color.substr(5,2),nullptr,16)); + color gradient[16]; + for (int i=0;i<16;++i) { + gradient[i].r=start.r+i*(end.r-start.r)/15.0f; + gradient[i].g=start.g+i*(end.g-start.g)/15.0f; + gradient[i].b=start.b+i*(end.b-start.b)/15.0f; + } + string encoding=string(argv[3]); + if (find(known_encoding.begin(),known_encoding.end(),encoding)==known_encoding.end()) { + cout<<"[Fontgen] Error: encoding not supported."<8) { + cout<<"[Fontgen] Warning: "< pixels(data,data+w*h*channel); + stbi_image_free(data); + chars_data[key]=std::move(pixels); + i++; + } + vector characters; + for (auto it:chars_data) { + character chara; + chara.codepoint=it.first; + uint8_t byte1=(chara.codepoint>>24) & 0xFF; + uint8_t byte2=(chara.codepoint>>16) & 0xFF; + uint8_t byte3=(chara.codepoint>>8) & 0xFF; + bool pcheck_1=(byte1>>1) & 1; + bool pcheck_2=(byte2>>3) & 1; + bool pcheck_3=(byte3>>5) & 1; + for (int y=0;y15 || i<0) { + cout<<"[Fontgen] Error: found a color that isn't in shade map. Codepoint that caused the error: "<>3)&1; + p.shade_2=(shade>>2)&1; + p.shade_3=(shade>>1)&1; + p.shade_4=(shade>>0)&1; + if (shade!=0) { + p.enabled=true; + } else { + p.enabled=false; + } + p.check_1=pcheck_1; + p.check_2=pcheck_2; + p.check_3=pcheck_3; + uint8_t byte=pixel_to_byte(p); + chara.data.push_back(byte); + } + } + characters.push_back(chara); + } + fbm_header header; + header.charnum=(uint32_t)characters.size(); + if (width>255 || width<1) { + cout<<"[Fontgen] Error: width isn't between 1 and 255."<255 || height<1) { + cout<<"[Fontgen] Error: height isn't between 1 and 255."< fbm_font; + size_t charsize=4+header.width*header.height; + fbm_font.resize(10+header.charnum*charsize); + memcpy(fbm_font.data(),header.sig,3); + memcpy(fbm_font.data()+3,&header.encoding,1); + memcpy(fbm_font.data()+4,&header.charnum,4); + memcpy(fbm_font.data()+8,&header.width,1); + memcpy(fbm_font.data()+9,&header.height,1); + for (int i=0;i(fbm_font.data()),fbm_font.size()); + fileout.close(); + cout<<"[Fontgen] Successfully generated font.fbm ("< +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#if defined(_MSC_VER) || defined(__SYMBIAN32__) +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two ints fits in a signed short, 0 on overflow. +static int stbi__mul2shorts_valid(int a, int b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { + h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + stbi_uc x = stbi__get8(j->s); + while (x == 0xff) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + j->marker = stbi__skip_jpeg_junk_at_end(j); + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); + } else { + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); + } + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + int hit_zeof_once; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + if (!a->hit_zeof_once) { + // This is the first time we hit eof, insert 16 extra padding btis + // to allow us to keep going; if we actually consume any of them + // though, that is invalid data. This is caught later. + a->hit_zeof_once = 1; + a->num_bits += 16; // add 16 implicit zero bits + } else { + // We already inserted our extra 16 padding bits and are again + // out, this stream is actually prematurely terminated. + return -1; + } + } else { + stbi__fill_bits(a); + } + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + if (a->hit_zeof_once && a->num_bits < 16) { + // The first time we hit zeof, we inserted 16 extra zero bits into our bit + // buffer so the decoder can just do its speculative decoding. But if we + // actually consumed any of those bits (which is the case when num_bits < 16), + // the stream actually read past the end so it is malformed. + return stbi__err("unexpected end","Corrupt PNG"); + } + return 1; + } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (len > a->zout_end - zout) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + a->hit_zeof_once = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filter used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub +}; + +static int stbi__paeth(int a, int b, int c) +{ + // This formulation looks very different from the reference in the PNG spec, but is + // actually equivalent and has favorable data dependencies and admits straightforward + // generation of branch-free code, which helps performance significantly. + int thresh = c*3 - (a + b); + int lo = a < b ? a : b; + int hi = a < b ? b : a; + int t0 = (hi <= thresh) ? lo : c; + int t1 = (thresh <= lo) ? hi : t0; + return t1; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// adds an extra all-255 alpha channel +// dest == src is legal +// img_n must be 1 or 3 +static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) +{ + int i; + // must process data backwards since we allow dest==src + if (img_n == 1) { + for (i=x-1; i >= 0; --i) { + dest[i*2+1] = 255; + dest[i*2+0] = src[i]; + } + } else { + STBI_ASSERT(img_n == 3); + for (i=x-1; i >= 0; --i) { + dest[i*4+3] = 255; + dest[i*4+2] = src[i*3+2]; + dest[i*4+1] = src[i*3+1]; + dest[i*4+0] = src[i*3+0]; + } + } +} + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16 ? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + stbi_uc *filter_buf; + int all_ok = 1; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + // note: error exits here don't need to clean up a->out individually, + // stbi__do_png always does on error. + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG"); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + // Allocate two scan lines worth of filter workspace buffer. + filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0); + if (!filter_buf) return stbi__err("outofmem", "Out of memory"); + + // Filtering for low-bit-depth images + if (depth < 8) { + filter_bytes = 1; + width = img_width_bytes; + } + + for (j=0; j < y; ++j) { + // cur/prior filter buffers alternate + stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes; + stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes; + stbi_uc *dest = a->out + stride*j; + int nk = width * filter_bytes; + int filter = *raw++; + + // check filter type + if (filter > 4) { + all_ok = stbi__err("invalid filter","Corrupt PNG"); + break; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // perform actual filtering + switch (filter) { + case STBI__F_none: + memcpy(cur, raw, nk); + break; + case STBI__F_sub: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); + break; + case STBI__F_up: + for (k = 0; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + case STBI__F_avg: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); + break; + case STBI__F_paeth: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes])); + break; + case STBI__F_avg_first: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); + break; + } + + raw += nk; + + // expand decoded bits in cur to dest, also adding an extra alpha channel if desired + if (depth < 8) { + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + stbi_uc *in = cur; + stbi_uc *out = dest; + stbi_uc inb = 0; + stbi__uint32 nsmp = x*img_n; + + // expand bits to bytes first + if (depth == 4) { + for (i=0; i < nsmp; ++i) { + if ((i & 1) == 0) inb = *in++; + *out++ = scale * (inb >> 4); + inb <<= 4; + } + } else if (depth == 2) { + for (i=0; i < nsmp; ++i) { + if ((i & 3) == 0) inb = *in++; + *out++ = scale * (inb >> 6); + inb <<= 2; + } + } else { + STBI_ASSERT(depth == 1); + for (i=0; i < nsmp; ++i) { + if ((i & 7) == 0) inb = *in++; + *out++ = scale * (inb >> 7); + inb <<= 1; + } + } + + // insert alpha=255 values if desired + if (img_n != out_n) + stbi__create_png_alpha_expand8(dest, dest, x, img_n); + } else if (depth == 8) { + if (img_n == out_n) + memcpy(dest, cur, x*img_n); + else + stbi__create_png_alpha_expand8(dest, cur, x, img_n); + } else if (depth == 16) { + // convert the image data from big-endian to platform-native + stbi__uint16 *dest16 = (stbi__uint16*)dest; + stbi__uint32 nsmp = x*img_n; + + if (img_n == out_n) { + for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) + *dest16 = (cur[0] << 8) | cur[1]; + } else { + STBI_ASSERT(img_n+1 == out_n); + if (img_n == 1) { + for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = 0xffff; + } + } else { + STBI_ASSERT(img_n == 3); + for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = (cur[2] << 8) | cur[3]; + dest16[2] = (cur[4] << 8) | cur[5]; + dest16[3] = 0xffff; + } + } + } + } + } + + STBI_FREE(filter_buf); + if (!all_ok) return 0; + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + } + // even with SCAN_header, have to scan to see if we have a tRNS + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } + if (z->depth == 16) { + for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning + tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n && k < 3; ++k) + tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } + + if (req_comp && req_comp != s->img_n) { + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/Blastproof/initfsgen/address.c b/Blastproof/initfsgen/address.c new file mode 100644 index 0000000..b136af8 --- /dev/null +++ b/Blastproof/initfsgen/address.c @@ -0,0 +1,104 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +/* + * Specify which level of Merkle tree (the "layer") we're working on + */ +void set_layer_addr(uint32_t addr[8], uint32_t layer) +{ + ((unsigned char *)addr)[SPX_OFFSET_LAYER] = (unsigned char)layer; +} + +/* + * Specify which Merkle tree within the level (the "tree address") we're working on + */ +void set_tree_addr(uint32_t addr[8], uint64_t tree) +{ +#if (SPX_TREE_HEIGHT * (SPX_D - 1)) > 64 + #error Subtree addressing is currently limited to at most 2^64 trees +#endif + ull_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_TREE], 8, tree ); +} + +/* + * Specify the reason we'll use this address structure for, that is, what + * hash will we compute with it. This is used so that unrelated types of + * hashes don't accidentally get the same address structure. The type will be + * one of the SPX_ADDR_TYPE constants + */ +void set_type(uint32_t addr[8], uint32_t type) +{ + ((unsigned char *)addr)[SPX_OFFSET_TYPE] = (unsigned char)type; +} + +/* + * Copy the layer and tree fields of the address structure. This is used + * when we're doing multiple types of hashes within the same Merkle tree + */ +void copy_subtree_addr(uint32_t out[8], const uint32_t in[8]) +{ + memcpy( out, in, SPX_OFFSET_TREE+8 ); +} + +/* These functions are used for OTS addresses. */ + +/* + * Specify which Merkle leaf we're working on; that is, which OTS keypair + * we're talking about. + */ +void set_keypair_addr(uint32_t addr[8], uint32_t keypair) +{ + u32_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_KP_ADDR], keypair); +} + +/* + * Copy the layer, tree and keypair fields of the address structure. This is + * used when we're doing multiple things within the same OTS keypair + */ +void copy_keypair_addr(uint32_t out[8], const uint32_t in[8]) +{ + memcpy( out, in, SPX_OFFSET_TREE+8 ); + memcpy( (unsigned char *)out + SPX_OFFSET_KP_ADDR, (unsigned char *)in + SPX_OFFSET_KP_ADDR, 4); +} + +/* + * Specify which Merkle chain within the OTS we're working with + * (the chain address) + */ +void set_chain_addr(uint32_t addr[8], uint32_t chain) +{ + ((unsigned char *)addr)[SPX_OFFSET_CHAIN_ADDR] = (unsigned char)chain; +} + +/* + * Specify where in the Merkle chain we are +* (the hash address) + */ +void set_hash_addr(uint32_t addr[8], uint32_t hash) +{ + ((unsigned char *)addr)[SPX_OFFSET_HASH_ADDR] = (unsigned char)hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +/* + * Specify the height of the node in the Merkle/FORS tree we are in + * (the tree height) + */ +void set_tree_height(uint32_t addr[8], uint32_t tree_height) +{ + ((unsigned char *)addr)[SPX_OFFSET_TREE_HGT] = (unsigned char)tree_height; +} + +/* + * Specify the distance from the left edge of the node in the Merkle/FORS tree + * (the tree index) + */ +void set_tree_index(uint32_t addr[8], uint32_t tree_index) +{ + u32_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_TREE_INDEX], tree_index ); +} diff --git a/Blastproof/initfsgen/address.h b/Blastproof/initfsgen/address.h new file mode 100644 index 0000000..49f8d66 --- /dev/null +++ b/Blastproof/initfsgen/address.h @@ -0,0 +1,51 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include +#include "params.h" + +/* The hash types that are passed to set_type */ +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 +#define SPX_ADDR_TYPE_WOTSPRF 5 +#define SPX_ADDR_TYPE_FORSPRF 6 + +#define set_layer_addr SPX_NAMESPACE(set_layer_addr) +void set_layer_addr(uint32_t addr[8], uint32_t layer); + +#define set_tree_addr SPX_NAMESPACE(set_tree_addr) +void set_tree_addr(uint32_t addr[8], uint64_t tree); + +#define set_type SPX_NAMESPACE(set_type) +void set_type(uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +#define copy_subtree_addr SPX_NAMESPACE(copy_subtree_addr) +void copy_subtree_addr(uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +#define set_keypair_addr SPX_NAMESPACE(set_keypair_addr) +void set_keypair_addr(uint32_t addr[8], uint32_t keypair); + +#define set_chain_addr SPX_NAMESPACE(set_chain_addr) +void set_chain_addr(uint32_t addr[8], uint32_t chain); + +#define set_hash_addr SPX_NAMESPACE(set_hash_addr) +void set_hash_addr(uint32_t addr[8], uint32_t hash); + +#define copy_keypair_addr SPX_NAMESPACE(copy_keypair_addr) +void copy_keypair_addr(uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +#define set_tree_height SPX_NAMESPACE(set_tree_height) +void set_tree_height(uint32_t addr[8], uint32_t tree_height); + +#define set_tree_index SPX_NAMESPACE(set_tree_index) +void set_tree_index(uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/Blastproof/initfsgen/api.h b/Blastproof/initfsgen/api.h new file mode 100644 index 0000000..d57a148 --- /dev/null +++ b/Blastproof/initfsgen/api.h @@ -0,0 +1,77 @@ +#ifndef SPX_API_H +#define SPX_API_H + +#include +#include + +#include "params.h" + +#define CRYPTO_ALGNAME "SPHINCS+" + +#define CRYPTO_SECRETKEYBYTES SPX_SK_BYTES +#define CRYPTO_PUBLICKEYBYTES SPX_PK_BYTES +#define CRYPTO_BYTES SPX_BYTES +#define CRYPTO_SEEDBYTES 3*SPX_N + +/* + * Returns the length of a secret key, in bytes + */ +unsigned long long crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +unsigned long long crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +unsigned long long crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +unsigned long long crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk, + const unsigned char *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int crypto_sign_keypair(unsigned char *pk, unsigned char *sk); + +/** + * Returns an array containing a detached signature. + */ +int crypto_sign_signature(uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int crypto_sign_verify(const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int crypto_sign(unsigned char *sm, unsigned long long *smlen, + const unsigned char *m, unsigned long long mlen, + const unsigned char *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int crypto_sign_open(unsigned char *m, unsigned long long *mlen, + const unsigned char *sm, unsigned long long smlen, + const unsigned char *pk); + +#endif diff --git a/Blastproof/initfsgen/build.sh b/Blastproof/initfsgen/build.sh new file mode 100755 index 0000000..0e45a6e --- /dev/null +++ b/Blastproof/initfsgen/build.sh @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: MPL-2.0 +gcc -c address.c fors.c hash_sha2.c merkle.c randombytes.c sign.c utils.c utilsx1.c wots.c wotsx1.c thash_sha2_robust.c sha2.c sha3.c -Ofast -march=native +g++ initfsgen.cpp address.o fors.o hash_sha2.o merkle.o randombytes.o sign.o utils.o utilsx1.o wots.o wotsx1.o thash_sha2_robust.o sha2.o sha3.o -o initfsgen -lcrypto -Ofast -march=native +rm *.o diff --git a/Blastproof/initfsgen/context.h b/Blastproof/initfsgen/context.h new file mode 100644 index 0000000..aded564 --- /dev/null +++ b/Blastproof/initfsgen/context.h @@ -0,0 +1,28 @@ +#ifndef SPX_CONTEXT_H +#define SPX_CONTEXT_H + +#include + +#include "params.h" + +typedef struct { + uint8_t pub_seed[SPX_N]; + uint8_t sk_seed[SPX_N]; + +#ifdef SPX_SHA2 + // sha256 state that absorbed pub_seed + uint8_t state_seeded[40]; + +# if SPX_SHA512 + // sha512 state that absorbed pub_seed + uint8_t state_seeded_512[72]; +# endif +#endif + +#ifdef SPX_HARAKA + uint64_t tweaked512_rc64[10][8]; + uint32_t tweaked256_rc32[10][8]; +#endif +} spx_ctx; + +#endif diff --git a/Blastproof/initfsgen/fors.c b/Blastproof/initfsgen/fors.c new file mode 100644 index 0000000..e6aa4b4 --- /dev/null +++ b/Blastproof/initfsgen/fors.c @@ -0,0 +1,161 @@ +#include +#include +#include + +#include "fors.h" +#include "utils.h" +#include "utilsx1.h" +#include "hash.h" +#include "thash.h" +#include "address.h" + +static void fors_gen_sk(unsigned char *sk, const spx_ctx *ctx, + uint32_t fors_leaf_addr[8]) +{ + prf_addr(sk, ctx, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const spx_ctx *ctx, + uint32_t fors_leaf_addr[8]) +{ + thash(leaf, sk, 1, ctx, fors_leaf_addr); +} + +struct fors_gen_leaf_info { + uint32_t leaf_addrx[8]; +}; + +static void fors_gen_leafx1(unsigned char *leaf, + const spx_ctx *ctx, + uint32_t addr_idx, void *info) +{ + struct fors_gen_leaf_info *fors_info = info; + uint32_t *fors_leaf_addr = fors_info->leaf_addrx; + + /* Only set the parts that the caller doesn't set */ + set_tree_index(fors_leaf_addr, addr_idx); + set_type(fors_leaf_addr, SPX_ADDR_TYPE_FORSPRF); + fors_gen_sk(leaf, ctx, fors_leaf_addr); + + set_type(fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + fors_sk_to_leaf(leaf, leaf, + ctx, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) +{ + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= ((m[offset >> 3] >> (offset & 0x7)) & 1u) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void fors_sign(unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const spx_ctx *ctx, + const uint32_t fors_addr[8]) +{ + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + struct fors_gen_leaf_info fors_info = {0}; + uint32_t *fors_leaf_addr = fors_info.leaf_addrx; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + copy_keypair_addr(fors_tree_addr, fors_addr); + copy_keypair_addr(fors_leaf_addr, fors_addr); + + copy_keypair_addr(fors_pk_addr, fors_addr); + set_type(fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + set_tree_height(fors_tree_addr, 0); + set_tree_index(fors_tree_addr, indices[i] + idx_offset); + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSPRF); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, ctx, fors_tree_addr); + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + treehashx1(roots + i*SPX_N, sig, ctx, + indices[i], idx_offset, SPX_FORS_HEIGHT, fors_gen_leafx1, + fors_tree_addr, &fors_info); + + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + thash(pk, roots, SPX_FORS_TREES, ctx, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void fors_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const spx_ctx* ctx, + const uint32_t fors_addr[8]) +{ + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + copy_keypair_addr(fors_tree_addr, fors_addr); + copy_keypair_addr(fors_pk_addr, fors_addr); + + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + set_type(fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + set_tree_height(fors_tree_addr, 0); + set_tree_index(fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, ctx, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + compute_root(roots + i*SPX_N, leaf, indices[i], idx_offset, + sig, SPX_FORS_HEIGHT, ctx, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + thash(pk, roots, SPX_FORS_TREES, ctx, fors_pk_addr); +} diff --git a/Blastproof/initfsgen/fors.h b/Blastproof/initfsgen/fors.h new file mode 100644 index 0000000..8d98017 --- /dev/null +++ b/Blastproof/initfsgen/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "context.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +#define fors_sign SPX_NAMESPACE(fors_sign) +void fors_sign(unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const spx_ctx* ctx, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +#define fors_pk_from_sig SPX_NAMESPACE(fors_pk_from_sig) +void fors_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const spx_ctx* ctx, + const uint32_t fors_addr[8]); + +#endif diff --git a/Blastproof/initfsgen/hash.h b/Blastproof/initfsgen/hash.h new file mode 100644 index 0000000..b141f09 --- /dev/null +++ b/Blastproof/initfsgen/hash.h @@ -0,0 +1,27 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include +#include "context.h" +#include "params.h" + +#define initialize_hash_function SPX_NAMESPACE(initialize_hash_function) +void initialize_hash_function(spx_ctx *ctx); + +#define prf_addr SPX_NAMESPACE(prf_addr) +void prf_addr(unsigned char *out, const spx_ctx *ctx, + const uint32_t addr[8]); + +#define gen_message_random SPX_NAMESPACE(gen_message_random) +void gen_message_random(unsigned char *R, const unsigned char *sk_prf, + const unsigned char *optrand, + const unsigned char *m, unsigned long long mlen, + const spx_ctx *ctx); + +#define hash_message SPX_NAMESPACE(hash_message) +void hash_message(unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, unsigned long long mlen, + const spx_ctx *ctx); + +#endif diff --git a/Blastproof/initfsgen/hash_sha2.c b/Blastproof/initfsgen/hash_sha2.c new file mode 100644 index 0000000..67098e6 --- /dev/null +++ b/Blastproof/initfsgen/hash_sha2.c @@ -0,0 +1,197 @@ +#include +#include + +#include "address.h" +#include "utils.h" +#include "params.h" +#include "hash.h" +#include "sha2.h" + +#if SPX_N >= 24 +#define SPX_SHAX_OUTPUT_BYTES SPX_SHA512_OUTPUT_BYTES +#define SPX_SHAX_BLOCK_BYTES SPX_SHA512_BLOCK_BYTES +#define shaX_inc_init sha512_inc_init +#define shaX_inc_blocks sha512_inc_blocks +#define shaX_inc_finalize sha512_inc_finalize +#define shaX sha512 +#define mgf1_X mgf1_512 +#else +#define SPX_SHAX_OUTPUT_BYTES SPX_SHA256_OUTPUT_BYTES +#define SPX_SHAX_BLOCK_BYTES SPX_SHA256_BLOCK_BYTES +#define shaX_inc_init sha256_inc_init +#define shaX_inc_blocks sha256_inc_blocks +#define shaX_inc_finalize sha256_inc_finalize +#define shaX sha256 +#define mgf1_X mgf1_256 +#endif + + +/* For SHA, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void initialize_hash_function(spx_ctx *ctx) +{ + seed_state(ctx); +} + +/* + * Computes PRF(pk_seed, sk_seed, addr). + */ +void prf_addr(unsigned char *out, const spx_ctx *ctx, + const uint32_t addr[8]) +{ + uint8_t sha2_state[40]; + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_N]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, ctx->state_seeded, 40 * sizeof(uint8_t)); + + /* Remainder: ADDR^c ‖ SK.seed */ + memcpy(buf, addr, SPX_SHA256_ADDR_BYTES); + memcpy(buf + SPX_SHA256_ADDR_BYTES, ctx->sk_seed, SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + SPX_N); + + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHAX_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void gen_message_random(unsigned char *R, const unsigned char *sk_prf, + const unsigned char *optrand, + const unsigned char *m, unsigned long long mlen, + const spx_ctx *ctx) +{ + (void)ctx; + + unsigned char buf[SPX_SHAX_BLOCK_BYTES + SPX_SHAX_OUTPUT_BYTES]; + uint8_t state[8 + SPX_SHAX_OUTPUT_BYTES]; + int i; + +#if SPX_N > SPX_SHAX_BLOCK_BYTES + #error "Currently only supports SPX_N of at most SPX_SHAX_BLOCK_BYTES" +#endif + + /* This implements HMAC-SHA */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHAX_BLOCK_BYTES - SPX_N); + + shaX_inc_init(state); + shaX_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHAX_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + shaX_inc_finalize(buf + SPX_SHAX_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHAX_BLOCK_BYTES - SPX_N); + shaX_inc_blocks(state, buf, 1); + + m += SPX_SHAX_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHAX_BLOCK_BYTES - SPX_N; + shaX_inc_finalize(buf + SPX_SHAX_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHAX_BLOCK_BYTES - SPX_N); + + shaX(buf, buf, SPX_SHAX_BLOCK_BYTES + SPX_SHAX_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void hash_message(unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, unsigned long long mlen, + const spx_ctx *ctx) +{ + (void)ctx; +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[2*SPX_N + SPX_SHAX_OUTPUT_BYTES]; + + /* Round to nearest multiple of SPX_SHAX_BLOCK_BYTES */ +#if (SPX_SHAX_BLOCK_BYTES & (SPX_SHAX_BLOCK_BYTES-1)) != 0 + #error "Assumes that SPX_SHAX_BLOCK_BYTES is a power of 2" +#endif +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHAX_BLOCK_BYTES - 1) & \ + -SPX_SHAX_BLOCK_BYTES) / SPX_SHAX_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[8 + SPX_SHAX_OUTPUT_BYTES]; + + shaX_inc_init(state); + + // seed: SHA-X(R ‖ PK.seed ‖ PK.root ‖ M) + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + shaX_inc_finalize(seed + 2*SPX_N, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + shaX_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + shaX_inc_finalize(seed + 2*SPX_N, state, m, mlen); + } + + // H_msg: MGF1-SHA-X(R ‖ PK.seed ‖ seed) + memcpy(seed, R, SPX_N); + memcpy(seed + SPX_N, pk, SPX_N); + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + mgf1_X(bufp, SPX_DGST_BYTES, seed, 2*SPX_N + SPX_SHAX_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + +#if SPX_TREE_BITS > 64 + #error For given height and depth, 64 bits cannot represent all subtrees +#endif + + if (SPX_D == 1) { + *tree = 0; + } else { + *tree = bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + } + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)bytes_to_ull(bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} + + diff --git a/Blastproof/initfsgen/initfsgen.cpp b/Blastproof/initfsgen/initfsgen.cpp new file mode 100644 index 0000000..8473592 --- /dev/null +++ b/Blastproof/initfsgen/initfsgen.cpp @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: MPL-2.0 +extern "C" { +#include "api.h" +#include "sha3.h" +} +#undef str +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; +namespace fs=filesystem; +#pragma pack(push,1) +struct initfs_header { + uint8_t sign[8]={'I','n','i','t','F','i','S','y'}; + uint16_t bootloader_version=0x0001; + uint16_t initfs_version=0x0001; + uint32_t os_version=0x00000001; + uint8_t installation_id[48]={0}; + uint64_t initfs_size=0; + uint64_t table_size=0; + uint64_t files_area_size=0; + uint64_t entries_width=256; + uint64_t entries_count=0; + uint64_t files_area_offset=0; + uint64_t entropy_check1=0; + uint64_t check1=0; + uint8_t entry_table_hash[64]={0}; + uint8_t files_area_hash[64]={0}; + uint8_t installation_id_hash_hash[64]={0}; + uint8_t padding[128]={0}; + uint8_t header_hash[64]={0}; +}; +#pragma pack(pop) +#pragma pack(push,1) +struct file_entry { + uint64_t file_offset=0; + uint64_t file_size=0; + uint8_t pk[64]={0}; + uint8_t hash[64]={0}; + char file_name[112]={0}; +}; +#pragma pack(pop) +#pragma pack(push,1) +struct signsyst_header { + uint8_t sign[8]={'S','i','g','n','S','y','s','t'}; + uint16_t bootloader_version=0x0001; + uint16_t initfs_version=0x0001; + uint32_t os_version=0x00000001; + uint8_t installation_id[48]={0}; + uint64_t signature_size=0; + uint64_t signature_count=0; + uint64_t signsyst_size=0; + uint64_t signature_block_size=0; + uint8_t signature_block_hash[64]; + uint8_t padding[288]={0}; + uint8_t header_hash[64]={0}; +}; +#pragma pack(pop) +void secure_erase(void *address,size_t size) { + explicit_bzero(address,size); +} +vector generate_padding(size_t x,unsigned char p) { + vector result(x,0); + size_t f0=0; + size_t f1=1; + while (f0 initfs_files_name; + vector initfs_files_size; + size_t total_size=0; + for (auto &entry:fs::directory_iterator(folder_path)) { + if (!entry.is_regular_file()) { + cout<<"[InitFSGen] Error: InitFS only support files. Following entry isn't a regular file : "< entropy_id; + entropy_id.resize(48); + ifstream random("/dev/urandom",ios::binary); + if (!random) { + cout<<"[InitFSGen] Error: Can't open secure entropy source."<(entropy_id.data()),entropy_id.size()); + if (random.gcount()!=entropy_id.size()) { + cout<<"[InitFSGen] Error: Can't read enougth entropy for installation id."< entropy; + entropy.resize(8); + random.read(reinterpret_cast(entropy.data()),entropy.size()); + if (random.gcount()!=entropy.size()) { + cout<<"[InitFSGen] Error: Can't read enougth entropy."<(header.installation_id_hash_hash),sizeof(header.installation_id_hash_hash)); + sha3(header.installation_id_hash_hash,sizeof(header.installation_id_hash_hash),header.installation_id_hash_hash,sizeof(header.installation_id_hash_hash)); + vector files_area; + vector files_entries_table; + vector sig_list; + sig_list.resize(CRYPTO_BYTES*header.entries_count); + size_t cursor=0; + files_area.resize(header.files_area_size); + uint8_t sk[CRYPTO_SECRETKEYBYTES]={0}; + for (size_t i=0;i(files_area.data()+cursor),initfs_files_size[i]); + if (file.gcount()!=initfs_files_size[i]) { + cout<<"[InitFSGen] Error: Couldn't read full file: "<(112)),entry.file_name); + entry.file_size=initfs_files_size[i]; + entry.file_offset=cursor; + sha3(files_area.data()+cursor,initfs_files_size[i],entry.hash,64); + if (crypto_sign_keypair(entry.pk,sk)) { + cout<<"[InitFSGen] Error: can't generate keypair for file: "< entries_table; + entries_table.resize(header.table_size); + for (size_t i=0;i(&header),sizeof(header)); + initfs_bin.write(reinterpret_cast(entries_table.data()),entries_table.size()); + initfs_bin.write(reinterpret_cast(files_area.data()),files_area.size()); + initfs_bin.close(); + ofstream signsyst_hash("signsyst-hash.bin",ios::binary); + if (!signsyst_hash) { + cout<<"[InitFSGen] Error: Can't open signsyst-hash.bin."< signsysthash(64,0); + sha3(&sign_header,sizeof(sign_header),signsysthash.data(),signsysthash.size()); + signsyst_hash.write(reinterpret_cast(signsysthash.data()),signsysthash.size()); + signsyst_hash.close(); + ofstream signsyst_bin("signsyst.bin",ios::binary); + if (!signsyst_bin) { + cout<<"[InitFSGen] Error: Can't open signsyst.bin."<(&sign_header),sizeof(sign_header)); + signsyst_bin.write(reinterpret_cast(sig_list.data()),sig_list.size()); + signsyst_bin.close(); + return 0; +} diff --git a/Blastproof/initfsgen/merkle.c b/Blastproof/initfsgen/merkle.c new file mode 100644 index 0000000..414f468 --- /dev/null +++ b/Blastproof/initfsgen/merkle.c @@ -0,0 +1,61 @@ +#include +#include + +#include "utils.h" +#include "utilsx1.h" +#include "wots.h" +#include "wotsx1.h" +#include "merkle.h" +#include "address.h" +#include "params.h" + +/* + * This generates a Merkle signature (WOTS signature followed by the Merkle + * authentication path). This is in this file because most of the complexity + * is involved with the WOTS signature; the Merkle authentication path logic + * is mostly hidden in treehashx4 + */ +void merkle_sign(uint8_t *sig, unsigned char *root, + const spx_ctx *ctx, + uint32_t wots_addr[8], uint32_t tree_addr[8], + uint32_t idx_leaf) +{ + unsigned char *auth_path = sig + SPX_WOTS_BYTES; + struct leaf_info_x1 info = { 0 }; + unsigned steps[ SPX_WOTS_LEN ]; + + info.wots_sig = sig; + chain_lengths(steps, root); + info.wots_steps = steps; + + set_type(&tree_addr[0], SPX_ADDR_TYPE_HASHTREE); + set_type(&info.pk_addr[0], SPX_ADDR_TYPE_WOTSPK); + copy_subtree_addr(&info.leaf_addr[0], wots_addr); + copy_subtree_addr(&info.pk_addr[0], wots_addr); + + info.wots_sign_leaf = idx_leaf; + + treehashx1(root, auth_path, ctx, + idx_leaf, 0, + SPX_TREE_HEIGHT, + wots_gen_leafx1, + tree_addr, &info); +} + +/* Compute root node of the top-most subtree. */ +void merkle_gen_root(unsigned char *root, const spx_ctx *ctx) +{ + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N + SPX_WOTS_BYTES]; + uint32_t top_tree_addr[8] = {0}; + uint32_t wots_addr[8] = {0}; + + set_layer_addr(top_tree_addr, SPX_D - 1); + set_layer_addr(wots_addr, SPX_D - 1); + + merkle_sign(auth_path, root, ctx, + wots_addr, top_tree_addr, + (uint32_t)~0 /* ~0 means "don't bother generating an auth path */ ); +} diff --git a/Blastproof/initfsgen/merkle.h b/Blastproof/initfsgen/merkle.h new file mode 100644 index 0000000..9ac2759 --- /dev/null +++ b/Blastproof/initfsgen/merkle.h @@ -0,0 +1,18 @@ +#if !defined( MERKLE_H_ ) +#define MERKLE_H_ + +#include + +/* Generate a Merkle signature (WOTS signature followed by the Merkle */ +/* authentication path) */ +#define merkle_sign SPX_NAMESPACE(merkle_sign) +void merkle_sign(uint8_t *sig, unsigned char *root, + const spx_ctx* ctx, + uint32_t wots_addr[8], uint32_t tree_addr[8], + uint32_t idx_leaf); + +/* Compute the root node of the top-most subtree. */ +#define merkle_gen_root SPX_NAMESPACE(merkle_gen_root) +void merkle_gen_root(unsigned char *root, const spx_ctx* ctx); + +#endif /* MERKLE_H_ */ diff --git a/Blastproof/initfsgen/params.h b/Blastproof/initfsgen/params.h new file mode 100644 index 0000000..6dc6974 --- /dev/null +++ b/Blastproof/initfsgen/params.h @@ -0,0 +1,3 @@ +#define str(s) #s +#define xstr(s) str(s) +#include "params/params-sphincs-sha2-256f.h" diff --git a/Blastproof/initfsgen/params/params-sphincs-sha2-256f.h b/Blastproof/initfsgen/params/params-sphincs-sha2-256f.h new file mode 100644 index 0000000..53c5bef --- /dev/null +++ b/Blastproof/initfsgen/params/params-sphincs-sha2-256f.h @@ -0,0 +1,85 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +#define SPX_NAMESPACE(s) SPX_##s + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 68 +/* Number of subtree layer. */ +#define SPX_D 17 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 9 +#define SPX_FORS_TREES 35 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* This is a SHA2-based parameter set, hence whether we use SHA-256 + * exclusively or we use both SHA-256 and SHA-512 is controlled by + * the following #define */ +#define SPX_SHA512 1 /* Use SHA-512 for H and T_l, l >= 2 */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#if SPX_WOTS_W == 256 + #define SPX_WOTS_LOGW 8 +#elif SPX_WOTS_W == 16 + #define SPX_WOTS_LOGW 4 +#else + #error SPX_WOTS_W assumed 16 or 256 +#endif + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#if SPX_WOTS_W == 256 + #if SPX_N <= 1 + #define SPX_WOTS_LEN2 1 + #elif SPX_N <= 256 + #define SPX_WOTS_LEN2 2 + #else + #error Did not precompute SPX_WOTS_LEN2 for n outside {2, .., 256} + #endif +#elif SPX_WOTS_W == 16 + #if SPX_N <= 8 + #define SPX_WOTS_LEN2 2 + #elif SPX_N <= 136 + #define SPX_WOTS_LEN2 3 + #elif SPX_N <= 256 + #define SPX_WOTS_LEN2 4 + #else + #error Did not precompute SPX_WOTS_LEN2 for n outside {2, .., 256} + #endif +#endif + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +#if SPX_TREE_HEIGHT * SPX_D != SPX_FULL_HEIGHT + #error SPX_D should always divide SPX_FULL_HEIGHT +#endif + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +#include "../sha2_offsets.h" + +#endif diff --git a/Blastproof/initfsgen/randombytes.c b/Blastproof/initfsgen/randombytes.c new file mode 100644 index 0000000..cfbca17 --- /dev/null +++ b/Blastproof/initfsgen/randombytes.c @@ -0,0 +1,43 @@ +/* +This code was taken from the SPHINCS reference implementation and is public domain. +*/ + +#include +#include + +#include "randombytes.h" + +static int fd = -1; + +void randombytes(unsigned char *x, unsigned long long xlen) +{ + unsigned long long i; + + if (fd == -1) { + for (;;) { + fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + break; + } + sleep(1); + } + } + + while (xlen > 0) { + if (xlen < 1048576) { + i = xlen; + } + else { + i = 1048576; + } + + i = (unsigned long long)read(fd, x, i); + if (i < 1) { + sleep(1); + continue; + } + + x += i; + xlen -= i; + } +} diff --git a/Blastproof/initfsgen/randombytes.h b/Blastproof/initfsgen/randombytes.h new file mode 100644 index 0000000..671c1b1 --- /dev/null +++ b/Blastproof/initfsgen/randombytes.h @@ -0,0 +1,6 @@ +#ifndef SPX_RANDOMBYTES_H +#define SPX_RANDOMBYTES_H + +extern void randombytes(unsigned char * x,unsigned long long xlen); + +#endif diff --git a/Blastproof/initfsgen/sha2.c b/Blastproof/initfsgen/sha2.c new file mode 100644 index 0000000..ef73047 --- /dev/null +++ b/Blastproof/initfsgen/sha2.c @@ -0,0 +1,700 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "utils.h" +#include "sha2.h" + +static uint32_t load_bigendian_32(const uint8_t *x) { + return (uint32_t)(x[3]) | (((uint32_t)(x[2])) << 8) | + (((uint32_t)(x[1])) << 16) | (((uint32_t)(x[0])) << 24); +} + +static uint64_t load_bigendian_64(const uint8_t *x) { + return (uint64_t)(x[7]) | (((uint64_t)(x[6])) << 8) | + (((uint64_t)(x[5])) << 16) | (((uint64_t)(x[4])) << 24) | + (((uint64_t)(x[3])) << 32) | (((uint64_t)(x[2])) << 40) | + (((uint64_t)(x[1])) << 48) | (((uint64_t)(x[0])) << 56); +} + +static void store_bigendian_32(uint8_t *x, uint64_t u) { + x[3] = (uint8_t) u; + u >>= 8; + x[2] = (uint8_t) u; + u >>= 8; + x[1] = (uint8_t) u; + u >>= 8; + x[0] = (uint8_t) u; +} + +static void store_bigendian_64(uint8_t *x, uint64_t u) { + x[7] = (uint8_t) u; + u >>= 8; + x[6] = (uint8_t) u; + u >>= 8; + x[5] = (uint8_t) u; + u >>= 8; + x[4] = (uint8_t) u; + u >>= 8; + x[3] = (uint8_t) u; + u >>= 8; + x[2] = (uint8_t) u; + u >>= 8; + x[1] = (uint8_t) u; + u >>= 8; + x[0] = (uint8_t) u; +} + +#define SHR(x, c) ((x) >> (c)) +#define ROTR_32(x, c) (((x) >> (c)) | ((x) << (32 - (c)))) +#define ROTR_64(x,c) (((x) >> (c)) | ((x) << (64 - (c)))) + +#define Ch(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +#define Sigma0_32(x) (ROTR_32(x, 2) ^ ROTR_32(x,13) ^ ROTR_32(x,22)) +#define Sigma1_32(x) (ROTR_32(x, 6) ^ ROTR_32(x,11) ^ ROTR_32(x,25)) +#define sigma0_32(x) (ROTR_32(x, 7) ^ ROTR_32(x,18) ^ SHR(x, 3)) +#define sigma1_32(x) (ROTR_32(x,17) ^ ROTR_32(x,19) ^ SHR(x,10)) + +#define Sigma0_64(x) (ROTR_64(x,28) ^ ROTR_64(x,34) ^ ROTR_64(x,39)) +#define Sigma1_64(x) (ROTR_64(x,14) ^ ROTR_64(x,18) ^ ROTR_64(x,41)) +#define sigma0_64(x) (ROTR_64(x, 1) ^ ROTR_64(x, 8) ^ SHR(x,7)) +#define sigma1_64(x) (ROTR_64(x,19) ^ ROTR_64(x,61) ^ SHR(x,6)) + +#define M_32(w0, w14, w9, w1) w0 = sigma1_32(w14) + (w9) + sigma0_32(w1) + (w0); +#define M_64(w0, w14, w9, w1) w0 = sigma1_64(w14) + (w9) + sigma0_64(w1) + (w0); + +#define EXPAND_32 \ + M_32(w0, w14, w9, w1) \ + M_32(w1, w15, w10, w2) \ + M_32(w2, w0, w11, w3) \ + M_32(w3, w1, w12, w4) \ + M_32(w4, w2, w13, w5) \ + M_32(w5, w3, w14, w6) \ + M_32(w6, w4, w15, w7) \ + M_32(w7, w5, w0, w8) \ + M_32(w8, w6, w1, w9) \ + M_32(w9, w7, w2, w10) \ + M_32(w10, w8, w3, w11) \ + M_32(w11, w9, w4, w12) \ + M_32(w12, w10, w5, w13) \ + M_32(w13, w11, w6, w14) \ + M_32(w14, w12, w7, w15) \ + M_32(w15, w13, w8, w0) + +#define EXPAND_64 \ + M_64(w0 ,w14,w9 ,w1 ) \ + M_64(w1 ,w15,w10,w2 ) \ + M_64(w2 ,w0 ,w11,w3 ) \ + M_64(w3 ,w1 ,w12,w4 ) \ + M_64(w4 ,w2 ,w13,w5 ) \ + M_64(w5 ,w3 ,w14,w6 ) \ + M_64(w6 ,w4 ,w15,w7 ) \ + M_64(w7 ,w5 ,w0 ,w8 ) \ + M_64(w8 ,w6 ,w1 ,w9 ) \ + M_64(w9 ,w7 ,w2 ,w10) \ + M_64(w10,w8 ,w3 ,w11) \ + M_64(w11,w9 ,w4 ,w12) \ + M_64(w12,w10,w5 ,w13) \ + M_64(w13,w11,w6 ,w14) \ + M_64(w14,w12,w7 ,w15) \ + M_64(w15,w13,w8 ,w0 ) + +#define F_32(w, k) \ + T1 = h + Sigma1_32(e) + Ch(e, f, g) + (k) + (w); \ + T2 = Sigma0_32(a) + Maj(a, b, c); \ + h = g; \ + g = f; \ + f = e; \ + e = d + T1; \ + d = c; \ + c = b; \ + b = a; \ + a = T1 + T2; + +#define F_64(w,k) \ + T1 = h + Sigma1_64(e) + Ch(e,f,g) + k + w; \ + T2 = Sigma0_64(a) + Maj(a,b,c); \ + h = g; \ + g = f; \ + f = e; \ + e = d + T1; \ + d = c; \ + c = b; \ + b = a; \ + a = T1 + T2; + +static size_t crypto_hashblocks_sha256(uint8_t *statebytes, + const uint8_t *in, size_t inlen) { + uint32_t state[8]; + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + uint32_t e; + uint32_t f; + uint32_t g; + uint32_t h; + uint32_t T1; + uint32_t T2; + + a = load_bigendian_32(statebytes + 0); + state[0] = a; + b = load_bigendian_32(statebytes + 4); + state[1] = b; + c = load_bigendian_32(statebytes + 8); + state[2] = c; + d = load_bigendian_32(statebytes + 12); + state[3] = d; + e = load_bigendian_32(statebytes + 16); + state[4] = e; + f = load_bigendian_32(statebytes + 20); + state[5] = f; + g = load_bigendian_32(statebytes + 24); + state[6] = g; + h = load_bigendian_32(statebytes + 28); + state[7] = h; + + while (inlen >= 64) { + uint32_t w0 = load_bigendian_32(in + 0); + uint32_t w1 = load_bigendian_32(in + 4); + uint32_t w2 = load_bigendian_32(in + 8); + uint32_t w3 = load_bigendian_32(in + 12); + uint32_t w4 = load_bigendian_32(in + 16); + uint32_t w5 = load_bigendian_32(in + 20); + uint32_t w6 = load_bigendian_32(in + 24); + uint32_t w7 = load_bigendian_32(in + 28); + uint32_t w8 = load_bigendian_32(in + 32); + uint32_t w9 = load_bigendian_32(in + 36); + uint32_t w10 = load_bigendian_32(in + 40); + uint32_t w11 = load_bigendian_32(in + 44); + uint32_t w12 = load_bigendian_32(in + 48); + uint32_t w13 = load_bigendian_32(in + 52); + uint32_t w14 = load_bigendian_32(in + 56); + uint32_t w15 = load_bigendian_32(in + 60); + + F_32(w0, 0x428a2f98) + F_32(w1, 0x71374491) + F_32(w2, 0xb5c0fbcf) + F_32(w3, 0xe9b5dba5) + F_32(w4, 0x3956c25b) + F_32(w5, 0x59f111f1) + F_32(w6, 0x923f82a4) + F_32(w7, 0xab1c5ed5) + F_32(w8, 0xd807aa98) + F_32(w9, 0x12835b01) + F_32(w10, 0x243185be) + F_32(w11, 0x550c7dc3) + F_32(w12, 0x72be5d74) + F_32(w13, 0x80deb1fe) + F_32(w14, 0x9bdc06a7) + F_32(w15, 0xc19bf174) + + EXPAND_32 + + F_32(w0, 0xe49b69c1) + F_32(w1, 0xefbe4786) + F_32(w2, 0x0fc19dc6) + F_32(w3, 0x240ca1cc) + F_32(w4, 0x2de92c6f) + F_32(w5, 0x4a7484aa) + F_32(w6, 0x5cb0a9dc) + F_32(w7, 0x76f988da) + F_32(w8, 0x983e5152) + F_32(w9, 0xa831c66d) + F_32(w10, 0xb00327c8) + F_32(w11, 0xbf597fc7) + F_32(w12, 0xc6e00bf3) + F_32(w13, 0xd5a79147) + F_32(w14, 0x06ca6351) + F_32(w15, 0x14292967) + + EXPAND_32 + + F_32(w0, 0x27b70a85) + F_32(w1, 0x2e1b2138) + F_32(w2, 0x4d2c6dfc) + F_32(w3, 0x53380d13) + F_32(w4, 0x650a7354) + F_32(w5, 0x766a0abb) + F_32(w6, 0x81c2c92e) + F_32(w7, 0x92722c85) + F_32(w8, 0xa2bfe8a1) + F_32(w9, 0xa81a664b) + F_32(w10, 0xc24b8b70) + F_32(w11, 0xc76c51a3) + F_32(w12, 0xd192e819) + F_32(w13, 0xd6990624) + F_32(w14, 0xf40e3585) + F_32(w15, 0x106aa070) + + EXPAND_32 + + F_32(w0, 0x19a4c116) + F_32(w1, 0x1e376c08) + F_32(w2, 0x2748774c) + F_32(w3, 0x34b0bcb5) + F_32(w4, 0x391c0cb3) + F_32(w5, 0x4ed8aa4a) + F_32(w6, 0x5b9cca4f) + F_32(w7, 0x682e6ff3) + F_32(w8, 0x748f82ee) + F_32(w9, 0x78a5636f) + F_32(w10, 0x84c87814) + F_32(w11, 0x8cc70208) + F_32(w12, 0x90befffa) + F_32(w13, 0xa4506ceb) + F_32(w14, 0xbef9a3f7) + F_32(w15, 0xc67178f2) + + a += state[0]; + b += state[1]; + c += state[2]; + d += state[3]; + e += state[4]; + f += state[5]; + g += state[6]; + h += state[7]; + + state[0] = a; + state[1] = b; + state[2] = c; + state[3] = d; + state[4] = e; + state[5] = f; + state[6] = g; + state[7] = h; + + in += 64; + inlen -= 64; + } + + store_bigendian_32(statebytes + 0, state[0]); + store_bigendian_32(statebytes + 4, state[1]); + store_bigendian_32(statebytes + 8, state[2]); + store_bigendian_32(statebytes + 12, state[3]); + store_bigendian_32(statebytes + 16, state[4]); + store_bigendian_32(statebytes + 20, state[5]); + store_bigendian_32(statebytes + 24, state[6]); + store_bigendian_32(statebytes + 28, state[7]); + + return inlen; +} + +static int crypto_hashblocks_sha512(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen) +{ + uint64_t state[8]; + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; + uint64_t e; + uint64_t f; + uint64_t g; + uint64_t h; + uint64_t T1; + uint64_t T2; + + a = load_bigendian_64(statebytes + 0); state[0] = a; + b = load_bigendian_64(statebytes + 8); state[1] = b; + c = load_bigendian_64(statebytes + 16); state[2] = c; + d = load_bigendian_64(statebytes + 24); state[3] = d; + e = load_bigendian_64(statebytes + 32); state[4] = e; + f = load_bigendian_64(statebytes + 40); state[5] = f; + g = load_bigendian_64(statebytes + 48); state[6] = g; + h = load_bigendian_64(statebytes + 56); state[7] = h; + + while (inlen >= 128) { + uint64_t w0 = load_bigendian_64(in + 0); + uint64_t w1 = load_bigendian_64(in + 8); + uint64_t w2 = load_bigendian_64(in + 16); + uint64_t w3 = load_bigendian_64(in + 24); + uint64_t w4 = load_bigendian_64(in + 32); + uint64_t w5 = load_bigendian_64(in + 40); + uint64_t w6 = load_bigendian_64(in + 48); + uint64_t w7 = load_bigendian_64(in + 56); + uint64_t w8 = load_bigendian_64(in + 64); + uint64_t w9 = load_bigendian_64(in + 72); + uint64_t w10 = load_bigendian_64(in + 80); + uint64_t w11 = load_bigendian_64(in + 88); + uint64_t w12 = load_bigendian_64(in + 96); + uint64_t w13 = load_bigendian_64(in + 104); + uint64_t w14 = load_bigendian_64(in + 112); + uint64_t w15 = load_bigendian_64(in + 120); + + F_64(w0 ,0x428a2f98d728ae22ULL) + F_64(w1 ,0x7137449123ef65cdULL) + F_64(w2 ,0xb5c0fbcfec4d3b2fULL) + F_64(w3 ,0xe9b5dba58189dbbcULL) + F_64(w4 ,0x3956c25bf348b538ULL) + F_64(w5 ,0x59f111f1b605d019ULL) + F_64(w6 ,0x923f82a4af194f9bULL) + F_64(w7 ,0xab1c5ed5da6d8118ULL) + F_64(w8 ,0xd807aa98a3030242ULL) + F_64(w9 ,0x12835b0145706fbeULL) + F_64(w10,0x243185be4ee4b28cULL) + F_64(w11,0x550c7dc3d5ffb4e2ULL) + F_64(w12,0x72be5d74f27b896fULL) + F_64(w13,0x80deb1fe3b1696b1ULL) + F_64(w14,0x9bdc06a725c71235ULL) + F_64(w15,0xc19bf174cf692694ULL) + + EXPAND_64 + + F_64(w0 ,0xe49b69c19ef14ad2ULL) + F_64(w1 ,0xefbe4786384f25e3ULL) + F_64(w2 ,0x0fc19dc68b8cd5b5ULL) + F_64(w3 ,0x240ca1cc77ac9c65ULL) + F_64(w4 ,0x2de92c6f592b0275ULL) + F_64(w5 ,0x4a7484aa6ea6e483ULL) + F_64(w6 ,0x5cb0a9dcbd41fbd4ULL) + F_64(w7 ,0x76f988da831153b5ULL) + F_64(w8 ,0x983e5152ee66dfabULL) + F_64(w9 ,0xa831c66d2db43210ULL) + F_64(w10,0xb00327c898fb213fULL) + F_64(w11,0xbf597fc7beef0ee4ULL) + F_64(w12,0xc6e00bf33da88fc2ULL) + F_64(w13,0xd5a79147930aa725ULL) + F_64(w14,0x06ca6351e003826fULL) + F_64(w15,0x142929670a0e6e70ULL) + + EXPAND_64 + + F_64(w0 ,0x27b70a8546d22ffcULL) + F_64(w1 ,0x2e1b21385c26c926ULL) + F_64(w2 ,0x4d2c6dfc5ac42aedULL) + F_64(w3 ,0x53380d139d95b3dfULL) + F_64(w4 ,0x650a73548baf63deULL) + F_64(w5 ,0x766a0abb3c77b2a8ULL) + F_64(w6 ,0x81c2c92e47edaee6ULL) + F_64(w7 ,0x92722c851482353bULL) + F_64(w8 ,0xa2bfe8a14cf10364ULL) + F_64(w9 ,0xa81a664bbc423001ULL) + F_64(w10,0xc24b8b70d0f89791ULL) + F_64(w11,0xc76c51a30654be30ULL) + F_64(w12,0xd192e819d6ef5218ULL) + F_64(w13,0xd69906245565a910ULL) + F_64(w14,0xf40e35855771202aULL) + F_64(w15,0x106aa07032bbd1b8ULL) + + EXPAND_64 + + F_64(w0 ,0x19a4c116b8d2d0c8ULL) + F_64(w1 ,0x1e376c085141ab53ULL) + F_64(w2 ,0x2748774cdf8eeb99ULL) + F_64(w3 ,0x34b0bcb5e19b48a8ULL) + F_64(w4 ,0x391c0cb3c5c95a63ULL) + F_64(w5 ,0x4ed8aa4ae3418acbULL) + F_64(w6 ,0x5b9cca4f7763e373ULL) + F_64(w7 ,0x682e6ff3d6b2b8a3ULL) + F_64(w8 ,0x748f82ee5defb2fcULL) + F_64(w9 ,0x78a5636f43172f60ULL) + F_64(w10,0x84c87814a1f0ab72ULL) + F_64(w11,0x8cc702081a6439ecULL) + F_64(w12,0x90befffa23631e28ULL) + F_64(w13,0xa4506cebde82bde9ULL) + F_64(w14,0xbef9a3f7b2c67915ULL) + F_64(w15,0xc67178f2e372532bULL) + + EXPAND_64 + + F_64(w0 ,0xca273eceea26619cULL) + F_64(w1 ,0xd186b8c721c0c207ULL) + F_64(w2 ,0xeada7dd6cde0eb1eULL) + F_64(w3 ,0xf57d4f7fee6ed178ULL) + F_64(w4 ,0x06f067aa72176fbaULL) + F_64(w5 ,0x0a637dc5a2c898a6ULL) + F_64(w6 ,0x113f9804bef90daeULL) + F_64(w7 ,0x1b710b35131c471bULL) + F_64(w8 ,0x28db77f523047d84ULL) + F_64(w9 ,0x32caab7b40c72493ULL) + F_64(w10,0x3c9ebe0a15c9bebcULL) + F_64(w11,0x431d67c49c100d4cULL) + F_64(w12,0x4cc5d4becb3e42b6ULL) + F_64(w13,0x597f299cfc657e2aULL) + F_64(w14,0x5fcb6fab3ad6faecULL) + F_64(w15,0x6c44198c4a475817ULL) + + a += state[0]; + b += state[1]; + c += state[2]; + d += state[3]; + e += state[4]; + f += state[5]; + g += state[6]; + h += state[7]; + + state[0] = a; + state[1] = b; + state[2] = c; + state[3] = d; + state[4] = e; + state[5] = f; + state[6] = g; + state[7] = h; + + in += 128; + inlen -= 128; + } + + store_bigendian_64(statebytes + 0,state[0]); + store_bigendian_64(statebytes + 8,state[1]); + store_bigendian_64(statebytes + 16,state[2]); + store_bigendian_64(statebytes + 24,state[3]); + store_bigendian_64(statebytes + 32,state[4]); + store_bigendian_64(statebytes + 40,state[5]); + store_bigendian_64(statebytes + 48,state[6]); + store_bigendian_64(statebytes + 56,state[7]); + + return inlen; +} + + +static const uint8_t iv_256[32] = { + 0x6a, 0x09, 0xe6, 0x67, 0xbb, 0x67, 0xae, 0x85, + 0x3c, 0x6e, 0xf3, 0x72, 0xa5, 0x4f, 0xf5, 0x3a, + 0x51, 0x0e, 0x52, 0x7f, 0x9b, 0x05, 0x68, 0x8c, + 0x1f, 0x83, 0xd9, 0xab, 0x5b, 0xe0, 0xcd, 0x19 +}; + +static const uint8_t iv_512[64] = { + 0x6a, 0x09, 0xe6, 0x67, 0xf3, 0xbc, 0xc9, 0x08, 0xbb, 0x67, 0xae, + 0x85, 0x84, 0xca, 0xa7, 0x3b, 0x3c, 0x6e, 0xf3, 0x72, 0xfe, 0x94, + 0xf8, 0x2b, 0xa5, 0x4f, 0xf5, 0x3a, 0x5f, 0x1d, 0x36, 0xf1, 0x51, + 0x0e, 0x52, 0x7f, 0xad, 0xe6, 0x82, 0xd1, 0x9b, 0x05, 0x68, 0x8c, + 0x2b, 0x3e, 0x6c, 0x1f, 0x1f, 0x83, 0xd9, 0xab, 0xfb, 0x41, 0xbd, + 0x6b, 0x5b, 0xe0, 0xcd, 0x19, 0x13, 0x7e, 0x21, 0x79 +}; + +void sha256_inc_init(uint8_t *state) { + for (size_t i = 0; i < 32; ++i) { + state[i] = iv_256[i]; + } + for (size_t i = 32; i < 40; ++i) { + state[i] = 0; + } +} + +void sha512_inc_init(uint8_t *state) { + for (size_t i = 0; i < 64; ++i) { + state[i] = iv_512[i]; + } + for (size_t i = 64; i < 72; ++i) { + state[i] = 0; + } +} + +void sha256_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks) { + uint64_t bytes = load_bigendian_64(state + 32); + + crypto_hashblocks_sha256(state, in, 64 * inblocks); + bytes += 64 * inblocks; + + store_bigendian_64(state + 32, bytes); +} + +void sha512_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks) { + uint64_t bytes = load_bigendian_64(state + 64); + + crypto_hashblocks_sha512(state, in, 128 * inblocks); + bytes += 128 * inblocks; + + store_bigendian_64(state + 64, bytes); +} + +void sha256_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen) { + uint8_t padded[128]; + uint64_t bytes = load_bigendian_64(state + 32) + inlen; + + crypto_hashblocks_sha256(state, in, inlen); + in += inlen; + inlen &= 63; + in -= inlen; + + for (size_t i = 0; i < inlen; ++i) { + padded[i] = in[i]; + } + padded[inlen] = 0x80; + + if (inlen < 56) { + for (size_t i = inlen + 1; i < 56; ++i) { + padded[i] = 0; + } + padded[56] = (uint8_t) (bytes >> 53); + padded[57] = (uint8_t) (bytes >> 45); + padded[58] = (uint8_t) (bytes >> 37); + padded[59] = (uint8_t) (bytes >> 29); + padded[60] = (uint8_t) (bytes >> 21); + padded[61] = (uint8_t) (bytes >> 13); + padded[62] = (uint8_t) (bytes >> 5); + padded[63] = (uint8_t) (bytes << 3); + crypto_hashblocks_sha256(state, padded, 64); + } else { + for (size_t i = inlen + 1; i < 120; ++i) { + padded[i] = 0; + } + padded[120] = (uint8_t) (bytes >> 53); + padded[121] = (uint8_t) (bytes >> 45); + padded[122] = (uint8_t) (bytes >> 37); + padded[123] = (uint8_t) (bytes >> 29); + padded[124] = (uint8_t) (bytes >> 21); + padded[125] = (uint8_t) (bytes >> 13); + padded[126] = (uint8_t) (bytes >> 5); + padded[127] = (uint8_t) (bytes << 3); + crypto_hashblocks_sha256(state, padded, 128); + } + + for (size_t i = 0; i < 32; ++i) { + out[i] = state[i]; + } + +} + +void sha512_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen) { + uint8_t padded[256]; + uint64_t bytes = load_bigendian_64(state + 64) + inlen; + + crypto_hashblocks_sha512(state, in, inlen); + in += inlen; + inlen &= 127; + in -= inlen; + + for (size_t i = 0; i < inlen; ++i) { + padded[i] = in[i]; + } + padded[inlen] = 0x80; + + if (inlen < 112) { + for (size_t i = inlen + 1; i < 119; ++i) { + padded[i] = 0; + } + padded[119] = (uint8_t) (bytes >> 61); + padded[120] = (uint8_t) (bytes >> 53); + padded[121] = (uint8_t) (bytes >> 45); + padded[122] = (uint8_t) (bytes >> 37); + padded[123] = (uint8_t) (bytes >> 29); + padded[124] = (uint8_t) (bytes >> 21); + padded[125] = (uint8_t) (bytes >> 13); + padded[126] = (uint8_t) (bytes >> 5); + padded[127] = (uint8_t) (bytes << 3); + crypto_hashblocks_sha512(state, padded, 128); + } else { + for (size_t i = inlen + 1; i < 247; ++i) { + padded[i] = 0; + } + padded[247] = (uint8_t) (bytes >> 61); + padded[248] = (uint8_t) (bytes >> 53); + padded[249] = (uint8_t) (bytes >> 45); + padded[250] = (uint8_t) (bytes >> 37); + padded[251] = (uint8_t) (bytes >> 29); + padded[252] = (uint8_t) (bytes >> 21); + padded[253] = (uint8_t) (bytes >> 13); + padded[254] = (uint8_t) (bytes >> 5); + padded[255] = (uint8_t) (bytes << 3); + crypto_hashblocks_sha512(state, padded, 256); + } + + for (size_t i = 0; i < 64; ++i) { + out[i] = state[i]; + } +} + +void sha256(uint8_t *out, const uint8_t *in, size_t inlen) { + uint8_t state[40]; + + sha256_inc_init(state); + sha256_inc_finalize(out, state, in, inlen); +} + +void sha512(uint8_t *out, const uint8_t *in, size_t inlen) { + uint8_t state[72]; + + sha512_inc_init(state); + sha512_inc_finalize(out, state, in, inlen); +} + +/** + * mgf1 function based on the SHA-256 hash function + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void mgf1_256(unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) +{ + SPX_VLA(uint8_t, inbuf, inlen+4); + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i+1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + u32_to_bytes(inbuf + inlen, i); + sha256(out, inbuf, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i*SPX_SHA256_OUTPUT_BYTES) { + u32_to_bytes(inbuf + inlen, i); + sha256(outbuf, inbuf, inlen + 4); + memcpy(out, outbuf, outlen - i*SPX_SHA256_OUTPUT_BYTES); + } +} + +/* + * mgf1 function based on the SHA-512 hash function + */ +void mgf1_512(unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) +{ + SPX_VLA(uint8_t, inbuf, inlen+4); + unsigned char outbuf[SPX_SHA512_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* While we can fit in at least another full block of SHA512 output.. */ + for (i = 0; (i+1)*SPX_SHA512_OUTPUT_BYTES <= outlen; i++) { + u32_to_bytes(inbuf + inlen, i); + sha512(out, inbuf, inlen + 4); + out += SPX_SHA512_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i*SPX_SHA512_OUTPUT_BYTES) { + u32_to_bytes(inbuf + inlen, i); + sha512(outbuf, inbuf, inlen + 4); + memcpy(out, outbuf, outlen - i*SPX_SHA512_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded and state_seeded_512, which can then be + * reused in thash + **/ +void seed_state(spx_ctx *ctx) { + uint8_t block[SPX_SHA512_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = ctx->pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA512_BLOCK_BYTES; ++i) { + block[i] = 0; + } + /* block has been properly initialized for both SHA-256 and SHA-512 */ + + sha256_inc_init(ctx->state_seeded); + sha256_inc_blocks(ctx->state_seeded, block, 1); +#if SPX_SHA512 + sha512_inc_init(ctx->state_seeded_512); + sha512_inc_blocks(ctx->state_seeded_512, block, 1); +#endif +} diff --git a/Blastproof/initfsgen/sha2.h b/Blastproof/initfsgen/sha2.h new file mode 100644 index 0000000..732ab4b --- /dev/null +++ b/Blastproof/initfsgen/sha2.h @@ -0,0 +1,43 @@ +#ifndef SPX_SHA2_H +#define SPX_SHA2_H + +#include "params.h" + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ + +#define SPX_SHA512_BLOCK_BYTES 128 +#define SPX_SHA512_OUTPUT_BYTES 64 + +#if SPX_SHA256_OUTPUT_BYTES < SPX_N + #error Linking against SHA-256 with N larger than 32 bytes is not supported +#endif + +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void sha256_inc_init(uint8_t *state); +void sha256_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks); +void sha256_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen); +void sha256(uint8_t *out, const uint8_t *in, size_t inlen); + +void sha512_inc_init(uint8_t *state); +void sha512_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks); +void sha512_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen); +void sha512(uint8_t *out, const uint8_t *in, size_t inlen); + +#define mgf1_256 SPX_NAMESPACE(mgf1_256) +void mgf1_256(unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen); + +#define mgf1_512 SPX_NAMESPACE(mgf1_512) +void mgf1_512(unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen); + +#define seed_state SPX_NAMESPACE(seed_state) +void seed_state(spx_ctx *ctx); + + +#endif diff --git a/Blastproof/initfsgen/sha2_offsets.h b/Blastproof/initfsgen/sha2_offsets.h new file mode 100644 index 0000000..49f7e85 --- /dev/null +++ b/Blastproof/initfsgen/sha2_offsets.h @@ -0,0 +1,20 @@ +#ifndef SHA2_OFFSETS_H_ +#define SHA2_OFFSETS_H_ + +/* + * Offsets of various fields in the address structure when we use SHA2 as + * the Sphincs+ hash function + */ + +#define SPX_OFFSET_LAYER 0 /* The byte used to specify the Merkle tree layer */ +#define SPX_OFFSET_TREE 1 /* The start of the 8 byte field used to specify the tree */ +#define SPX_OFFSET_TYPE 9 /* The byte used to specify the hash type (reason) */ +#define SPX_OFFSET_KP_ADDR 10 /* The start of the 4 byte field used to specify the key pair address */ +#define SPX_OFFSET_CHAIN_ADDR 17 /* The byte used to specify the chain address (which Winternitz chain) */ +#define SPX_OFFSET_HASH_ADDR 21 /* The byte used to specify the hash address (where in the Winternitz chain) */ +#define SPX_OFFSET_TREE_HGT 17 /* The byte used to specify the height of this node in the FORS or Merkle tree */ +#define SPX_OFFSET_TREE_INDEX 18 /* The start of the 4 byte field used to specify the node in the FORS or Merkle tree */ + +#define SPX_SHA2 1 + +#endif /* SHA2_OFFSETS_H_ */ diff --git a/Blastproof/initfsgen/sha3.c b/Blastproof/initfsgen/sha3.c new file mode 100644 index 0000000..fb4ef05 --- /dev/null +++ b/Blastproof/initfsgen/sha3.c @@ -0,0 +1,190 @@ +// sha3.c +// 19-Nov-11 Markku-Juhani O. Saarinen + +// Revised 07-Aug-15 to match with official release of FIPS PUB 202 "SHA3" +// Revised 03-Sep-15 for portability + OpenSSL - style API + +#include "sha3.h" + +// update the state with given number of rounds + +void sha3_keccakf(uint64_t st[25]) +{ + // constants + const uint64_t keccakf_rndc[24] = { + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, + 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 + }; + const int keccakf_rotc[24] = { + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 + }; + const int keccakf_piln[24] = { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 + }; + + // variables + int i, j, r; + uint64_t t, bc[5]; + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + uint8_t *v; + + // endianess conversion. this is redundant on little-endian targets + for (i = 0; i < 25; i++) { + v = (uint8_t *) &st[i]; + st[i] = ((uint64_t) v[0]) | (((uint64_t) v[1]) << 8) | + (((uint64_t) v[2]) << 16) | (((uint64_t) v[3]) << 24) | + (((uint64_t) v[4]) << 32) | (((uint64_t) v[5]) << 40) | + (((uint64_t) v[6]) << 48) | (((uint64_t) v[7]) << 56); + } +#endif + + // actual iteration + for (r = 0; r < KECCAKF_ROUNDS; r++) { + + // Theta + for (i = 0; i < 5; i++) + bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20]; + + for (i = 0; i < 5; i++) { + t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); + for (j = 0; j < 25; j += 5) + st[j + i] ^= t; + } + + // Rho Pi + t = st[1]; + for (i = 0; i < 24; i++) { + j = keccakf_piln[i]; + bc[0] = st[j]; + st[j] = ROTL64(t, keccakf_rotc[i]); + t = bc[0]; + } + + // Chi + for (j = 0; j < 25; j += 5) { + for (i = 0; i < 5; i++) + bc[i] = st[j + i]; + for (i = 0; i < 5; i++) + st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5]; + } + + // Iota + st[0] ^= keccakf_rndc[r]; + } + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + // endianess conversion. this is redundant on little-endian targets + for (i = 0; i < 25; i++) { + v = (uint8_t *) &st[i]; + t = st[i]; + v[0] = t & 0xFF; + v[1] = (t >> 8) & 0xFF; + v[2] = (t >> 16) & 0xFF; + v[3] = (t >> 24) & 0xFF; + v[4] = (t >> 32) & 0xFF; + v[5] = (t >> 40) & 0xFF; + v[6] = (t >> 48) & 0xFF; + v[7] = (t >> 56) & 0xFF; + } +#endif +} + +// Initialize the context for SHA3 + +int sha3_init(sha3_ctx_t *c, int mdlen) +{ + int i; + + for (i = 0; i < 25; i++) + c->st.q[i] = 0; + c->mdlen = mdlen; + c->rsiz = 200 - 2 * mdlen; + c->pt = 0; + + return 1; +} + +// update state with more data + +int sha3_update(sha3_ctx_t *c, const void *data, size_t len) +{ + size_t i; + int j; + + j = c->pt; + for (i = 0; i < len; i++) { + c->st.b[j++] ^= ((const uint8_t *) data)[i]; + if (j >= c->rsiz) { + sha3_keccakf(c->st.q); + j = 0; + } + } + c->pt = j; + + return 1; +} + +// finalize and output a hash + +int sha3_final(void *md, sha3_ctx_t *c) +{ + int i; + + c->st.b[c->pt] ^= 0x06; + c->st.b[c->rsiz - 1] ^= 0x80; + sha3_keccakf(c->st.q); + + for (i = 0; i < c->mdlen; i++) { + ((uint8_t *) md)[i] = c->st.b[i]; + } + + return 1; +} + +// compute a SHA-3 hash (md) of given byte length from "in" + +void *sha3(const void *in, size_t inlen, void *md, int mdlen) +{ + sha3_ctx_t sha3; + + sha3_init(&sha3, mdlen); + sha3_update(&sha3, in, inlen); + sha3_final(md, &sha3); + + return md; +} + +// SHAKE128 and SHAKE256 extensible-output functionality + +void shake_xof(sha3_ctx_t *c) +{ + c->st.b[c->pt] ^= 0x1F; + c->st.b[c->rsiz - 1] ^= 0x80; + sha3_keccakf(c->st.q); + c->pt = 0; +} + +void shake_out(sha3_ctx_t *c, void *out, size_t len) +{ + size_t i; + int j; + + j = c->pt; + for (i = 0; i < len; i++) { + if (j >= c->rsiz) { + sha3_keccakf(c->st.q); + j = 0; + } + ((uint8_t *) out)[i] = c->st.b[j++]; + } + c->pt = j; +} diff --git a/Blastproof/initfsgen/sha3.h b/Blastproof/initfsgen/sha3.h new file mode 100644 index 0000000..4431c49 --- /dev/null +++ b/Blastproof/initfsgen/sha3.h @@ -0,0 +1,46 @@ +// sha3.h +// 19-Nov-11 Markku-Juhani O. Saarinen + +#ifndef SHA3_H +#define SHA3_H + +#include +#include + +#ifndef KECCAKF_ROUNDS +#define KECCAKF_ROUNDS 24 +#endif + +#ifndef ROTL64 +#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) +#endif + +// state context +typedef struct { + union { // state: + uint8_t b[200]; // 8-bit bytes + uint64_t q[25]; // 64-bit words + } st; + int pt, rsiz, mdlen; // these don't overflow +} sha3_ctx_t; + +// Compression function. +void sha3_keccakf(uint64_t st[25]); + +// OpenSSL - like interfece +int sha3_init(sha3_ctx_t *c, int mdlen); // mdlen = hash output in bytes +int sha3_update(sha3_ctx_t *c, const void *data, size_t len); +int sha3_final(void *md, sha3_ctx_t *c); // digest goes to md + +// compute a sha3 hash (md) of given byte length from "in" +void *sha3(const void *in, size_t inlen, void *md, int mdlen); + +// SHAKE128 and SHAKE256 extensible-output functions +#define shake128_init(c) sha3_init(c, 16) +#define shake256_init(c) sha3_init(c, 32) +#define shake_update sha3_update + +void shake_xof(sha3_ctx_t *c); +void shake_out(sha3_ctx_t *c, void *out, size_t len); + +#endif diff --git a/Blastproof/initfsgen/sign.c b/Blastproof/initfsgen/sign.c new file mode 100644 index 0000000..a8e0c3c --- /dev/null +++ b/Blastproof/initfsgen/sign.c @@ -0,0 +1,287 @@ +#include +#include +#include + +#include "api.h" +#include "params.h" +#include "wots.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "address.h" +#include "randombytes.h" +#include "utils.h" +#include "merkle.h" + +/* + * Returns the length of a secret key, in bytes + */ +unsigned long long crypto_sign_secretkeybytes(void) +{ + return CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +unsigned long long crypto_sign_publickeybytes(void) +{ + return CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +unsigned long long crypto_sign_bytes(void) +{ + return CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +unsigned long long crypto_sign_seedbytes(void) +{ + return CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk, + const unsigned char *seed) +{ + spx_ctx ctx; + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2*SPX_N, SPX_N); + + memcpy(ctx.pub_seed, pk, SPX_N); + memcpy(ctx.sk_seed, sk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + /* Compute root node of the top-most subtree. */ + merkle_gen_root(sk + 3*SPX_N, &ctx); + + memcpy(pk + SPX_N, sk + 3*SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int crypto_sign_keypair(unsigned char *pk, unsigned char *sk) +{ + unsigned char seed[CRYPTO_SEEDBYTES]; + randombytes(seed, CRYPTO_SEEDBYTES); + crypto_sign_seed_keypair(pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int crypto_sign_signature(uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) +{ + spx_ctx ctx; + + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2*SPX_N; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + memcpy(ctx.sk_seed, sk, SPX_N); + memcpy(ctx.pub_seed, pk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + set_type(wots_addr, SPX_ADDR_TYPE_WOTS); + set_type(tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + gen_message_random(sig, sk_prf, optrand, m, mlen, &ctx); + + /* Derive the message digest and leaf index from R, PK and M. */ + hash_message(mhash, &tree, &idx_leaf, sig, pk, m, mlen, &ctx); + sig += SPX_N; + + set_tree_addr(wots_addr, tree); + set_keypair_addr(wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + fors_sign(sig, root, mhash, &ctx, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + set_layer_addr(tree_addr, i); + set_tree_addr(tree_addr, tree); + + copy_subtree_addr(wots_addr, tree_addr); + set_keypair_addr(wots_addr, idx_leaf); + + merkle_sign(sig, root, &ctx, wots_addr, tree_addr, idx_leaf); + sig += SPX_WOTS_BYTES + SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT)-1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int crypto_sign_verify(const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) +{ + spx_ctx ctx; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + memcpy(ctx.pub_seed, pk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + set_type(wots_addr, SPX_ADDR_TYPE_WOTS); + set_type(tree_addr, SPX_ADDR_TYPE_HASHTREE); + set_type(wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + hash_message(mhash, &tree, &idx_leaf, sig, pk, m, mlen, &ctx); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + set_tree_addr(wots_addr, tree); + set_keypair_addr(wots_addr, idx_leaf); + + fors_pk_from_sig(root, sig, mhash, &ctx, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + set_layer_addr(tree_addr, i); + set_tree_addr(tree_addr, tree); + + copy_subtree_addr(wots_addr, tree_addr); + set_keypair_addr(wots_addr, idx_leaf); + + copy_keypair_addr(wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + wots_pk_from_sig(wots_pk, sig, root, &ctx, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + thash(leaf, wots_pk, SPX_WOTS_LEN, &ctx, wots_pk_addr); + + /* Compute the root node of this subtree. */ + compute_root(root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + &ctx, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT)-1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N)) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int crypto_sign(unsigned char *sm, unsigned long long *smlen, + const unsigned char *m, unsigned long long mlen, + const unsigned char *sk) +{ + size_t siglen; + + crypto_sign_signature(sm, &siglen, m, (size_t)mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int crypto_sign_open(unsigned char *m, unsigned long long *mlen, + const unsigned char *sm, unsigned long long smlen, + const unsigned char *pk) +{ + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (crypto_sign_verify(sm, SPX_BYTES, sm + SPX_BYTES, (size_t)*mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/Blastproof/initfsgen/thash.h b/Blastproof/initfsgen/thash.h new file mode 100644 index 0000000..8687ccf --- /dev/null +++ b/Blastproof/initfsgen/thash.h @@ -0,0 +1,13 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include "context.h" +#include "params.h" + +#include + +#define thash SPX_NAMESPACE(thash) +void thash(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]); + +#endif diff --git a/Blastproof/initfsgen/thash_sha2_robust.c b/Blastproof/initfsgen/thash_sha2_robust.c new file mode 100644 index 0000000..67ca3da --- /dev/null +++ b/Blastproof/initfsgen/thash_sha2_robust.c @@ -0,0 +1,74 @@ +#include +#include + +#include "thash.h" +#include "address.h" +#include "params.h" +#include "utils.h" +#include "sha2.h" + +#if SPX_SHA512 +static void thash_512(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]); +#endif + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +void thash(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]) +{ +#if SPX_SHA512 + if (inblocks > 1) { + thash_512(out, in, inblocks, ctx, addr); + return; + } +#endif + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + SPX_VLA(uint8_t, bitmask, inblocks * SPX_N); + SPX_VLA(uint8_t, buf, SPX_N + SPX_SHA256_OUTPUT_BYTES + inblocks*SPX_N); + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, ctx->pub_seed, SPX_N); + memcpy(buf + SPX_N, addr, SPX_SHA256_ADDR_BYTES); + mgf1_256(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, ctx->state_seeded, 40 * sizeof(uint8_t)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + memcpy(out, outbuf, SPX_N); +} + +#if SPX_SHA512 +static void thash_512(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]) +{ + unsigned char outbuf[SPX_SHA512_OUTPUT_BYTES]; + SPX_VLA(uint8_t, bitmask, inblocks * SPX_N); + SPX_VLA(uint8_t, buf, SPX_N + SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + uint8_t sha2_state[72]; + unsigned int i; + + memcpy(buf, ctx->pub_seed, SPX_N); + memcpy(buf + SPX_N, addr, SPX_SHA256_ADDR_BYTES); + mgf1_512(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, ctx->state_seeded_512, 72 * sizeof(uint8_t)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha512_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + memcpy(out, outbuf, SPX_N); +} +#endif diff --git a/Blastproof/initfsgen/thash_sha2_simple.c b/Blastproof/initfsgen/thash_sha2_simple.c new file mode 100644 index 0000000..da58896 --- /dev/null +++ b/Blastproof/initfsgen/thash_sha2_simple.c @@ -0,0 +1,59 @@ +#include +#include + +#include "thash.h" +#include "address.h" +#include "params.h" +#include "utils.h" +#include "sha2.h" + +#if SPX_SHA512 +static void thash_512(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]); +#endif + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +void thash(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]) +{ +#if SPX_SHA512 + if (inblocks > 1) { + thash_512(out, in, inblocks, ctx, addr); + return; + } +#endif + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + uint8_t sha2_state[40]; + SPX_VLA(uint8_t, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, ctx->state_seeded, 40 * sizeof(uint8_t)); + + memcpy(buf, addr, SPX_SHA256_ADDR_BYTES); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + memcpy(out, outbuf, SPX_N); +} + +#if SPX_SHA512 +static void thash_512(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]) +{ + unsigned char outbuf[SPX_SHA512_OUTPUT_BYTES]; + uint8_t sha2_state[72]; + SPX_VLA(uint8_t, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, ctx->state_seeded_512, 72 * sizeof(uint8_t)); + + memcpy(buf, addr, SPX_SHA256_ADDR_BYTES); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha512_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + memcpy(out, outbuf, SPX_N); +} +#endif diff --git a/Blastproof/initfsgen/utils.c b/Blastproof/initfsgen/utils.c new file mode 100644 index 0000000..63d52ee --- /dev/null +++ b/Blastproof/initfsgen/utils.c @@ -0,0 +1,154 @@ +#include + +#include "utils.h" +#include "params.h" +#include "hash.h" +#include "thash.h" +#include "address.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void ull_to_bytes(unsigned char *out, unsigned int outlen, + unsigned long long in) +{ + int i; + + /* Iterate over out in decreasing order, for big-endianness. */ + for (i = (signed int)outlen - 1; i >= 0; i--) { + out[i] = in & 0xff; + in = in >> 8; + } +} + +void u32_to_bytes(unsigned char *out, uint32_t in) +{ + out[0] = (unsigned char)(in >> 24); + out[1] = (unsigned char)(in >> 16); + out[2] = (unsigned char)(in >> 8); + out[3] = (unsigned char)in; +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long bytes_to_ull(const unsigned char *in, unsigned int inlen) +{ + unsigned long long retval = 0; + unsigned int i; + + for (i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8*(inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void compute_root(unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const spx_ctx *ctx, uint32_t addr[8]) +{ + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } + else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + set_tree_height(addr, i + 1); + set_tree_index(addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + thash(buffer + SPX_N, buffer, 2, ctx, addr); + memcpy(buffer, auth_path, SPX_N); + } + else { + thash(buffer, buffer, 2, ctx, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + set_tree_height(addr, tree_height); + set_tree_index(addr, leaf_idx + idx_offset); + thash(root, buffer, 2, ctx, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void treehash(unsigned char *root, unsigned char *auth_path, const spx_ctx* ctx, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char* /* leaf */, + const spx_ctx* /* ctx */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) +{ + SPX_VLA(uint8_t, stack, (tree_height+1)*SPX_N); + SPX_VLA(unsigned int, heights, tree_height+1); + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset*SPX_N, ctx, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + set_tree_height(tree_addr, heights[offset - 1] + 1); + set_tree_index(tree_addr, + tree_idx + (idx_offset >> (heights[offset-1] + 1))); + /* Hash the top-most nodes from the stack together. */ + thash(stack + (offset - 2)*SPX_N, + stack + (offset - 2)*SPX_N, 2, ctx, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} diff --git a/Blastproof/initfsgen/utils.h b/Blastproof/initfsgen/utils.h new file mode 100644 index 0000000..b13502c --- /dev/null +++ b/Blastproof/initfsgen/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include +#include "params.h" +#include "context.h" + + +/* To support MSVC use alloca() instead of VLAs. See #20. */ +#ifdef _MSC_VER +/* MSVC defines _alloca in malloc.h */ +# include +/* Note: _malloca(), which is recommended over deprecated _alloca, + requires that you call _freea(). So we stick with _alloca */ +# define SPX_VLA(__t,__x,__s) __t *__x = (__t*)_alloca((__s)*sizeof(__t)) +#else +# define SPX_VLA(__t,__x,__s) __t __x[__s] +#endif + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +#define ull_to_bytes SPX_NAMESPACE(ull_to_bytes) +void ull_to_bytes(unsigned char *out, unsigned int outlen, + unsigned long long in); +#define u32_to_bytes SPX_NAMESPACE(u32_to_bytes) +void u32_to_bytes(unsigned char *out, uint32_t in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +#define bytes_to_ull SPX_NAMESPACE(bytes_to_ull) +unsigned long long bytes_to_ull(const unsigned char *in, unsigned int inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +#define compute_root SPX_NAMESPACE(compute_root) +void compute_root(unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const spx_ctx *ctx, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +#define treehash SPX_NAMESPACE(treehash) +void treehash(unsigned char *root, unsigned char *auth_path, + const spx_ctx* ctx, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char* /* leaf */, + const spx_ctx* ctx /* ctx */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + + +#endif diff --git a/Blastproof/initfsgen/utilsx1.c b/Blastproof/initfsgen/utilsx1.c new file mode 100644 index 0000000..f6a6700 --- /dev/null +++ b/Blastproof/initfsgen/utilsx1.c @@ -0,0 +1,100 @@ +#include + +#include "utils.h" +#include "utilsx1.h" +#include "params.h" +#include "thash.h" +#include "address.h" + +/* + * Generate the entire Merkle tree, computing the authentication path for + * leaf_idx, and the resulting root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE) + * + * This expects tree_addr to be initialized to the addr structures for the + * Merkle tree nodes + * + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + * + * This works by using the standard Merkle tree building algorithm, + */ +void treehashx1(unsigned char *root, unsigned char *auth_path, + const spx_ctx* ctx, + uint32_t leaf_idx, uint32_t idx_offset, + uint32_t tree_height, + void (*gen_leaf)( + unsigned char* /* Where to write the leaves */, + const spx_ctx* /* ctx */, + uint32_t idx, void *info), + uint32_t tree_addr[8], + void *info) +{ + /* This is where we keep the intermediate nodes */ + SPX_VLA(uint8_t, stack, tree_height*SPX_N); + + uint32_t idx; + uint32_t max_idx = (uint32_t)((1 << tree_height) - 1); + for (idx = 0;; idx++) { + unsigned char current[2*SPX_N]; /* Current logical node is at */ + /* index[SPX_N]. We do this to minimize the number of copies */ + /* needed during a thash */ + gen_leaf( ¤t[SPX_N], ctx, idx + idx_offset, + info ); + + /* Now combine the freshly generated right node with previously */ + /* generated left ones */ + uint32_t internal_idx_offset = idx_offset; + uint32_t internal_idx = idx; + uint32_t internal_leaf = leaf_idx; + uint32_t h; /* The height we are in the Merkle tree */ + for (h=0;; h++, internal_idx >>= 1, internal_leaf >>= 1) { + + /* Check if we hit the top of the tree */ + if (h == tree_height) { + /* We hit the root; return it */ + memcpy( root, ¤t[SPX_N], SPX_N ); + return; + } + + /* + * Check if the node we have is a part of the + * authentication path; if it is, write it out + */ + if ((internal_idx ^ internal_leaf) == 0x01) { + memcpy( &auth_path[ h * SPX_N ], + ¤t[SPX_N], + SPX_N ); + } + + /* + * Check if we're at a left child; if so, stop going up the stack + * Exception: if we've reached the end of the tree, keep on going + * (so we combine the last 4 nodes into the one root node in two + * more iterations) + */ + if ((internal_idx & 1) == 0 && idx < max_idx) { + break; + } + + /* Ok, we're at a right node */ + /* Now combine the left and right logical nodes together */ + + /* Set the address of the node we're creating. */ + internal_idx_offset >>= 1; + set_tree_height(tree_addr, h + 1); + set_tree_index(tree_addr, internal_idx/2 + internal_idx_offset ); + + unsigned char *left = &stack[h * SPX_N]; + memcpy( ¤t[0], left, SPX_N ); + thash( ¤t[1 * SPX_N], + ¤t[0 * SPX_N], + 2, ctx, tree_addr); + } + + /* We've hit a left child; save the current for when we get the */ + /* corresponding right right */ + memcpy( &stack[h * SPX_N], ¤t[SPX_N], SPX_N); + } +} diff --git a/Blastproof/initfsgen/utilsx1.h b/Blastproof/initfsgen/utilsx1.h new file mode 100644 index 0000000..a7fcf15 --- /dev/null +++ b/Blastproof/initfsgen/utilsx1.h @@ -0,0 +1,26 @@ +#ifndef SPX_UTILSX4_H +#define SPX_UTILSX4_H + +#include +#include "params.h" +#include "context.h" + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +#define treehashx1 SPX_NAMESPACE(treehashx1) +void treehashx1(unsigned char *root, unsigned char *auth_path, + const spx_ctx* ctx, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char* /* Where to write the leaf */, + const spx_ctx* /* ctx */, + uint32_t addr_idx, void *info), + uint32_t tree_addrx4[8], void *info); + +#endif diff --git a/Blastproof/initfsgen/wots.c b/Blastproof/initfsgen/wots.c new file mode 100644 index 0000000..df83278 --- /dev/null +++ b/Blastproof/initfsgen/wots.c @@ -0,0 +1,112 @@ +#include +#include + +#include "utils.h" +#include "utilsx1.h" +#include "hash.h" +#include "thash.h" +#include "wots.h" +#include "wotsx1.h" +#include "address.h" +#include "params.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const spx_ctx *ctx, uint32_t addr[8]) +{ + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start+steps) && i < SPX_WOTS_W; i++) { + set_hash_addr(addr, i); + thash(out, out, 1, ctx, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const int out_len, + const unsigned char *input) +{ + int in = 0; + int out = 0; + unsigned char total; + int bits = 0; + int consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (total >> bits) & (SPX_WOTS_W - 1); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) +{ + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << ((8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)) % 8); + ull_to_bytes(csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +void chain_lengths(unsigned int *lengths, const unsigned char *msg) +{ + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void wots_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const spx_ctx *ctx, uint32_t addr[8]) +{ + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + set_chain_addr(addr, i); + gen_chain(pk + i*SPX_N, sig + i*SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], ctx, addr); + } +} diff --git a/Blastproof/initfsgen/wots.h b/Blastproof/initfsgen/wots.h new file mode 100644 index 0000000..7e77056 --- /dev/null +++ b/Blastproof/initfsgen/wots.h @@ -0,0 +1,25 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include + +#include "params.h" +#include "context.h" + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +#define wots_pk_from_sig SPX_NAMESPACE(wots_pk_from_sig) +void wots_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const spx_ctx *ctx, uint32_t addr[8]); + +/* + * Compute the chain lengths needed for a given message hash + */ +#define chain_lengths SPX_NAMESPACE(chain_lengths) +void chain_lengths(unsigned int *lengths, const unsigned char *msg); + +#endif diff --git a/Blastproof/initfsgen/wotsx1.c b/Blastproof/initfsgen/wotsx1.c new file mode 100644 index 0000000..dfb3780 --- /dev/null +++ b/Blastproof/initfsgen/wotsx1.c @@ -0,0 +1,73 @@ +#include +#include + +#include "utils.h" +#include "hash.h" +#include "thash.h" +#include "wots.h" +#include "wotsx1.h" +#include "address.h" +#include "params.h" + +/* + * This generates a WOTS public key + * It also generates the WOTS signature if leaf_info indicates + * that we're signing with this WOTS key + */ +void wots_gen_leafx1(unsigned char *dest, + const spx_ctx *ctx, + uint32_t leaf_idx, void *v_info) { + struct leaf_info_x1 *info = v_info; + uint32_t *leaf_addr = info->leaf_addr; + uint32_t *pk_addr = info->pk_addr; + unsigned int i, k; + unsigned char pk_buffer[ SPX_WOTS_BYTES ]; + unsigned char *buffer; + uint32_t wots_k_mask; + + if (leaf_idx == info->wots_sign_leaf) { + /* We're traversing the leaf that's signing; generate the WOTS */ + /* signature */ + wots_k_mask = 0; + } else { + /* Nope, we're just generating pk's; turn off the signature logic */ + wots_k_mask = (uint32_t)~0; + } + + set_keypair_addr( leaf_addr, leaf_idx ); + set_keypair_addr( pk_addr, leaf_idx ); + + for (i = 0, buffer = pk_buffer; i < SPX_WOTS_LEN; i++, buffer += SPX_N) { + uint32_t wots_k = info->wots_steps[i] | wots_k_mask; /* Set wots_k to */ + /* the step if we're generating a signature, ~0 if we're not */ + + /* Start with the secret seed */ + set_chain_addr(leaf_addr, i); + set_hash_addr(leaf_addr, 0); + set_type(leaf_addr, SPX_ADDR_TYPE_WOTSPRF); + + prf_addr(buffer, ctx, leaf_addr); + + set_type(leaf_addr, SPX_ADDR_TYPE_WOTS); + + /* Iterate down the WOTS chain */ + for (k=0;; k++) { + /* Check if this is the value that needs to be saved as a */ + /* part of the WOTS signature */ + if (k == wots_k) { + memcpy( info->wots_sig + i * SPX_N, buffer, SPX_N ); + } + + /* Check if we hit the top of the chain */ + if (k == SPX_WOTS_W - 1) break; + + /* Iterate one step on the chain */ + set_hash_addr(leaf_addr, k); + + thash(buffer, buffer, 1, ctx, leaf_addr); + } + } + + /* Do the final thash to generate the public keys */ + thash(dest, pk_buffer, SPX_WOTS_LEN, ctx, pk_addr); +} diff --git a/Blastproof/initfsgen/wotsx1.h b/Blastproof/initfsgen/wotsx1.h new file mode 100644 index 0000000..1257f81 --- /dev/null +++ b/Blastproof/initfsgen/wotsx1.h @@ -0,0 +1,36 @@ +#if !defined( WOTSX1_H_ ) +#define WOTSX1_H_ + +#include + +/* + * This is here to provide an interface to the internal wots_gen_leafx1 + * routine. While this routine is not referenced in the package outside of + * wots.c, it is called from the stand-alone benchmark code to characterize + * the performance + */ +struct leaf_info_x1 { + unsigned char *wots_sig; + uint32_t wots_sign_leaf; /* The index of the WOTS we're using to sign */ + uint32_t *wots_steps; + uint32_t leaf_addr[8]; + uint32_t pk_addr[8]; +}; + +/* Macro to set the leaf_info to something 'benign', that is, it would */ +/* run with the same time as it does during the real signing process */ +/* Used only by the benchmark code */ +#define INITIALIZE_LEAF_INFO_X1(info, addr, step_buffer) { \ + info.wots_sig = 0; \ + info.wots_sign_leaf = ~0u; \ + info.wots_steps = step_buffer; \ + memcpy( &info.leaf_addr[0], addr, 32 ); \ + memcpy( &info.pk_addr[0], addr, 32 ); \ +} + +#define wots_gen_leafx1 SPX_NAMESPACE(wots_gen_leafx1) +void wots_gen_leafx1(unsigned char *dest, + const spx_ctx *ctx, + uint32_t leaf_idx, void *v_info); + +#endif /* WOTSX1_H_ */ diff --git a/Blastproof/keygen/address.c b/Blastproof/keygen/address.c new file mode 100644 index 0000000..b136af8 --- /dev/null +++ b/Blastproof/keygen/address.c @@ -0,0 +1,104 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +/* + * Specify which level of Merkle tree (the "layer") we're working on + */ +void set_layer_addr(uint32_t addr[8], uint32_t layer) +{ + ((unsigned char *)addr)[SPX_OFFSET_LAYER] = (unsigned char)layer; +} + +/* + * Specify which Merkle tree within the level (the "tree address") we're working on + */ +void set_tree_addr(uint32_t addr[8], uint64_t tree) +{ +#if (SPX_TREE_HEIGHT * (SPX_D - 1)) > 64 + #error Subtree addressing is currently limited to at most 2^64 trees +#endif + ull_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_TREE], 8, tree ); +} + +/* + * Specify the reason we'll use this address structure for, that is, what + * hash will we compute with it. This is used so that unrelated types of + * hashes don't accidentally get the same address structure. The type will be + * one of the SPX_ADDR_TYPE constants + */ +void set_type(uint32_t addr[8], uint32_t type) +{ + ((unsigned char *)addr)[SPX_OFFSET_TYPE] = (unsigned char)type; +} + +/* + * Copy the layer and tree fields of the address structure. This is used + * when we're doing multiple types of hashes within the same Merkle tree + */ +void copy_subtree_addr(uint32_t out[8], const uint32_t in[8]) +{ + memcpy( out, in, SPX_OFFSET_TREE+8 ); +} + +/* These functions are used for OTS addresses. */ + +/* + * Specify which Merkle leaf we're working on; that is, which OTS keypair + * we're talking about. + */ +void set_keypair_addr(uint32_t addr[8], uint32_t keypair) +{ + u32_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_KP_ADDR], keypair); +} + +/* + * Copy the layer, tree and keypair fields of the address structure. This is + * used when we're doing multiple things within the same OTS keypair + */ +void copy_keypair_addr(uint32_t out[8], const uint32_t in[8]) +{ + memcpy( out, in, SPX_OFFSET_TREE+8 ); + memcpy( (unsigned char *)out + SPX_OFFSET_KP_ADDR, (unsigned char *)in + SPX_OFFSET_KP_ADDR, 4); +} + +/* + * Specify which Merkle chain within the OTS we're working with + * (the chain address) + */ +void set_chain_addr(uint32_t addr[8], uint32_t chain) +{ + ((unsigned char *)addr)[SPX_OFFSET_CHAIN_ADDR] = (unsigned char)chain; +} + +/* + * Specify where in the Merkle chain we are +* (the hash address) + */ +void set_hash_addr(uint32_t addr[8], uint32_t hash) +{ + ((unsigned char *)addr)[SPX_OFFSET_HASH_ADDR] = (unsigned char)hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +/* + * Specify the height of the node in the Merkle/FORS tree we are in + * (the tree height) + */ +void set_tree_height(uint32_t addr[8], uint32_t tree_height) +{ + ((unsigned char *)addr)[SPX_OFFSET_TREE_HGT] = (unsigned char)tree_height; +} + +/* + * Specify the distance from the left edge of the node in the Merkle/FORS tree + * (the tree index) + */ +void set_tree_index(uint32_t addr[8], uint32_t tree_index) +{ + u32_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_TREE_INDEX], tree_index ); +} diff --git a/Blastproof/keygen/address.h b/Blastproof/keygen/address.h new file mode 100644 index 0000000..49f8d66 --- /dev/null +++ b/Blastproof/keygen/address.h @@ -0,0 +1,51 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include +#include "params.h" + +/* The hash types that are passed to set_type */ +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 +#define SPX_ADDR_TYPE_WOTSPRF 5 +#define SPX_ADDR_TYPE_FORSPRF 6 + +#define set_layer_addr SPX_NAMESPACE(set_layer_addr) +void set_layer_addr(uint32_t addr[8], uint32_t layer); + +#define set_tree_addr SPX_NAMESPACE(set_tree_addr) +void set_tree_addr(uint32_t addr[8], uint64_t tree); + +#define set_type SPX_NAMESPACE(set_type) +void set_type(uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +#define copy_subtree_addr SPX_NAMESPACE(copy_subtree_addr) +void copy_subtree_addr(uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +#define set_keypair_addr SPX_NAMESPACE(set_keypair_addr) +void set_keypair_addr(uint32_t addr[8], uint32_t keypair); + +#define set_chain_addr SPX_NAMESPACE(set_chain_addr) +void set_chain_addr(uint32_t addr[8], uint32_t chain); + +#define set_hash_addr SPX_NAMESPACE(set_hash_addr) +void set_hash_addr(uint32_t addr[8], uint32_t hash); + +#define copy_keypair_addr SPX_NAMESPACE(copy_keypair_addr) +void copy_keypair_addr(uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +#define set_tree_height SPX_NAMESPACE(set_tree_height) +void set_tree_height(uint32_t addr[8], uint32_t tree_height); + +#define set_tree_index SPX_NAMESPACE(set_tree_index) +void set_tree_index(uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/Blastproof/keygen/address.o b/Blastproof/keygen/address.o new file mode 100644 index 0000000..5f348fe Binary files /dev/null and b/Blastproof/keygen/address.o differ diff --git a/Blastproof/keygen/api.h b/Blastproof/keygen/api.h new file mode 100644 index 0000000..d57a148 --- /dev/null +++ b/Blastproof/keygen/api.h @@ -0,0 +1,77 @@ +#ifndef SPX_API_H +#define SPX_API_H + +#include +#include + +#include "params.h" + +#define CRYPTO_ALGNAME "SPHINCS+" + +#define CRYPTO_SECRETKEYBYTES SPX_SK_BYTES +#define CRYPTO_PUBLICKEYBYTES SPX_PK_BYTES +#define CRYPTO_BYTES SPX_BYTES +#define CRYPTO_SEEDBYTES 3*SPX_N + +/* + * Returns the length of a secret key, in bytes + */ +unsigned long long crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +unsigned long long crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +unsigned long long crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +unsigned long long crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk, + const unsigned char *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int crypto_sign_keypair(unsigned char *pk, unsigned char *sk); + +/** + * Returns an array containing a detached signature. + */ +int crypto_sign_signature(uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int crypto_sign_verify(const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int crypto_sign(unsigned char *sm, unsigned long long *smlen, + const unsigned char *m, unsigned long long mlen, + const unsigned char *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int crypto_sign_open(unsigned char *m, unsigned long long *mlen, + const unsigned char *sm, unsigned long long smlen, + const unsigned char *pk); + +#endif diff --git a/Blastproof/keygen/argon2.h b/Blastproof/keygen/argon2.h new file mode 100644 index 0000000..3980bb3 --- /dev/null +++ b/Blastproof/keygen/argon2.h @@ -0,0 +1,437 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_H +#define ARGON2_H + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Symbols visibility control */ +#ifdef A2_VISCTL +#define ARGON2_PUBLIC __attribute__((visibility("default"))) +#define ARGON2_LOCAL __attribute__ ((visibility ("hidden"))) +#elif defined(_MSC_VER) +#define ARGON2_PUBLIC __declspec(dllexport) +#define ARGON2_LOCAL +#else +#define ARGON2_PUBLIC +#define ARGON2_LOCAL +#endif + +/* + * Argon2 input parameter restrictions + */ + +/* Minimum and maximum number of lanes (degree of parallelism) */ +#define ARGON2_MIN_LANES UINT32_C(1) +#define ARGON2_MAX_LANES UINT32_C(0xFFFFFF) + +/* Minimum and maximum number of threads */ +#define ARGON2_MIN_THREADS UINT32_C(1) +#define ARGON2_MAX_THREADS UINT32_C(0xFFFFFF) + +/* Number of synchronization points between lanes per pass */ +#define ARGON2_SYNC_POINTS UINT32_C(4) + +/* Minimum and maximum digest size in bytes */ +#define ARGON2_MIN_OUTLEN UINT32_C(4) +#define ARGON2_MAX_OUTLEN UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */ +#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */ + +#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b)) +/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */ +#define ARGON2_MAX_MEMORY_BITS \ + ARGON2_MIN(UINT32_C(32), (sizeof(void *) * CHAR_BIT - 10 - 1)) +#define ARGON2_MAX_MEMORY \ + ARGON2_MIN(UINT32_C(0xFFFFFFFF), UINT64_C(1) << ARGON2_MAX_MEMORY_BITS) + +/* Minimum and maximum number of passes */ +#define ARGON2_MIN_TIME UINT32_C(1) +#define ARGON2_MAX_TIME UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum password length in bytes */ +#define ARGON2_MIN_PWD_LENGTH UINT32_C(0) +#define ARGON2_MAX_PWD_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum associated data length in bytes */ +#define ARGON2_MIN_AD_LENGTH UINT32_C(0) +#define ARGON2_MAX_AD_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum salt length in bytes */ +#define ARGON2_MIN_SALT_LENGTH UINT32_C(8) +#define ARGON2_MAX_SALT_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum key length in bytes */ +#define ARGON2_MIN_SECRET UINT32_C(0) +#define ARGON2_MAX_SECRET UINT32_C(0xFFFFFFFF) + +/* Flags to determine which fields are securely wiped (default = no wipe). */ +#define ARGON2_DEFAULT_FLAGS UINT32_C(0) +#define ARGON2_FLAG_CLEAR_PASSWORD (UINT32_C(1) << 0) +#define ARGON2_FLAG_CLEAR_SECRET (UINT32_C(1) << 1) + +/* Global flag to determine if we are wiping internal memory buffers. This flag + * is defined in core.c and defaults to 1 (wipe internal memory). */ +extern int FLAG_clear_internal_memory; + +/* Error codes */ +typedef enum Argon2_ErrorCodes { + ARGON2_OK = 0, + + ARGON2_OUTPUT_PTR_NULL = -1, + + ARGON2_OUTPUT_TOO_SHORT = -2, + ARGON2_OUTPUT_TOO_LONG = -3, + + ARGON2_PWD_TOO_SHORT = -4, + ARGON2_PWD_TOO_LONG = -5, + + ARGON2_SALT_TOO_SHORT = -6, + ARGON2_SALT_TOO_LONG = -7, + + ARGON2_AD_TOO_SHORT = -8, + ARGON2_AD_TOO_LONG = -9, + + ARGON2_SECRET_TOO_SHORT = -10, + ARGON2_SECRET_TOO_LONG = -11, + + ARGON2_TIME_TOO_SMALL = -12, + ARGON2_TIME_TOO_LARGE = -13, + + ARGON2_MEMORY_TOO_LITTLE = -14, + ARGON2_MEMORY_TOO_MUCH = -15, + + ARGON2_LANES_TOO_FEW = -16, + ARGON2_LANES_TOO_MANY = -17, + + ARGON2_PWD_PTR_MISMATCH = -18, /* NULL ptr with non-zero length */ + ARGON2_SALT_PTR_MISMATCH = -19, /* NULL ptr with non-zero length */ + ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */ + ARGON2_AD_PTR_MISMATCH = -21, /* NULL ptr with non-zero length */ + + ARGON2_MEMORY_ALLOCATION_ERROR = -22, + + ARGON2_FREE_MEMORY_CBK_NULL = -23, + ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24, + + ARGON2_INCORRECT_PARAMETER = -25, + ARGON2_INCORRECT_TYPE = -26, + + ARGON2_OUT_PTR_MISMATCH = -27, + + ARGON2_THREADS_TOO_FEW = -28, + ARGON2_THREADS_TOO_MANY = -29, + + ARGON2_MISSING_ARGS = -30, + + ARGON2_ENCODING_FAIL = -31, + + ARGON2_DECODING_FAIL = -32, + + ARGON2_THREAD_FAIL = -33, + + ARGON2_DECODING_LENGTH_FAIL = -34, + + ARGON2_VERIFY_MISMATCH = -35 +} argon2_error_codes; + +/* Memory allocator types --- for external allocation */ +typedef int (*allocate_fptr)(uint8_t **memory, size_t bytes_to_allocate); +typedef void (*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate); + +/* Argon2 external data structures */ + +/* + ***** + * Context: structure to hold Argon2 inputs: + * output array and its length, + * password and its length, + * salt and its length, + * secret and its length, + * associated data and its length, + * number of passes, amount of used memory (in KBytes, can be rounded up a bit) + * number of parallel threads that will be run. + * All the parameters above affect the output hash value. + * Additionally, two function pointers can be provided to allocate and + * deallocate the memory (if NULL, memory will be allocated internally). + * Also, three flags indicate whether to erase password, secret as soon as they + * are pre-hashed (and thus not needed anymore), and the entire memory + ***** + * Simplest situation: you have output array out[8], password is stored in + * pwd[32], salt is stored in salt[16], you do not have keys nor associated + * data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with + * 4 parallel lanes. + * You want to erase the password, but you're OK with last pass not being + * erased. You want to use the default memory allocator. + * Then you initialize: + Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false) + */ +typedef struct Argon2_Context { + uint8_t *out; /* output array */ + uint32_t outlen; /* digest length */ + + uint8_t *pwd; /* password array */ + uint32_t pwdlen; /* password length */ + + uint8_t *salt; /* salt array */ + uint32_t saltlen; /* salt length */ + + uint8_t *secret; /* key array */ + uint32_t secretlen; /* key length */ + + uint8_t *ad; /* associated data array */ + uint32_t adlen; /* associated data length */ + + uint32_t t_cost; /* number of passes */ + uint32_t m_cost; /* amount of memory requested (KB) */ + uint32_t lanes; /* number of lanes */ + uint32_t threads; /* maximum number of threads */ + + uint32_t version; /* version number */ + + allocate_fptr allocate_cbk; /* pointer to memory allocator */ + deallocate_fptr free_cbk; /* pointer to memory deallocator */ + + uint32_t flags; /* array of bool options */ +} argon2_context; + +/* Argon2 primitive type */ +typedef enum Argon2_type { + Argon2_d = 0, + Argon2_i = 1, + Argon2_id = 2 +} argon2_type; + +/* Version of the algorithm */ +typedef enum Argon2_version { + ARGON2_VERSION_10 = 0x10, + ARGON2_VERSION_13 = 0x13, + ARGON2_VERSION_NUMBER = ARGON2_VERSION_13 +} argon2_version; + +/* + * Function that gives the string representation of an argon2_type. + * @param type The argon2_type that we want the string for + * @param uppercase Whether the string should have the first letter uppercase + * @return NULL if invalid type, otherwise the string representation. + */ +ARGON2_PUBLIC const char *argon2_type2string(argon2_type type, int uppercase); + +/* + * Function that performs memory-hard hashing with certain degree of parallelism + * @param context Pointer to the Argon2 internal structure + * @return Error code if smth is wrong, ARGON2_OK otherwise + */ +ARGON2_PUBLIC int argon2_ctx(argon2_context *context, argon2_type type); + +/** + * Hashes a password with Argon2i, producing an encoded hash + * @param t_cost Number of iterations + * @param m_cost Sets memory usage to m_cost kibibytes + * @param parallelism Number of threads and compute lanes + * @param pwd Pointer to password + * @param pwdlen Password size in bytes + * @param salt Pointer to salt + * @param saltlen Salt size in bytes + * @param hashlen Desired length of the hash in bytes + * @param encoded Buffer where to write the encoded hash + * @param encodedlen Size of the buffer (thus max size of the encoded hash) + * @pre Different parallelism levels will give different results + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC int argon2i_hash_encoded(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, + const void *pwd, const size_t pwdlen, + const void *salt, const size_t saltlen, + const size_t hashlen, char *encoded, + const size_t encodedlen); + +/** + * Hashes a password with Argon2i, producing a raw hash at @hash + * @param t_cost Number of iterations + * @param m_cost Sets memory usage to m_cost kibibytes + * @param parallelism Number of threads and compute lanes + * @param pwd Pointer to password + * @param pwdlen Password size in bytes + * @param salt Pointer to salt + * @param saltlen Salt size in bytes + * @param hash Buffer where to write the raw hash - updated by the function + * @param hashlen Desired length of the hash in bytes + * @pre Different parallelism levels will give different results + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen); + +ARGON2_PUBLIC int argon2d_hash_encoded(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, + const void *pwd, const size_t pwdlen, + const void *salt, const size_t saltlen, + const size_t hashlen, char *encoded, + const size_t encodedlen); + +ARGON2_PUBLIC int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen); + +ARGON2_PUBLIC int argon2id_hash_encoded(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, + const void *pwd, const size_t pwdlen, + const void *salt, const size_t saltlen, + const size_t hashlen, char *encoded, + const size_t encodedlen); + +ARGON2_PUBLIC int argon2id_hash_raw(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen, char *encoded, + const size_t encodedlen, argon2_type type, + const uint32_t version); + +/** + * Verifies a password against an encoded string + * Encoded string is restricted as in validate_inputs() + * @param encoded String encoding parameters, salt, hash + * @param pwd Pointer to password + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC int argon2i_verify(const char *encoded, const void *pwd, + const size_t pwdlen); + +ARGON2_PUBLIC int argon2d_verify(const char *encoded, const void *pwd, + const size_t pwdlen); + +ARGON2_PUBLIC int argon2id_verify(const char *encoded, const void *pwd, + const size_t pwdlen); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC int argon2_verify(const char *encoded, const void *pwd, + const size_t pwdlen, argon2_type type); + +/** + * Argon2d: Version of Argon2 that picks memory blocks depending + * on the password and salt. Only for side-channel-free + * environment!! + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2d_ctx(argon2_context *context); + +/** + * Argon2i: Version of Argon2 that picks memory blocks + * independent on the password and salt. Good for side-channels, + * but worse w.r.t. tradeoff attacks if only one pass is used. + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2i_ctx(argon2_context *context); + +/** + * Argon2id: Version of Argon2 where the first half-pass over memory is + * password-independent, the rest are password-dependent (on the password and + * salt). OK against side channels (they reduce to 1/2-pass Argon2i), and + * better with w.r.t. tradeoff attacks (similar to Argon2d). + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2id_ctx(argon2_context *context); + +/** + * Verify if a given password is correct for Argon2d hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2d_verify_ctx(argon2_context *context, const char *hash); + +/** + * Verify if a given password is correct for Argon2i hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2i_verify_ctx(argon2_context *context, const char *hash); + +/** + * Verify if a given password is correct for Argon2id hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC int argon2id_verify_ctx(argon2_context *context, + const char *hash); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC int argon2_verify_ctx(argon2_context *context, const char *hash, + argon2_type type); + +/** + * Get the associated error message for given error code + * @return The error message associated with the given error code + */ +ARGON2_PUBLIC const char *argon2_error_message(int error_code); + +/** + * Returns the encoded hash length for the given input parameters + * @param t_cost Number of iterations + * @param m_cost Memory usage in kibibytes + * @param parallelism Number of threads; used to compute lanes + * @param saltlen Salt size in bytes + * @param hashlen Hash size in bytes + * @param type The argon2_type that we want the encoded length for + * @return The encoded hash length in bytes + */ +ARGON2_PUBLIC size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, + uint32_t parallelism, uint32_t saltlen, + uint32_t hashlen, argon2_type type); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/Blastproof/keygen/argon2/Argon2.sln b/Blastproof/keygen/argon2/Argon2.sln new file mode 100644 index 0000000..b16cda0 --- /dev/null +++ b/Blastproof/keygen/argon2/Argon2.sln @@ -0,0 +1,158 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2OptTestCI", "vs2015\Argon2OptTestCI\Argon2OptTestCI.vcxproj", "{12956597-5E42-433A-93F3-D4EFF50AA207}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2RefTestCI", "vs2015\Argon2RefTestCI\Argon2RefTestCI.vcxproj", "{8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2OptGenKAT", "vs2015\Argon2OptGenKAT\Argon2OptGenKAT.vcxproj", "{DBBAAAE6-4560-4D11-8280-30A6650A82EF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2RefGenKAT", "vs2015\Argon2RefGenKAT\Argon2RefGenKAT.vcxproj", "{71921B4C-A795-4A37-95A3-99D600E01211}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2Opt", "vs2015\Argon2Opt\Argon2Opt.vcxproj", "{CAA75C57-998C-494E-B8A5-5894EF0FC528}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2Ref", "vs2015\Argon2Ref\Argon2Ref.vcxproj", "{B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2OptBench", "vs2015\Argon2OptBench\Argon2OptBench.vcxproj", "{B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2RefBench", "vs2015\Argon2RefBench\Argon2RefBench.vcxproj", "{99203F6A-6E8C-42FC-8C7C-C07E8913D539}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2OptDll", "vs2015\Argon2OptDll\Argon2OptDll.vcxproj", "{3A898DD8-ACAE-4269-ADFE-EB7260D71583}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2RefDll", "vs2015\Argon2RefDll\Argon2RefDll.vcxproj", "{19D911A1-533C-4475-B313-F372481A35D4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + ReleaseStatic|x64 = ReleaseStatic|x64 + ReleaseStatic|x86 = ReleaseStatic|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {12956597-5E42-433A-93F3-D4EFF50AA207}.Debug|x64.ActiveCfg = Debug|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Debug|x64.Build.0 = Debug|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Debug|x86.ActiveCfg = Debug|Win32 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Debug|x86.Build.0 = Debug|Win32 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Release|x64.ActiveCfg = Release|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Release|x64.Build.0 = Release|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Release|x86.ActiveCfg = Release|Win32 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Release|x86.Build.0 = Release|Win32 + {12956597-5E42-433A-93F3-D4EFF50AA207}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {12956597-5E42-433A-93F3-D4EFF50AA207}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Debug|x64.ActiveCfg = Debug|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Debug|x64.Build.0 = Debug|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Debug|x86.ActiveCfg = Debug|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Debug|x86.Build.0 = Debug|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Release|x64.ActiveCfg = Release|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Release|x64.Build.0 = Release|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Release|x86.ActiveCfg = Release|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Release|x86.Build.0 = Release|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Debug|x64.ActiveCfg = Debug|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Debug|x64.Build.0 = Debug|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Debug|x86.ActiveCfg = Debug|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Debug|x86.Build.0 = Debug|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Release|x64.ActiveCfg = Release|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Release|x64.Build.0 = Release|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Release|x86.ActiveCfg = Release|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Release|x86.Build.0 = Release|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.Debug|x64.ActiveCfg = Debug|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.Debug|x64.Build.0 = Debug|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.Debug|x86.ActiveCfg = Debug|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.Debug|x86.Build.0 = Debug|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.Release|x64.ActiveCfg = Release|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.Release|x64.Build.0 = Release|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.Release|x86.ActiveCfg = Release|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.Release|x86.Build.0 = Release|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Debug|x64.ActiveCfg = Debug|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Debug|x64.Build.0 = Debug|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Debug|x86.ActiveCfg = Debug|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Debug|x86.Build.0 = Debug|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Release|x64.ActiveCfg = Release|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Release|x64.Build.0 = Release|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Release|x86.ActiveCfg = Release|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Release|x86.Build.0 = Release|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Debug|x64.ActiveCfg = Debug|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Debug|x64.Build.0 = Debug|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Debug|x86.ActiveCfg = Debug|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Debug|x86.Build.0 = Debug|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Release|x64.ActiveCfg = Release|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Release|x64.Build.0 = Release|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Release|x86.ActiveCfg = Release|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Release|x86.Build.0 = Release|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Debug|x64.ActiveCfg = Debug|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Debug|x64.Build.0 = Debug|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Debug|x86.ActiveCfg = Debug|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Debug|x86.Build.0 = Debug|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Release|x64.ActiveCfg = Release|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Release|x64.Build.0 = Release|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Release|x86.ActiveCfg = Release|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Release|x86.Build.0 = Release|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Debug|x64.ActiveCfg = Debug|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Debug|x64.Build.0 = Debug|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Debug|x86.ActiveCfg = Debug|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Debug|x86.Build.0 = Debug|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Release|x64.ActiveCfg = Release|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Release|x64.Build.0 = Release|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Release|x86.ActiveCfg = Release|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Release|x86.Build.0 = Release|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Debug|x64.ActiveCfg = Debug|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Debug|x64.Build.0 = Debug|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Debug|x86.ActiveCfg = Debug|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Debug|x86.Build.0 = Debug|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Release|x64.ActiveCfg = Release|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Release|x64.Build.0 = Release|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Release|x86.ActiveCfg = Release|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Release|x86.Build.0 = Release|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.Debug|x64.ActiveCfg = Debug|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.Debug|x64.Build.0 = Debug|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.Debug|x86.ActiveCfg = Debug|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.Debug|x86.Build.0 = Debug|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.Release|x64.ActiveCfg = Release|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.Release|x64.Build.0 = Release|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.Release|x86.ActiveCfg = Release|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.Release|x86.Build.0 = Release|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Blastproof/keygen/argon2/CHANGELOG.md b/Blastproof/keygen/argon2/CHANGELOG.md new file mode 100644 index 0000000..0578fde --- /dev/null +++ b/Blastproof/keygen/argon2/CHANGELOG.md @@ -0,0 +1,32 @@ +# 20171227 +* Added ABI version number +* AVX2/AVX-512F optimizations of BLAMKA +* Set Argon2 version number from the command line +* New bindings +* Minor bug and warning fixes (no security issue) + +# 20161029 + +* Argon2id added +* Better documentation +* Dual licensing CC0 / Apache 2.0 +* Minor bug fixes (no security issue) + +# 20160406 + +* Version 1.3 of Argon2 +* Version number in encoded hash +* Refactored low-level API +* Visibility control for library symbols +* Microsoft Visual Studio solution +* New bindings +* Minor bug and warning fixes (no security issue) + + +# 20151206 + +* Python bindings +* Password read from stdin, instead of being an argument +* Compatibility FreeBSD, NetBSD, OpenBSD +* Constant-time verification +* Minor bug and warning fixes (no security issue) diff --git a/Blastproof/keygen/argon2/LICENSE b/Blastproof/keygen/argon2/LICENSE new file mode 100644 index 0000000..a16d6d2 --- /dev/null +++ b/Blastproof/keygen/argon2/LICENSE @@ -0,0 +1,314 @@ +Argon2 reference source code package - reference C implementations + +Copyright 2015 +Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + +You may use this work under the terms of a Creative Commons CC0 1.0 +License/Waiver or the Apache Public License 2.0, at your option. The terms of +these licenses can be found at: + +- CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 +- Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + +The terms of the licenses are reproduced below. + +-------------------------------------------------------------------------------- + +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + +-------------------------------------------------------------------------------- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/Blastproof/keygen/argon2/Makefile b/Blastproof/keygen/argon2/Makefile new file mode 100644 index 0000000..44c076a --- /dev/null +++ b/Blastproof/keygen/argon2/Makefile @@ -0,0 +1,255 @@ +# +# Argon2 reference source code package - reference C implementations +# +# Copyright 2015 +# Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +# +# You may use this work under the terms of a Creative Commons CC0 1.0 +# License/Waiver or the Apache Public License 2.0, at your option. The terms of +# these licenses can be found at: +# +# - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 +# - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 +# +# You should have received a copy of both of these licenses along with this +# software. If not, they may be obtained at the above URLs. +# + +RUN = argon2 +BENCH = bench +GENKAT = genkat +ARGON2_VERSION ?= ZERO + +# installation parameters for staging area and final installation path +# Note; if Linux and not Debian/Ubuntu version also add lib override to make command-line +# for RedHat/Fedora, add: LIBRARY_REL=lib64 +DESTDIR ?= +PREFIX ?= /usr + +# Increment on an ABI breaking change +ABI_VERSION = 1 + +DIST = phc-winner-argon2 + +SRC = src/argon2.c src/core.c src/blake2/blake2b.c src/thread.c src/encoding.c +SRC_RUN = src/run.c +SRC_BENCH = src/bench.c +SRC_GENKAT = src/genkat.c +OBJ = $(SRC:.c=.o) + +CFLAGS += -std=c89 -O3 -Wall -g -Iinclude -Isrc + +ifeq ($(NO_THREADS), 1) +CFLAGS += -DARGON2_NO_THREADS +else +CFLAGS += -pthread +endif + +CI_CFLAGS := $(CFLAGS) -Werror=declaration-after-statement -D_FORTIFY_SOURCE=2 \ + -Wextra -Wno-type-limits -Werror -coverage -DTEST_LARGE_RAM + +OPTTARGET ?= native +OPTTEST := $(shell $(CC) -Iinclude -Isrc -march=$(OPTTARGET) src/opt.c -c \ + -o /dev/null 2>/dev/null; echo $$?) +# Detect compatible platform +ifneq ($(OPTTEST), 0) +$(info Building without optimizations) + SRC += src/ref.c +else +$(info Building with optimizations for $(OPTTARGET)) + CFLAGS += -march=$(OPTTARGET) + SRC += src/opt.c +endif + +BUILD_PATH := $(shell pwd) +KERNEL_NAME := $(shell uname -s) +MACHINE_NAME := $(shell uname -m) + +LIB_NAME = argon2 +PC_NAME = lib$(LIB_NAME).pc +PC_SRC = $(PC_NAME).in + +ifeq ($(KERNEL_NAME), Linux) + LIB_EXT := so.$(ABI_VERSION) + LIB_CFLAGS := -shared -fPIC -fvisibility=hidden -DA2_VISCTL=1 + SO_LDFLAGS := -Wl,-soname,lib$(LIB_NAME).$(LIB_EXT) + LINKED_LIB_EXT := so + PC_EXTRA_LIBS ?= -lrt -ldl +endif +ifeq ($(KERNEL_NAME), $(filter $(KERNEL_NAME),DragonFly FreeBSD NetBSD OpenBSD)) + LIB_EXT := so + LIB_CFLAGS := -shared -fPIC + PC_EXTRA_LIBS ?= +endif +ifeq ($(KERNEL_NAME), Darwin) + LIB_EXT := $(ABI_VERSION).dylib + LIB_CFLAGS = -dynamiclib -install_name $(PREFIX)/$(LIBRARY_REL)/lib$(LIB_NAME).$(LIB_EXT) + LINKED_LIB_EXT := dylib + PC_EXTRA_LIBS ?= +endif +ifeq ($(findstring CYGWIN, $(KERNEL_NAME)), CYGWIN) + LIB_EXT := dll + LIB_CFLAGS := -shared -Wl,--out-implib,lib$(LIB_NAME).$(LIB_EXT).a + PC_EXTRA_LIBS ?= +endif +ifeq ($(findstring MINGW, $(KERNEL_NAME)), MINGW) + LIB_EXT := dll + LIB_CFLAGS := -shared -Wl,--out-implib,lib$(LIB_NAME).$(LIB_EXT).a + PC_EXTRA_LIBS ?= +endif +ifeq ($(findstring MSYS, $(KERNEL_NAME)), MSYS) + LIB_EXT := dll + LIB_CFLAGS := -shared -Wl,--out-implib,lib$(LIB_NAME).$(LIB_EXT).a + PC_EXTRA_LIBS ?= +endif +ifeq ($(KERNEL_NAME), SunOS) + CC := gcc + CFLAGS += -D_REENTRANT + LIB_EXT := so + LIB_CFLAGS := -shared -fPIC + PC_EXTRA_LIBS ?= +endif + +ifeq ($(KERNEL_NAME), Linux) +ifeq ($(CC), clang) + CI_CFLAGS += -fsanitize=address -fsanitize=undefined +endif +endif + +LIB_SH := lib$(LIB_NAME).$(LIB_EXT) +LIB_ST := lib$(LIB_NAME).a + +ifdef LINKED_LIB_EXT +LINKED_LIB_SH := lib$(LIB_NAME).$(LINKED_LIB_EXT) +endif + +# Some systems don't provide an unprefixed ar when cross-compiling. +AR=ar + +LIBRARIES = $(LIB_SH) $(LIB_ST) +HEADERS = include/argon2.h + +INSTALL = install + +# relative paths for different OS +ifeq ($(KERNEL_NAME), $(filter $(KERNEL_NAME),DragonFly FreeBSD)) + +# default for FreeBSD +BINARY_REL ?= bin +INCLUDE_REL ?= include +LIBRARY_REL ?= lib +PKGCONFIG_REL ?= libdata + +else ifeq ($(KERNEL_NAME)-$(MACHINE_NAME), Linux-x86_64) + +# default for Debian/Ubuntu x86_64 +BINARY_REL ?= bin +INCLUDE_REL ?= include +LIBRARY_REL ?= lib/x86_64-linux-gnu +PKGCONFIG_REL ?= $(LIBRARY_REL) + +else + +# NetBSD, ... and Linux64/Linux32 variants that use plain lib directory +BINARY_REL ?= bin +INCLUDE_REL ?= include +LIBRARY_REL ?= lib +PKGCONFIG_REL ?= $(LIBRARY_REL) + +endif + +# absolute paths to staging area +INST_INCLUDE = $(DESTDIR)$(PREFIX)/$(INCLUDE_REL) +INST_LIBRARY = $(DESTDIR)$(PREFIX)/$(LIBRARY_REL) +INST_BINARY = $(DESTDIR)$(PREFIX)/$(BINARY_REL) +INST_PKGCONFIG = $(DESTDIR)$(PREFIX)/$(PKGCONFIG_REL)/pkgconfig + +# main target +.PHONY: all +all: $(RUN) libs + +.PHONY: libs +libs: $(LIBRARIES) $(PC_NAME) + +$(RUN): $(SRC) $(SRC_RUN) + $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ + +$(BENCH): $(SRC) $(SRC_BENCH) + $(CC) $(CFLAGS) $^ -o $@ + +$(GENKAT): $(SRC) $(SRC_GENKAT) + $(CC) $(CFLAGS) $^ -o $@ -DGENKAT + +$(LIB_SH): $(SRC) + $(CC) $(CFLAGS) $(LIB_CFLAGS) $(LDFLAGS) $(SO_LDFLAGS) $^ -o $@ + +$(LIB_ST): $(OBJ) + $(AR) rcs $@ $^ + +.PHONY: clean +clean: + rm -f '$(RUN)' '$(BENCH)' '$(GENKAT)' + rm -f '$(LIB_SH)' '$(LIB_ST)' kat-argon2* '$(PC_NAME)' + rm -f testcase + rm -rf *.dSYM + cd src/ && rm -f *.o + cd src/blake2/ && rm -f *.o + cd kats/ && rm -f kat-* diff* run_* make_* + + +# all substitutions to pc template +SED_COMMANDS = /^\#\#.*$$/d; +SED_COMMANDS += s\#@PREFIX@\#$(PREFIX)\#g; +SED_COMMANDS += s\#@EXTRA_LIBS@\#$(PC_EXTRA_LIBS)\#g; +SED_COMMANDS += s\#@UPSTREAM_VER@\#$(ARGON2_VERSION)\#g; +SED_COMMANDS += s\#@HOST_MULTIARCH@\#$(LIBRARY_REL)\#g; +SED_COMMANDS += s\#@INCLUDE@\#$(INCLUDE_REL)\#g; + +# substitute PREFIX and PC_EXTRA_LIBS into pkgconfig pc file +$(PC_NAME): $(PC_SRC) + sed '$(SED_COMMANDS)' < '$(PC_SRC)' > '$@' + + +.PHONY: dist +dist: + cd ..; \ + tar -c --exclude='.??*' -z -f $(DIST)-`date "+%Y%m%d"`.tgz $(DIST)/* + +.PHONY: test +test: $(SRC) src/test.c + $(CC) $(CFLAGS) -Wextra -Wno-type-limits $^ -o testcase + @sh kats/test.sh + ./testcase + +.PHONY: testci +testci: $(SRC) src/test.c + $(CC) $(CI_CFLAGS) $^ -o testcase + @sh kats/test.sh + ./testcase + + +.PHONY: format +format: + clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4}" \ + -i include/*.h src/*.c src/*.h src/blake2/*.c src/blake2/*.h + +.PHONY: install +install: $(RUN) libs + $(INSTALL) -d $(INST_INCLUDE) + $(INSTALL) -m 0644 $(HEADERS) $(INST_INCLUDE) + $(INSTALL) -d $(INST_LIBRARY) + $(INSTALL) -m 0644 $(LIBRARIES) $(INST_LIBRARY) +ifdef LINKED_LIB_SH + cd $(INST_LIBRARY) && ln -sf $(notdir $(LIB_SH) $(LINKED_LIB_SH)) +endif + $(INSTALL) -d $(INST_BINARY) + $(INSTALL) $(RUN) $(INST_BINARY) + $(INSTALL) -d $(INST_PKGCONFIG) + $(INSTALL) -m 0644 $(PC_NAME) $(INST_PKGCONFIG) + +.PHONY: uninstall +uninstall: + cd $(INST_INCLUDE) && rm -f $(notdir $(HEADERS)) + cd $(INST_LIBRARY) && rm -f $(notdir $(LIBRARIES) $(LINKED_LIB_SH)) + cd $(INST_BINARY) && rm -f $(notdir $(RUN)) + cd $(INST_PKG_CONFIG) && rm -f $(notdir $(PC_NAME)) diff --git a/Blastproof/keygen/argon2/Package.swift b/Blastproof/keygen/argon2/Package.swift new file mode 100644 index 0000000..d3d9c83 --- /dev/null +++ b/Blastproof/keygen/argon2/Package.swift @@ -0,0 +1,46 @@ +// swift-tools-version:5.3 + +import PackageDescription + +let package = Package( + name: "argon2", + products: [ + .library( + name: "argon2", + targets: ["argon2"]), + ], + targets: [ + .target( + name: "argon2", + path: ".", + exclude: [ + "kats", + "vs2015", + "latex", + "libargon2.pc.in", + "export.sh", + "appveyor.yml", + "Argon2.sln", + "argon2-specs.pdf", + "CHANGELOG.md", + "LICENSE", + "Makefile", + "man", + "README.md", + "src/bench.c", + "src/genkat.c", + "src/opt.c", + "src/run.c", + "src/test.c", + ], + sources: [ + "src/blake2/blake2b.c", + "src/argon2.c", + "src/core.c", + "src/encoding.c", + "src/ref.c", + "src/thread.c" + ] + ) + ] +) \ No newline at end of file diff --git a/Blastproof/keygen/argon2/README.md b/Blastproof/keygen/argon2/README.md new file mode 100644 index 0000000..91fc3fd --- /dev/null +++ b/Blastproof/keygen/argon2/README.md @@ -0,0 +1,303 @@ +# Argon2 + +[![Build Status](https://travis-ci.org/P-H-C/phc-winner-argon2.svg?branch=master)](https://travis-ci.org/P-H-C/phc-winner-argon2) +[![Build status](https://ci.appveyor.com/api/projects/status/8nfwuwq55sgfkele?svg=true)](https://ci.appveyor.com/project/P-H-C/phc-winner-argon2) +[![codecov.io](https://codecov.io/github/P-H-C/phc-winner-argon2/coverage.svg?branch=master)](https://codecov.io/github/P-H-C/phc-winner-argon2?branch=master) + +This is the reference C implementation of Argon2, the password-hashing +function that won the [Password Hashing Competition +(PHC)](https://password-hashing.net). + +Argon2 is a password-hashing function that summarizes the state of the +art in the design of memory-hard functions and can be used to hash +passwords for credential storage, key derivation, or other applications. + +It has a simple design aimed at the highest memory filling rate and +effective use of multiple computing units, while still providing defense +against tradeoff attacks (by exploiting the cache and memory organization +of the recent processors). + +Argon2 has three variants: Argon2i, Argon2d, and Argon2id. Argon2d is faster +and uses data-depending memory access, which makes it highly resistant +against GPU cracking attacks and suitable for applications with no threats +from side-channel timing attacks (eg. cryptocurrencies). Argon2i instead +uses data-independent memory access, which is preferred for password +hashing and password-based key derivation, but it is slower as it makes +more passes over the memory to protect from tradeoff attacks. Argon2id is a +hybrid of Argon2i and Argon2d, using a combination of data-depending and +data-independent memory accesses, which gives some of Argon2i's resistance to +side-channel cache timing attacks and much of Argon2d's resistance to GPU +cracking attacks. + +Argon2i, Argon2d, and Argon2id are parametrized by: + +* A **time** cost, which defines the amount of computation realized and + therefore the execution time, given in number of iterations +* A **memory** cost, which defines the memory usage, given in kibibytes +* A **parallelism** degree, which defines the number of parallel threads + +The [Argon2 document](argon2-specs.pdf) gives detailed specs and design +rationale. + +Please report bugs as issues on this repository. + +## Usage + +`make` builds the executable `argon2`, the static library `libargon2.a`, +and the shared library `libargon2.so` (or on macOS, the dynamic library +`libargon2.dylib` -- make sure to specify the installation prefix when +you compile: `make PREFIX=/usr`). Make sure to run `make test` to verify +that your build produces valid results. `sudo make install PREFIX=/usr` +installs it to your system. + +### Command-line utility + +`argon2` is a command-line utility to test specific Argon2 instances +on your system. To show usage instructions, run +`./argon2 -h` as +``` +Usage: ./argon2 [-h] salt [-i|-d|-id] [-t iterations] [-m memory] [-p parallelism] [-l hash length] [-e|-r] [-v (10|13)] + Password is read from stdin +Parameters: + salt The salt to use, at least 8 characters + -i Use Argon2i (this is the default) + -d Use Argon2d instead of Argon2i + -id Use Argon2id instead of Argon2i + -t N Sets the number of iterations to N (default = 3) + -m N Sets the memory usage of 2^N KiB (default 12) + -p N Sets parallelism to N threads (default 1) + -l N Sets hash output length to N bytes (default 32) + -e Output only encoded hash + -r Output only the raw bytes of the hash + -v (10|13) Argon2 version (defaults to the most recent version, currently 13) + -h Print argon2 usage +``` +For example, to hash "password" using "somesalt" as a salt and doing 2 +iterations, consuming 64 MiB, using four parallel threads and an output hash +of 24 bytes +``` +$ echo -n "password" | ./argon2 somesalt -t 2 -m 16 -p 4 -l 24 +Type: Argon2i +Iterations: 2 +Memory: 65536 KiB +Parallelism: 4 +Hash: 45d7ac72e76f242b20b77b9bf9bf9d5915894e669a24e6c6 +Encoded: $argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG +0.188 seconds +Verification ok +``` + +### Library + +`libargon2` provides an API to both low-level and high-level functions +for using Argon2. + +The example program below hashes the string "password" with Argon2i +using the high-level API and then using the low-level API. While the +high-level API takes the three cost parameters (time, memory, and +parallelism), the password input buffer, the salt input buffer, and the +output buffers, the low-level API takes in these and additional parameters +, as defined in [`include/argon2.h`](include/argon2.h). + +There are many additional parameters, but we will highlight three of them here. + +1. The `secret` parameter, which is used for [keyed hashing]( + https://en.wikipedia.org/wiki/Hash-based_message_authentication_code). + This allows a secret key to be input at hashing time (from some external + location) and be folded into the value of the hash. This means that even if + your salts and hashes are compromised, an attacker cannot brute-force to find + the password without the key. + +2. The `ad` parameter, which is used to fold any additional data into the hash + value. Functionally, this behaves almost exactly like the `secret` or `salt` + parameters; the `ad` parameter is folding into the value of the hash. + However, this parameter is used for different data. The `salt` should be a + random string stored alongside your password. The `secret` should be a random + key only usable at hashing time. The `ad` is for any other data. + +3. The `flags` parameter, which determines which memory should be securely + erased. This is useful if you want to securely delete the `pwd` or `secret` + fields right after they are used. To do this set `flags` to either + `ARGON2_FLAG_CLEAR_PASSWORD` or `ARGON2_FLAG_CLEAR_SECRET`. To change how + internal memory is cleared, change the global flag + `FLAG_clear_internal_memory` (defaults to clearing internal memory). + +Here the time cost `t_cost` is set to 2 iterations, the +memory cost `m_cost` is set to 216 kibibytes (64 mebibytes), +and parallelism is set to 1 (single-thread). + +Compile for example as `gcc test.c libargon2.a -Isrc -o test`, if the program +below is named `test.c` and placed in the project's root directory. + +```c +#include "argon2.h" +#include +#include +#include + +#define HASHLEN 32 +#define SALTLEN 16 +#define PWD "password" + +int main(void) +{ + uint8_t hash1[HASHLEN]; + uint8_t hash2[HASHLEN]; + + uint8_t salt[SALTLEN]; + memset( salt, 0x00, SALTLEN ); + + uint8_t *pwd = (uint8_t *)strdup(PWD); + uint32_t pwdlen = strlen((char *)pwd); + + uint32_t t_cost = 2; // 2-pass computation + uint32_t m_cost = (1<<16); // 64 mebibytes memory usage + uint32_t parallelism = 1; // number of threads and lanes + + // high-level API + argon2i_hash_raw(t_cost, m_cost, parallelism, pwd, pwdlen, salt, SALTLEN, hash1, HASHLEN); + + // low-level API + argon2_context context = { + hash2, /* output array, at least HASHLEN in size */ + HASHLEN, /* digest length */ + pwd, /* password array */ + pwdlen, /* password length */ + salt, /* salt array */ + SALTLEN, /* salt length */ + NULL, 0, /* optional secret data */ + NULL, 0, /* optional associated data */ + t_cost, m_cost, parallelism, parallelism, + ARGON2_VERSION_13, /* algorithm version */ + NULL, NULL, /* custom memory allocation / deallocation functions */ + /* by default only internal memory is cleared (pwd is not wiped) */ + ARGON2_DEFAULT_FLAGS + }; + + int rc = argon2i_ctx( &context ); + if(ARGON2_OK != rc) { + printf("Error: %s\n", argon2_error_message(rc)); + exit(1); + } + free(pwd); + + for( int i=0; i $tempfile + } else { + vs2015\build\Argon2OptGenKAT.exe $type $version > $tempfile + } + + if (19 -eq $version) { + $kats = "kats\argon2" + $type + } else { + $kats = "kats\argon2" + $type + "_v" + $version + } + + Write-Host -NoNewline "Argon2$type v=$version : " + CompareFiles $tempfile $kats $i + Write-Output "" + } + } + } + + if (Test-Path $tempfile) { + Remove-Item $tempfile + } +} + +main diff --git a/Blastproof/keygen/argon2/kats/test.sh b/Blastproof/keygen/argon2/kats/test.sh new file mode 100755 index 0000000..b320975 --- /dev/null +++ b/Blastproof/keygen/argon2/kats/test.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +for opttest in "" "OPTTEST=1" +do + if [ "" = "$opttest" ] + then + printf "Default build\n" + else + printf "Force OPTTEST=1\n" + fi + + make genkat $opttest > /dev/null + if [ $? -ne 0 ] + then + exit $? + fi + + i=0 + for version in 16 19 + do + for type in i d id + do + i=$(($i+1)) + + printf "argon2$type v=$version: " + + if [ 19 -eq $version ] + then + kats="kats/argon2"$type + else + kats="kats/argon2"$type"_v"$version + fi + + ./genkat $type $version > tmp + if diff tmp $kats + then + printf "OK" + else + printf "ERROR" + exit $i + fi + printf "\n" + done + done +done + +rm -f tmp + +exit 0 diff --git a/Blastproof/keygen/argon2/libargon2.pc b/Blastproof/keygen/argon2/libargon2.pc new file mode 100644 index 0000000..db37095 --- /dev/null +++ b/Blastproof/keygen/argon2/libargon2.pc @@ -0,0 +1,13 @@ +# libargon2 info for pkg-config + +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib/x86_64-linux-gnu +includedir=${prefix}/include + +Name: libargon2 +Description: Development libraries for libargon2 +Version: ZERO +Libs: -L${libdir} -largon2 -lrt -ldl +Cflags: -I${includedir} +URL: https://github.com/P-H-C/phc-winner-argon2 diff --git a/Blastproof/keygen/argon2/libargon2.pc.in b/Blastproof/keygen/argon2/libargon2.pc.in new file mode 100644 index 0000000..a0d2929 --- /dev/null +++ b/Blastproof/keygen/argon2/libargon2.pc.in @@ -0,0 +1,18 @@ +# libargon2 info for pkg-config +## Template for downstream installers: +## - replace @UPSTREAM_VER@ with current version, e.g. '20160406' +## - replace @HOST_MULTIARCH@ with target arch lib, e.g. 'lib', 'lib/x86_64-linux-gnu' or 'lib64' +## - replace @PREFIX@ with install path, e.g. '/usr', '/usr/local', '/usr/pkg' +## - replace @INCLUDE@ with include path, e.g. 'include' or 'include/argon2' + +prefix=@PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@HOST_MULTIARCH@ +includedir=${prefix}/@INCLUDE@ + +Name: libargon2 +Description: Development libraries for libargon2 +Version: @UPSTREAM_VER@ +Libs: -L${libdir} -largon2 @EXTRA_LIBS@ +Cflags: -I${includedir} +URL: https://github.com/P-H-C/phc-winner-argon2 diff --git a/Blastproof/keygen/argon2/libargon2.so.1 b/Blastproof/keygen/argon2/libargon2.so.1 new file mode 100755 index 0000000..61eef91 Binary files /dev/null and b/Blastproof/keygen/argon2/libargon2.so.1 differ diff --git a/Blastproof/keygen/argon2/man/argon2.1 b/Blastproof/keygen/argon2/man/argon2.1 new file mode 100644 index 0000000..77c0f07 --- /dev/null +++ b/Blastproof/keygen/argon2/man/argon2.1 @@ -0,0 +1,57 @@ +.TH ARGON2 "1" "April 2016" "argon2 " "User Commands" + +.SH NAME +argon2 \- generate argon2 hashes + +.SH SYNOPSIS +.B argon2 salt +.RB [ OPTIONS ] + +.SH DESCRIPTION +Generate Argon2 hashes from the command line. + +The supplied salt (the first argument to the command) must be at least +8 octets in length, and the password is supplied on standard input. + +By default, this uses Argon2i variant (where memory access is +independent of secret data) which is the preferred one for password +hashing and password-based key derivation. + +.SH OPTIONS +.TP +.B \-h +Display tool usage +.TP +.B \-d +Use Argon2d instead of Argon2i (Argon2i is the default) +.TP +.B \-id +Use Argon2id instead of Argon2i (Argon2i is the default) +.TP +.BI \-t " N" +Sets the number of iterations to N (default = 3) +.TP +.BI \-m " N" +Sets the memory usage of 2^N KiB (default = 12) +.TP +.BI \-p " N" +Sets parallelism to N threads (default = 1) +.TP +.BI \-l " N" +Sets hash output length to N bytes (default = 32) +.TP +.B \-e +Output only encoded hash +.TP +.B \-r +Output only the raw bytes of the hash +.TP +.B \-v (10|13) +Argon2 version (defaults to the most recent version, currently 13) + +.SH COPYRIGHT +This manpage was written by \fBDaniel Kahn Gillmor\fR for the Debian +distribution (but may be used by others). It is released, like the +rest of this Argon2 implementation, under a dual license. You may use this work +under the terms of a Creative Commons CC0 1.0 License/Waiver or the Apache +Public License 2.0, at your option. diff --git a/Blastproof/keygen/argon2/src/argon2.c b/Blastproof/keygen/argon2/src/argon2.c new file mode 100644 index 0000000..34da3d6 --- /dev/null +++ b/Blastproof/keygen/argon2/src/argon2.c @@ -0,0 +1,452 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include + +#include "argon2.h" +#include "encoding.h" +#include "core.h" + +const char *argon2_type2string(argon2_type type, int uppercase) { + switch (type) { + case Argon2_d: + return uppercase ? "Argon2d" : "argon2d"; + case Argon2_i: + return uppercase ? "Argon2i" : "argon2i"; + case Argon2_id: + return uppercase ? "Argon2id" : "argon2id"; + } + + return NULL; +} + +int argon2_ctx(argon2_context *context, argon2_type type) { + /* 1. Validate all inputs */ + int result = validate_inputs(context); + uint32_t memory_blocks, segment_length; + argon2_instance_t instance; + + if (ARGON2_OK != result) { + return result; + } + + if (Argon2_d != type && Argon2_i != type && Argon2_id != type) { + return ARGON2_INCORRECT_TYPE; + } + + /* 2. Align memory size */ + /* Minimum memory_blocks = 8L blocks, where L is the number of lanes */ + memory_blocks = context->m_cost; + + if (memory_blocks < 2 * ARGON2_SYNC_POINTS * context->lanes) { + memory_blocks = 2 * ARGON2_SYNC_POINTS * context->lanes; + } + + segment_length = memory_blocks / (context->lanes * ARGON2_SYNC_POINTS); + /* Ensure that all segments have equal length */ + memory_blocks = segment_length * (context->lanes * ARGON2_SYNC_POINTS); + + instance.version = context->version; + instance.memory = NULL; + instance.passes = context->t_cost; + instance.memory_blocks = memory_blocks; + instance.segment_length = segment_length; + instance.lane_length = segment_length * ARGON2_SYNC_POINTS; + instance.lanes = context->lanes; + instance.threads = context->threads; + instance.type = type; + + if (instance.threads > instance.lanes) { + instance.threads = instance.lanes; + } + + /* 3. Initialization: Hashing inputs, allocating memory, filling first + * blocks + */ + result = initialize(&instance, context); + + if (ARGON2_OK != result) { + return result; + } + + /* 4. Filling memory */ + result = fill_memory_blocks(&instance); + + if (ARGON2_OK != result) { + return result; + } + /* 5. Finalization */ + finalize(context, &instance); + + return ARGON2_OK; +} + +int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, const size_t saltlen, + void *hash, const size_t hashlen, char *encoded, + const size_t encodedlen, argon2_type type, + const uint32_t version){ + + argon2_context context; + int result; + uint8_t *out; + + if (pwdlen > ARGON2_MAX_PWD_LENGTH) { + return ARGON2_PWD_TOO_LONG; + } + + if (saltlen > ARGON2_MAX_SALT_LENGTH) { + return ARGON2_SALT_TOO_LONG; + } + + if (hashlen > ARGON2_MAX_OUTLEN) { + return ARGON2_OUTPUT_TOO_LONG; + } + + if (hashlen < ARGON2_MIN_OUTLEN) { + return ARGON2_OUTPUT_TOO_SHORT; + } + + out = malloc(hashlen); + if (!out) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + context.out = (uint8_t *)out; + context.outlen = (uint32_t)hashlen; + context.pwd = CONST_CAST(uint8_t *)pwd; + context.pwdlen = (uint32_t)pwdlen; + context.salt = CONST_CAST(uint8_t *)salt; + context.saltlen = (uint32_t)saltlen; + context.secret = NULL; + context.secretlen = 0; + context.ad = NULL; + context.adlen = 0; + context.t_cost = t_cost; + context.m_cost = m_cost; + context.lanes = parallelism; + context.threads = parallelism; + context.allocate_cbk = NULL; + context.free_cbk = NULL; + context.flags = ARGON2_DEFAULT_FLAGS; + context.version = version; + + result = argon2_ctx(&context, type); + + if (result != ARGON2_OK) { + clear_internal_memory(out, hashlen); + free(out); + return result; + } + + /* if raw hash requested, write it */ + if (hash) { + memcpy(hash, out, hashlen); + } + + /* if encoding requested, write it */ + if (encoded && encodedlen) { + if (encode_string(encoded, encodedlen, &context, type) != ARGON2_OK) { + clear_internal_memory(out, hashlen); /* wipe buffers if error */ + clear_internal_memory(encoded, encodedlen); + free(out); + return ARGON2_ENCODING_FAIL; + } + } + clear_internal_memory(out, hashlen); + free(out); + + return ARGON2_OK; +} + +int argon2i_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, const size_t hashlen, + char *encoded, const size_t encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_i, + ARGON2_VERSION_NUMBER); +} + +int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, const size_t hashlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_i, ARGON2_VERSION_NUMBER); +} + +int argon2d_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, const size_t hashlen, + char *encoded, const size_t encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_d, + ARGON2_VERSION_NUMBER); +} + +int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, const size_t hashlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER); +} + +int argon2id_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, const size_t hashlen, + char *encoded, const size_t encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_id, + ARGON2_VERSION_NUMBER); +} + +int argon2id_hash_raw(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, const size_t hashlen) { + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_id, + ARGON2_VERSION_NUMBER); +} + +static int argon2_compare(const uint8_t *b1, const uint8_t *b2, size_t len) { + size_t i; + uint8_t d = 0U; + + for (i = 0U; i < len; i++) { + d |= b1[i] ^ b2[i]; + } + return (int)((1 & ((d - 1) >> 8)) - 1); +} + +int argon2_verify(const char *encoded, const void *pwd, const size_t pwdlen, + argon2_type type) { + + argon2_context ctx; + uint8_t *desired_result = NULL; + + int ret = ARGON2_OK; + + size_t encoded_len; + uint32_t max_field_len; + + if (pwdlen > ARGON2_MAX_PWD_LENGTH) { + return ARGON2_PWD_TOO_LONG; + } + + if (encoded == NULL) { + return ARGON2_DECODING_FAIL; + } + + encoded_len = strlen(encoded); + if (encoded_len > UINT32_MAX) { + return ARGON2_DECODING_FAIL; + } + + /* No field can be longer than the encoded length */ + max_field_len = (uint32_t)encoded_len; + + ctx.saltlen = max_field_len; + ctx.outlen = max_field_len; + + ctx.salt = malloc(ctx.saltlen); + ctx.out = malloc(ctx.outlen); + if (!ctx.salt || !ctx.out) { + ret = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + ctx.pwd = (uint8_t *)pwd; + ctx.pwdlen = (uint32_t)pwdlen; + + ret = decode_string(&ctx, encoded, type); + if (ret != ARGON2_OK) { + goto fail; + } + + /* Set aside the desired result, and get a new buffer. */ + desired_result = ctx.out; + ctx.out = malloc(ctx.outlen); + if (!ctx.out) { + ret = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + ret = argon2_verify_ctx(&ctx, (char *)desired_result, type); + if (ret != ARGON2_OK) { + goto fail; + } + +fail: + free(ctx.salt); + free(ctx.out); + free(desired_result); + + return ret; +} + +int argon2i_verify(const char *encoded, const void *pwd, const size_t pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_i); +} + +int argon2d_verify(const char *encoded, const void *pwd, const size_t pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_d); +} + +int argon2id_verify(const char *encoded, const void *pwd, const size_t pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_id); +} + +int argon2d_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_d); +} + +int argon2i_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_i); +} + +int argon2id_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_id); +} + +int argon2_verify_ctx(argon2_context *context, const char *hash, + argon2_type type) { + int ret = argon2_ctx(context, type); + if (ret != ARGON2_OK) { + return ret; + } + + if (argon2_compare((uint8_t *)hash, context->out, context->outlen)) { + return ARGON2_VERIFY_MISMATCH; + } + + return ARGON2_OK; +} + +int argon2d_verify_ctx(argon2_context *context, const char *hash) { + return argon2_verify_ctx(context, hash, Argon2_d); +} + +int argon2i_verify_ctx(argon2_context *context, const char *hash) { + return argon2_verify_ctx(context, hash, Argon2_i); +} + +int argon2id_verify_ctx(argon2_context *context, const char *hash) { + return argon2_verify_ctx(context, hash, Argon2_id); +} + +const char *argon2_error_message(int error_code) { + switch (error_code) { + case ARGON2_OK: + return "OK"; + case ARGON2_OUTPUT_PTR_NULL: + return "Output pointer is NULL"; + case ARGON2_OUTPUT_TOO_SHORT: + return "Output is too short"; + case ARGON2_OUTPUT_TOO_LONG: + return "Output is too long"; + case ARGON2_PWD_TOO_SHORT: + return "Password is too short"; + case ARGON2_PWD_TOO_LONG: + return "Password is too long"; + case ARGON2_SALT_TOO_SHORT: + return "Salt is too short"; + case ARGON2_SALT_TOO_LONG: + return "Salt is too long"; + case ARGON2_AD_TOO_SHORT: + return "Associated data is too short"; + case ARGON2_AD_TOO_LONG: + return "Associated data is too long"; + case ARGON2_SECRET_TOO_SHORT: + return "Secret is too short"; + case ARGON2_SECRET_TOO_LONG: + return "Secret is too long"; + case ARGON2_TIME_TOO_SMALL: + return "Time cost is too small"; + case ARGON2_TIME_TOO_LARGE: + return "Time cost is too large"; + case ARGON2_MEMORY_TOO_LITTLE: + return "Memory cost is too small"; + case ARGON2_MEMORY_TOO_MUCH: + return "Memory cost is too large"; + case ARGON2_LANES_TOO_FEW: + return "Too few lanes"; + case ARGON2_LANES_TOO_MANY: + return "Too many lanes"; + case ARGON2_PWD_PTR_MISMATCH: + return "Password pointer is NULL, but password length is not 0"; + case ARGON2_SALT_PTR_MISMATCH: + return "Salt pointer is NULL, but salt length is not 0"; + case ARGON2_SECRET_PTR_MISMATCH: + return "Secret pointer is NULL, but secret length is not 0"; + case ARGON2_AD_PTR_MISMATCH: + return "Associated data pointer is NULL, but ad length is not 0"; + case ARGON2_MEMORY_ALLOCATION_ERROR: + return "Memory allocation error"; + case ARGON2_FREE_MEMORY_CBK_NULL: + return "The free memory callback is NULL"; + case ARGON2_ALLOCATE_MEMORY_CBK_NULL: + return "The allocate memory callback is NULL"; + case ARGON2_INCORRECT_PARAMETER: + return "Argon2_Context context is NULL"; + case ARGON2_INCORRECT_TYPE: + return "There is no such version of Argon2"; + case ARGON2_OUT_PTR_MISMATCH: + return "Output pointer mismatch"; + case ARGON2_THREADS_TOO_FEW: + return "Not enough threads"; + case ARGON2_THREADS_TOO_MANY: + return "Too many threads"; + case ARGON2_MISSING_ARGS: + return "Missing arguments"; + case ARGON2_ENCODING_FAIL: + return "Encoding failed"; + case ARGON2_DECODING_FAIL: + return "Decoding failed"; + case ARGON2_THREAD_FAIL: + return "Threading failure"; + case ARGON2_DECODING_LENGTH_FAIL: + return "Some of encoded parameters are too long or too short"; + case ARGON2_VERIFY_MISMATCH: + return "The password does not match the supplied hash"; + default: + return "Unknown error code"; + } +} + +size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, uint32_t parallelism, + uint32_t saltlen, uint32_t hashlen, argon2_type type) { + return strlen("$$v=$m=,t=,p=$$") + strlen(argon2_type2string(type, 0)) + + numlen(t_cost) + numlen(m_cost) + numlen(parallelism) + + b64len(saltlen) + b64len(hashlen) + numlen(ARGON2_VERSION_NUMBER) + 1; +} diff --git a/Blastproof/keygen/argon2/src/argon2.o b/Blastproof/keygen/argon2/src/argon2.o new file mode 100644 index 0000000..ea9d0ad Binary files /dev/null and b/Blastproof/keygen/argon2/src/argon2.o differ diff --git a/Blastproof/keygen/argon2/src/bench.c b/Blastproof/keygen/argon2/src/bench.c new file mode 100644 index 0000000..6335519 --- /dev/null +++ b/Blastproof/keygen/argon2/src/bench.c @@ -0,0 +1,111 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif + +#include "argon2.h" + +static uint64_t rdtsc(void) { +#ifdef _WIN32 + return __rdtsc(); +#else +#if defined(__amd64__) || defined(__x86_64__) + uint64_t rax, rdx; + __asm__ __volatile__("rdtsc" : "=a"(rax), "=d"(rdx) : :); + return (rdx << 32) | rax; +#elif defined(__i386__) || defined(__i386) || defined(__X86__) + uint64_t rax; + __asm__ __volatile__("rdtsc" : "=A"(rax) : :); + return rax; +#else +#error "Not implemented!" +#endif +#endif +} + +/* + * Benchmarks Argon2 with salt length 16, password length 16, t_cost 3, + and different m_cost and threads + */ +static void benchmark() { +#define BENCH_OUTLEN 16 +#define BENCH_INLEN 16 + const uint32_t inlen = BENCH_INLEN; + const unsigned outlen = BENCH_OUTLEN; + unsigned char out[BENCH_OUTLEN]; + unsigned char pwd_array[BENCH_INLEN]; + unsigned char salt_array[BENCH_INLEN]; +#undef BENCH_INLEN +#undef BENCH_OUTLEN + + uint32_t t_cost = 3; + uint32_t m_cost; + uint32_t thread_test[4] = {1, 2, 4, 8}; + argon2_type types[3] = {Argon2_i, Argon2_d, Argon2_id}; + + memset(pwd_array, 0, inlen); + memset(salt_array, 1, inlen); + + for (m_cost = (uint32_t)1 << 10; m_cost <= (uint32_t)1 << 22; m_cost *= 2) { + unsigned i; + for (i = 0; i < 4; ++i) { + double run_time = 0; + uint32_t thread_n = thread_test[i]; + + unsigned j; + for (j = 0; j < 3; ++j) { + clock_t start_time, stop_time; + uint64_t start_cycles, stop_cycles; + uint64_t delta; + double mcycles; + + argon2_type type = types[j]; + start_time = clock(); + start_cycles = rdtsc(); + + argon2_hash(t_cost, m_cost, thread_n, pwd_array, inlen, + salt_array, inlen, out, outlen, NULL, 0, type, + ARGON2_VERSION_NUMBER); + + stop_cycles = rdtsc(); + stop_time = clock(); + + delta = (stop_cycles - start_cycles) / (m_cost); + mcycles = (double)(stop_cycles - start_cycles) / (1UL << 20); + run_time += ((double)stop_time - start_time) / (CLOCKS_PER_SEC); + + printf("%s %d iterations %d MiB %d threads: %2.2f cpb %2.2f " + "Mcycles \n", argon2_type2string(type, 1), t_cost, + m_cost >> 10, thread_n, (float)delta / 1024, mcycles); + } + + printf("%2.4f seconds\n\n", run_time); + } + } +} + +int main() { + benchmark(); + return ARGON2_OK; +} diff --git a/Blastproof/keygen/argon2/src/blake2/blake2-impl.h b/Blastproof/keygen/argon2/src/blake2/blake2-impl.h new file mode 100644 index 0000000..86d0d5c --- /dev/null +++ b/Blastproof/keygen/argon2/src/blake2/blake2-impl.h @@ -0,0 +1,156 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef PORTABLE_BLAKE2_IMPL_H +#define PORTABLE_BLAKE2_IMPL_H + +#include +#include + +#ifdef _WIN32 +#define BLAKE2_INLINE __inline +#elif defined(__GNUC__) || defined(__clang__) +#define BLAKE2_INLINE __inline__ +#else +#define BLAKE2_INLINE +#endif + +/* Argon2 Team - Begin Code */ +/* + Not an exhaustive list, but should cover the majority of modern platforms + Additionally, the code will always be correct---this is only a performance + tweak. +*/ +#if (defined(__BYTE_ORDER__) && \ + (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \ + defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \ + defined(_M_ARM) +#define NATIVE_LITTLE_ENDIAN +#endif +/* Argon2 Team - End Code */ + +static BLAKE2_INLINE uint32_t load32(const void *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = (const uint8_t *)src; + uint32_t w = *p++; + w |= (uint32_t)(*p++) << 8; + w |= (uint32_t)(*p++) << 16; + w |= (uint32_t)(*p++) << 24; + return w; +#endif +} + +static BLAKE2_INLINE uint64_t load64(const void *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = (const uint8_t *)src; + uint64_t w = *p++; + w |= (uint64_t)(*p++) << 8; + w |= (uint64_t)(*p++) << 16; + w |= (uint64_t)(*p++) << 24; + w |= (uint64_t)(*p++) << 32; + w |= (uint64_t)(*p++) << 40; + w |= (uint64_t)(*p++) << 48; + w |= (uint64_t)(*p++) << 56; + return w; +#endif +} + +static BLAKE2_INLINE void store32(void *dst, uint32_t w) { +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +#endif +} + +static BLAKE2_INLINE void store64(void *dst, uint64_t w) { +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +#endif +} + +static BLAKE2_INLINE uint64_t load48(const void *src) { + const uint8_t *p = (const uint8_t *)src; + uint64_t w = *p++; + w |= (uint64_t)(*p++) << 8; + w |= (uint64_t)(*p++) << 16; + w |= (uint64_t)(*p++) << 24; + w |= (uint64_t)(*p++) << 32; + w |= (uint64_t)(*p++) << 40; + return w; +} + +static BLAKE2_INLINE void store48(void *dst, uint64_t w) { + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +} + +static BLAKE2_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) { + return (w >> c) | (w << (32 - c)); +} + +static BLAKE2_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) { + return (w >> c) | (w << (64 - c)); +} + +void clear_internal_memory(void *v, size_t n); + +#endif diff --git a/Blastproof/keygen/argon2/src/blake2/blake2.h b/Blastproof/keygen/argon2/src/blake2/blake2.h new file mode 100644 index 0000000..501c6a3 --- /dev/null +++ b/Blastproof/keygen/argon2/src/blake2/blake2.h @@ -0,0 +1,89 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef PORTABLE_BLAKE2_H +#define PORTABLE_BLAKE2_H + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +enum blake2b_constant { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 +}; + +#pragma pack(push, 1) +typedef struct __blake2b_param { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint64_t node_offset; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ +} blake2b_param; +#pragma pack(pop) + +typedef struct __blake2b_state { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + unsigned buflen; + unsigned outlen; + uint8_t last_node; +} blake2b_state; + +/* Ensure param structs have not been wrongly padded */ +/* Poor man's static_assert */ +enum { + blake2_size_check_0 = 1 / !!(CHAR_BIT == 8), + blake2_size_check_2 = + 1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT) +}; + +/* Streaming API */ +ARGON2_LOCAL int blake2b_init(blake2b_state *S, size_t outlen); +ARGON2_LOCAL int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, + size_t keylen); +ARGON2_LOCAL int blake2b_init_param(blake2b_state *S, const blake2b_param *P); +ARGON2_LOCAL int blake2b_update(blake2b_state *S, const void *in, size_t inlen); +ARGON2_LOCAL int blake2b_final(blake2b_state *S, void *out, size_t outlen); + +/* Simple API */ +ARGON2_LOCAL int blake2b(void *out, size_t outlen, const void *in, size_t inlen, + const void *key, size_t keylen); + +/* Argon2 Team - Begin Code */ +ARGON2_LOCAL int blake2b_long(void *out, size_t outlen, const void *in, size_t inlen); +/* Argon2 Team - End Code */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/Blastproof/keygen/argon2/src/blake2/blake2b.c b/Blastproof/keygen/argon2/src/blake2/blake2b.c new file mode 100644 index 0000000..3b519dd --- /dev/null +++ b/Blastproof/keygen/argon2/src/blake2/blake2b.c @@ -0,0 +1,390 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +static const uint64_t blake2b_IV[8] = { + UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b), + UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1), + UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f), + UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179)}; + +static const unsigned int blake2b_sigma[12][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, +}; + +static BLAKE2_INLINE void blake2b_set_lastnode(blake2b_state *S) { + S->f[1] = (uint64_t)-1; +} + +static BLAKE2_INLINE void blake2b_set_lastblock(blake2b_state *S) { + if (S->last_node) { + blake2b_set_lastnode(S); + } + S->f[0] = (uint64_t)-1; +} + +static BLAKE2_INLINE void blake2b_increment_counter(blake2b_state *S, + uint64_t inc) { + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); +} + +static BLAKE2_INLINE void blake2b_invalidate_state(blake2b_state *S) { + clear_internal_memory(S, sizeof(*S)); /* wipe */ + blake2b_set_lastblock(S); /* invalidate for further use */ +} + +static BLAKE2_INLINE void blake2b_init0(blake2b_state *S) { + memset(S, 0, sizeof(*S)); + memcpy(S->h, blake2b_IV, sizeof(S->h)); +} + +int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { + const unsigned char *p = (const unsigned char *)P; + unsigned int i; + + if (NULL == P || NULL == S) { + return -1; + } + + blake2b_init0(S); + /* IV XOR Parameter Block */ + for (i = 0; i < 8; ++i) { + S->h[i] ^= load64(&p[i * sizeof(S->h[i])]); + } + S->outlen = P->digest_length; + return 0; +} + +/* Sequential blake2b initialization */ +int blake2b_init(blake2b_state *S, size_t outlen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for unkeyed BLAKE2 */ + P.digest_length = (uint8_t)outlen; + P.key_length = 0; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + memset(P.reserved, 0, sizeof(P.reserved)); + memset(P.salt, 0, sizeof(P.salt)); + memset(P.personal, 0, sizeof(P.personal)); + + return blake2b_init_param(S, &P); +} + +int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, + size_t keylen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + if ((key == 0) || (keylen == 0) || (keylen > BLAKE2B_KEYBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for keyed BLAKE2 */ + P.digest_length = (uint8_t)outlen; + P.key_length = (uint8_t)keylen; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + memset(P.reserved, 0, sizeof(P.reserved)); + memset(P.salt, 0, sizeof(P.salt)); + memset(P.personal, 0, sizeof(P.personal)); + + if (blake2b_init_param(S, &P) < 0) { + blake2b_invalidate_state(S); + return -1; + } + + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset(block, 0, BLAKE2B_BLOCKBYTES); + memcpy(block, key, keylen); + blake2b_update(S, block, BLAKE2B_BLOCKBYTES); + /* Burn the key from stack */ + clear_internal_memory(block, BLAKE2B_BLOCKBYTES); + } + return 0; +} + +static void blake2b_compress(blake2b_state *S, const uint8_t *block) { + uint64_t m[16]; + uint64_t v[16]; + unsigned int i, r; + + for (i = 0; i < 16; ++i) { + m[i] = load64(block + i * sizeof(m[i])); + } + + for (i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2b_IV[0]; + v[9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while ((void)0, 0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while ((void)0, 0) + + for (r = 0; r < 12; ++r) { + ROUND(r); + } + + for (i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } + +#undef G +#undef ROUND +} + +int blake2b_update(blake2b_state *S, const void *in, size_t inlen) { + const uint8_t *pin = (const uint8_t *)in; + + if (inlen == 0) { + return 0; + } + + /* Sanity check */ + if (S == NULL || in == NULL) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) { + /* Complete current block */ + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + memcpy(&S->buf[left], pin, fill); + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, S->buf); + S->buflen = 0; + inlen -= fill; + pin += fill; + /* Avoid buffer copies when possible */ + while (inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, pin); + inlen -= BLAKE2B_BLOCKBYTES; + pin += BLAKE2B_BLOCKBYTES; + } + } + memcpy(&S->buf[S->buflen], pin, inlen); + S->buflen += (unsigned int)inlen; + return 0; +} + +int blake2b_final(blake2b_state *S, void *out, size_t outlen) { + uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; + unsigned int i; + + /* Sanity checks */ + if (S == NULL || out == NULL || outlen < S->outlen) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + blake2b_increment_counter(S, S->buflen); + blake2b_set_lastblock(S); + memset(&S->buf[S->buflen], 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */ + blake2b_compress(S, S->buf); + + for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */ + store64(buffer + sizeof(S->h[i]) * i, S->h[i]); + } + + memcpy(out, buffer, S->outlen); + clear_internal_memory(buffer, sizeof(buffer)); + clear_internal_memory(S->buf, sizeof(S->buf)); + clear_internal_memory(S->h, sizeof(S->h)); + return 0; +} + +int blake2b(void *out, size_t outlen, const void *in, size_t inlen, + const void *key, size_t keylen) { + blake2b_state S; + int ret = -1; + + /* Verify parameters */ + if (NULL == in && inlen > 0) { + goto fail; + } + + if (NULL == out || outlen == 0 || outlen > BLAKE2B_OUTBYTES) { + goto fail; + } + + if ((NULL == key && keylen > 0) || keylen > BLAKE2B_KEYBYTES) { + goto fail; + } + + if (keylen > 0) { + if (blake2b_init_key(&S, outlen, key, keylen) < 0) { + goto fail; + } + } else { + if (blake2b_init(&S, outlen) < 0) { + goto fail; + } + } + + if (blake2b_update(&S, in, inlen) < 0) { + goto fail; + } + ret = blake2b_final(&S, out, outlen); + +fail: + clear_internal_memory(&S, sizeof(S)); + return ret; +} + +/* Argon2 Team - Begin Code */ +int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { + uint8_t *out = (uint8_t *)pout; + blake2b_state blake_state; + uint8_t outlen_bytes[sizeof(uint32_t)] = {0}; + int ret = -1; + + if (outlen > UINT32_MAX) { + goto fail; + } + + /* Ensure little-endian byte order! */ + store32(outlen_bytes, (uint32_t)outlen); + +#define TRY(statement) \ + do { \ + ret = statement; \ + if (ret < 0) { \ + goto fail; \ + } \ + } while ((void)0, 0) + + if (outlen <= BLAKE2B_OUTBYTES) { + TRY(blake2b_init(&blake_state, outlen)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out, outlen)); + } else { + uint32_t toproduce; + uint8_t out_buffer[BLAKE2B_OUTBYTES]; + uint8_t in_buffer[BLAKE2B_OUTBYTES]; + TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2; + + while (toproduce > BLAKE2B_OUTBYTES) { + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer, + BLAKE2B_OUTBYTES, NULL, 0)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce -= BLAKE2B_OUTBYTES / 2; + } + + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, + 0)); + memcpy(out, out_buffer, toproduce); + } +fail: + clear_internal_memory(&blake_state, sizeof(blake_state)); + return ret; +#undef TRY +} +/* Argon2 Team - End Code */ diff --git a/Blastproof/keygen/argon2/src/blake2/blake2b.o b/Blastproof/keygen/argon2/src/blake2/blake2b.o new file mode 100644 index 0000000..6bbb15a Binary files /dev/null and b/Blastproof/keygen/argon2/src/blake2/blake2b.o differ diff --git a/Blastproof/keygen/argon2/src/blake2/blamka-round-opt.h b/Blastproof/keygen/argon2/src/blake2/blamka-round-opt.h new file mode 100644 index 0000000..3127f2a --- /dev/null +++ b/Blastproof/keygen/argon2/src/blake2/blamka-round-opt.h @@ -0,0 +1,471 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef BLAKE_ROUND_MKA_OPT_H +#define BLAKE_ROUND_MKA_OPT_H + +#include "blake2-impl.h" + +#include +#if defined(__SSSE3__) +#include /* for _mm_shuffle_epi8 and _mm_alignr_epi8 */ +#endif + +#if defined(__XOP__) && (defined(__GNUC__) || defined(__clang__)) +#include +#endif + +#if !defined(__AVX512F__) +#if !defined(__AVX2__) +#if !defined(__XOP__) +#if defined(__SSSE3__) +#define r16 \ + (_mm_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9)) +#define r24 \ + (_mm_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10)) +#define _mm_roti_epi64(x, c) \ + (-(c) == 32) \ + ? _mm_shuffle_epi32((x), _MM_SHUFFLE(2, 3, 0, 1)) \ + : (-(c) == 24) \ + ? _mm_shuffle_epi8((x), r24) \ + : (-(c) == 16) \ + ? _mm_shuffle_epi8((x), r16) \ + : (-(c) == 63) \ + ? _mm_xor_si128(_mm_srli_epi64((x), -(c)), \ + _mm_add_epi64((x), (x))) \ + : _mm_xor_si128(_mm_srli_epi64((x), -(c)), \ + _mm_slli_epi64((x), 64 - (-(c)))) +#else /* defined(__SSE2__) */ +#define _mm_roti_epi64(r, c) \ + _mm_xor_si128(_mm_srli_epi64((r), -(c)), _mm_slli_epi64((r), 64 - (-(c)))) +#endif +#else +#endif + +static BLAKE2_INLINE __m128i fBlaMka(__m128i x, __m128i y) { + const __m128i z = _mm_mul_epu32(x, y); + return _mm_add_epi64(_mm_add_epi64(x, y), _mm_add_epi64(z, z)); +} + +#define G1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = fBlaMka(A0, B0); \ + A1 = fBlaMka(A1, B1); \ + \ + D0 = _mm_xor_si128(D0, A0); \ + D1 = _mm_xor_si128(D1, A1); \ + \ + D0 = _mm_roti_epi64(D0, -32); \ + D1 = _mm_roti_epi64(D1, -32); \ + \ + C0 = fBlaMka(C0, D0); \ + C1 = fBlaMka(C1, D1); \ + \ + B0 = _mm_xor_si128(B0, C0); \ + B1 = _mm_xor_si128(B1, C1); \ + \ + B0 = _mm_roti_epi64(B0, -24); \ + B1 = _mm_roti_epi64(B1, -24); \ + } while ((void)0, 0) + +#define G2(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = fBlaMka(A0, B0); \ + A1 = fBlaMka(A1, B1); \ + \ + D0 = _mm_xor_si128(D0, A0); \ + D1 = _mm_xor_si128(D1, A1); \ + \ + D0 = _mm_roti_epi64(D0, -16); \ + D1 = _mm_roti_epi64(D1, -16); \ + \ + C0 = fBlaMka(C0, D0); \ + C1 = fBlaMka(C1, D1); \ + \ + B0 = _mm_xor_si128(B0, C0); \ + B1 = _mm_xor_si128(B1, C1); \ + \ + B0 = _mm_roti_epi64(B0, -63); \ + B1 = _mm_roti_epi64(B1, -63); \ + } while ((void)0, 0) + +#if defined(__SSSE3__) +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = _mm_alignr_epi8(B1, B0, 8); \ + __m128i t1 = _mm_alignr_epi8(B0, B1, 8); \ + B0 = t0; \ + B1 = t1; \ + \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + \ + t0 = _mm_alignr_epi8(D1, D0, 8); \ + t1 = _mm_alignr_epi8(D0, D1, 8); \ + D0 = t1; \ + D1 = t0; \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = _mm_alignr_epi8(B0, B1, 8); \ + __m128i t1 = _mm_alignr_epi8(B1, B0, 8); \ + B0 = t0; \ + B1 = t1; \ + \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + \ + t0 = _mm_alignr_epi8(D0, D1, 8); \ + t1 = _mm_alignr_epi8(D1, D0, 8); \ + D0 = t1; \ + D1 = t0; \ + } while ((void)0, 0) +#else /* SSE2 */ +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = D0; \ + __m128i t1 = B0; \ + D0 = C0; \ + C0 = C1; \ + C1 = D0; \ + D0 = _mm_unpackhi_epi64(D1, _mm_unpacklo_epi64(t0, t0)); \ + D1 = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(D1, D1)); \ + B0 = _mm_unpackhi_epi64(B0, _mm_unpacklo_epi64(B1, B1)); \ + B1 = _mm_unpackhi_epi64(B1, _mm_unpacklo_epi64(t1, t1)); \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0, t1; \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + t0 = B0; \ + t1 = D0; \ + B0 = _mm_unpackhi_epi64(B1, _mm_unpacklo_epi64(B0, B0)); \ + B1 = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(B1, B1)); \ + D0 = _mm_unpackhi_epi64(D0, _mm_unpacklo_epi64(D1, D1)); \ + D1 = _mm_unpackhi_epi64(D1, _mm_unpacklo_epi64(t1, t1)); \ + } while ((void)0, 0) +#endif + +#define BLAKE2_ROUND(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + } while ((void)0, 0) +#else /* __AVX2__ */ + +#include + +#define rotr32(x) _mm256_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1)) +#define rotr24(x) _mm256_shuffle_epi8(x, _mm256_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10, 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10)) +#define rotr16(x) _mm256_shuffle_epi8(x, _mm256_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9, 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9)) +#define rotr63(x) _mm256_xor_si256(_mm256_srli_epi64((x), 63), _mm256_add_epi64((x), (x))) + +#define G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i ml = _mm256_mul_epu32(A0, B0); \ + ml = _mm256_add_epi64(ml, ml); \ + A0 = _mm256_add_epi64(A0, _mm256_add_epi64(B0, ml)); \ + D0 = _mm256_xor_si256(D0, A0); \ + D0 = rotr32(D0); \ + \ + ml = _mm256_mul_epu32(C0, D0); \ + ml = _mm256_add_epi64(ml, ml); \ + C0 = _mm256_add_epi64(C0, _mm256_add_epi64(D0, ml)); \ + \ + B0 = _mm256_xor_si256(B0, C0); \ + B0 = rotr24(B0); \ + \ + ml = _mm256_mul_epu32(A1, B1); \ + ml = _mm256_add_epi64(ml, ml); \ + A1 = _mm256_add_epi64(A1, _mm256_add_epi64(B1, ml)); \ + D1 = _mm256_xor_si256(D1, A1); \ + D1 = rotr32(D1); \ + \ + ml = _mm256_mul_epu32(C1, D1); \ + ml = _mm256_add_epi64(ml, ml); \ + C1 = _mm256_add_epi64(C1, _mm256_add_epi64(D1, ml)); \ + \ + B1 = _mm256_xor_si256(B1, C1); \ + B1 = rotr24(B1); \ + } while((void)0, 0); + +#define G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i ml = _mm256_mul_epu32(A0, B0); \ + ml = _mm256_add_epi64(ml, ml); \ + A0 = _mm256_add_epi64(A0, _mm256_add_epi64(B0, ml)); \ + D0 = _mm256_xor_si256(D0, A0); \ + D0 = rotr16(D0); \ + \ + ml = _mm256_mul_epu32(C0, D0); \ + ml = _mm256_add_epi64(ml, ml); \ + C0 = _mm256_add_epi64(C0, _mm256_add_epi64(D0, ml)); \ + B0 = _mm256_xor_si256(B0, C0); \ + B0 = rotr63(B0); \ + \ + ml = _mm256_mul_epu32(A1, B1); \ + ml = _mm256_add_epi64(ml, ml); \ + A1 = _mm256_add_epi64(A1, _mm256_add_epi64(B1, ml)); \ + D1 = _mm256_xor_si256(D1, A1); \ + D1 = rotr16(D1); \ + \ + ml = _mm256_mul_epu32(C1, D1); \ + ml = _mm256_add_epi64(ml, ml); \ + C1 = _mm256_add_epi64(C1, _mm256_add_epi64(D1, ml)); \ + B1 = _mm256_xor_si256(B1, C1); \ + B1 = rotr63(B1); \ + } while((void)0, 0); + +#define DIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm256_permute4x64_epi64(B0, _MM_SHUFFLE(0, 3, 2, 1)); \ + C0 = _mm256_permute4x64_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + D0 = _mm256_permute4x64_epi64(D0, _MM_SHUFFLE(2, 1, 0, 3)); \ + \ + B1 = _mm256_permute4x64_epi64(B1, _MM_SHUFFLE(0, 3, 2, 1)); \ + C1 = _mm256_permute4x64_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ + D1 = _mm256_permute4x64_epi64(D1, _MM_SHUFFLE(2, 1, 0, 3)); \ + } while((void)0, 0); + +#define DIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i tmp1 = _mm256_blend_epi32(B0, B1, 0xCC); \ + __m256i tmp2 = _mm256_blend_epi32(B0, B1, 0x33); \ + B1 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + B0 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + \ + tmp1 = C0; \ + C0 = C1; \ + C1 = tmp1; \ + \ + tmp1 = _mm256_blend_epi32(D0, D1, 0xCC); \ + tmp2 = _mm256_blend_epi32(D0, D1, 0x33); \ + D0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + D1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + } while(0); + +#define UNDIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm256_permute4x64_epi64(B0, _MM_SHUFFLE(2, 1, 0, 3)); \ + C0 = _mm256_permute4x64_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + D0 = _mm256_permute4x64_epi64(D0, _MM_SHUFFLE(0, 3, 2, 1)); \ + \ + B1 = _mm256_permute4x64_epi64(B1, _MM_SHUFFLE(2, 1, 0, 3)); \ + C1 = _mm256_permute4x64_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ + D1 = _mm256_permute4x64_epi64(D1, _MM_SHUFFLE(0, 3, 2, 1)); \ + } while((void)0, 0); + +#define UNDIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i tmp1 = _mm256_blend_epi32(B0, B1, 0xCC); \ + __m256i tmp2 = _mm256_blend_epi32(B0, B1, 0x33); \ + B0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + B1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + \ + tmp1 = C0; \ + C0 = C1; \ + C1 = tmp1; \ + \ + tmp1 = _mm256_blend_epi32(D0, D1, 0x33); \ + tmp2 = _mm256_blend_epi32(D0, D1, 0xCC); \ + D0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + D1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + } while((void)0, 0); + +#define BLAKE2_ROUND_1(A0, A1, B0, B1, C0, C1, D0, D1) \ + do{ \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + DIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + UNDIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + } while((void)0, 0); + +#define BLAKE2_ROUND_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do{ \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + DIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + UNDIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + } while((void)0, 0); + +#endif /* __AVX2__ */ + +#else /* __AVX512F__ */ + +#include + +#define ror64(x, n) _mm512_ror_epi64((x), (n)) + +static __m512i muladd(__m512i x, __m512i y) +{ + __m512i z = _mm512_mul_epu32(x, y); + return _mm512_add_epi64(_mm512_add_epi64(x, y), _mm512_add_epi64(z, z)); +} + +#define G1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = muladd(A0, B0); \ + A1 = muladd(A1, B1); \ +\ + D0 = _mm512_xor_si512(D0, A0); \ + D1 = _mm512_xor_si512(D1, A1); \ +\ + D0 = ror64(D0, 32); \ + D1 = ror64(D1, 32); \ +\ + C0 = muladd(C0, D0); \ + C1 = muladd(C1, D1); \ +\ + B0 = _mm512_xor_si512(B0, C0); \ + B1 = _mm512_xor_si512(B1, C1); \ +\ + B0 = ror64(B0, 24); \ + B1 = ror64(B1, 24); \ + } while ((void)0, 0) + +#define G2(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = muladd(A0, B0); \ + A1 = muladd(A1, B1); \ +\ + D0 = _mm512_xor_si512(D0, A0); \ + D1 = _mm512_xor_si512(D1, A1); \ +\ + D0 = ror64(D0, 16); \ + D1 = ror64(D1, 16); \ +\ + C0 = muladd(C0, D0); \ + C1 = muladd(C1, D1); \ +\ + B0 = _mm512_xor_si512(B0, C0); \ + B1 = _mm512_xor_si512(B1, C1); \ +\ + B0 = ror64(B0, 63); \ + B1 = ror64(B1, 63); \ + } while ((void)0, 0) + +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm512_permutex_epi64(B0, _MM_SHUFFLE(0, 3, 2, 1)); \ + B1 = _mm512_permutex_epi64(B1, _MM_SHUFFLE(0, 3, 2, 1)); \ +\ + C0 = _mm512_permutex_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + C1 = _mm512_permutex_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ +\ + D0 = _mm512_permutex_epi64(D0, _MM_SHUFFLE(2, 1, 0, 3)); \ + D1 = _mm512_permutex_epi64(D1, _MM_SHUFFLE(2, 1, 0, 3)); \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm512_permutex_epi64(B0, _MM_SHUFFLE(2, 1, 0, 3)); \ + B1 = _mm512_permutex_epi64(B1, _MM_SHUFFLE(2, 1, 0, 3)); \ +\ + C0 = _mm512_permutex_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + C1 = _mm512_permutex_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ +\ + D0 = _mm512_permutex_epi64(D0, _MM_SHUFFLE(0, 3, 2, 1)); \ + D1 = _mm512_permutex_epi64(D1, _MM_SHUFFLE(0, 3, 2, 1)); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + } while ((void)0, 0) + +#define SWAP_HALVES(A0, A1) \ + do { \ + __m512i t0, t1; \ + t0 = _mm512_shuffle_i64x2(A0, A1, _MM_SHUFFLE(1, 0, 1, 0)); \ + t1 = _mm512_shuffle_i64x2(A0, A1, _MM_SHUFFLE(3, 2, 3, 2)); \ + A0 = t0; \ + A1 = t1; \ + } while((void)0, 0) + +#define SWAP_QUARTERS(A0, A1) \ + do { \ + SWAP_HALVES(A0, A1); \ + A0 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A0); \ + A1 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A1); \ + } while((void)0, 0) + +#define UNSWAP_QUARTERS(A0, A1) \ + do { \ + A0 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A0); \ + A1 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A1); \ + SWAP_HALVES(A0, A1); \ + } while((void)0, 0) + +#define BLAKE2_ROUND_1(A0, C0, B0, D0, A1, C1, B1, D1) \ + do { \ + SWAP_HALVES(A0, B0); \ + SWAP_HALVES(C0, D0); \ + SWAP_HALVES(A1, B1); \ + SWAP_HALVES(C1, D1); \ + BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1); \ + SWAP_HALVES(A0, B0); \ + SWAP_HALVES(C0, D0); \ + SWAP_HALVES(A1, B1); \ + SWAP_HALVES(C1, D1); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + SWAP_QUARTERS(A0, A1); \ + SWAP_QUARTERS(B0, B1); \ + SWAP_QUARTERS(C0, C1); \ + SWAP_QUARTERS(D0, D1); \ + BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1); \ + UNSWAP_QUARTERS(A0, A1); \ + UNSWAP_QUARTERS(B0, B1); \ + UNSWAP_QUARTERS(C0, C1); \ + UNSWAP_QUARTERS(D0, D1); \ + } while ((void)0, 0) + +#endif /* __AVX512F__ */ +#endif /* BLAKE_ROUND_MKA_OPT_H */ diff --git a/Blastproof/keygen/argon2/src/blake2/blamka-round-ref.h b/Blastproof/keygen/argon2/src/blake2/blamka-round-ref.h new file mode 100644 index 0000000..16cfc1c --- /dev/null +++ b/Blastproof/keygen/argon2/src/blake2/blamka-round-ref.h @@ -0,0 +1,56 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef BLAKE_ROUND_MKA_H +#define BLAKE_ROUND_MKA_H + +#include "blake2.h" +#include "blake2-impl.h" + +/* designed by the Lyra PHC team */ +static BLAKE2_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) { + const uint64_t m = UINT64_C(0xFFFFFFFF); + const uint64_t xy = (x & m) * (y & m); + return x + y + 2 * xy; +} + +#define G(a, b, c, d) \ + do { \ + a = fBlaMka(a, b); \ + d = rotr64(d ^ a, 32); \ + c = fBlaMka(c, d); \ + b = rotr64(b ^ c, 24); \ + a = fBlaMka(a, b); \ + d = rotr64(d ^ a, 16); \ + c = fBlaMka(c, d); \ + b = rotr64(b ^ c, 63); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \ + v12, v13, v14, v15) \ + do { \ + G(v0, v4, v8, v12); \ + G(v1, v5, v9, v13); \ + G(v2, v6, v10, v14); \ + G(v3, v7, v11, v15); \ + G(v0, v5, v10, v15); \ + G(v1, v6, v11, v12); \ + G(v2, v7, v8, v13); \ + G(v3, v4, v9, v14); \ + } while ((void)0, 0) + +#endif diff --git a/Blastproof/keygen/argon2/src/core.c b/Blastproof/keygen/argon2/src/core.c new file mode 100644 index 0000000..e697882 --- /dev/null +++ b/Blastproof/keygen/argon2/src/core.c @@ -0,0 +1,648 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +/*For memory wiping*/ +#ifdef _WIN32 +#include +#include /* For SecureZeroMemory */ +#endif +#if defined __STDC_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 +#endif +#define VC_GE_2005(version) (version >= 1400) + +/* for explicit_bzero() on glibc */ +#define _DEFAULT_SOURCE + +#include +#include +#include + +#include "core.h" +#include "thread.h" +#include "blake2/blake2.h" +#include "blake2/blake2-impl.h" + +#ifdef GENKAT +#include "genkat.h" +#endif + +#if defined(__clang__) +#if __has_attribute(optnone) +#define NOT_OPTIMIZED __attribute__((optnone)) +#endif +#elif defined(__GNUC__) +#define GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if GCC_VERSION >= 40400 +#define NOT_OPTIMIZED __attribute__((optimize("O0"))) +#endif +#endif +#ifndef NOT_OPTIMIZED +#define NOT_OPTIMIZED +#endif + +/***************Instance and Position constructors**********/ +void init_block_value(block *b, uint8_t in) { memset(b->v, in, sizeof(b->v)); } + +void copy_block(block *dst, const block *src) { + memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK); +} + +void xor_block(block *dst, const block *src) { + int i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + dst->v[i] ^= src->v[i]; + } +} + +static void load_block(block *dst, const void *input) { + unsigned i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i])); + } +} + +static void store_block(void *output, const block *src) { + unsigned i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]); + } +} + +/***************Memory functions*****************/ + +int allocate_memory(const argon2_context *context, uint8_t **memory, + size_t num, size_t size) { + size_t memory_size = num*size; + if (memory == NULL) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + /* 1. Check for multiplication overflow */ + if (size != 0 && memory_size / size != num) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + /* 2. Try to allocate with appropriate allocator */ + if (context->allocate_cbk) { + (context->allocate_cbk)(memory, memory_size); + } else { + *memory = malloc(memory_size); + } + + if (*memory == NULL) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + return ARGON2_OK; +} + +void free_memory(const argon2_context *context, uint8_t *memory, + size_t num, size_t size) { + size_t memory_size = num*size; + clear_internal_memory(memory, memory_size); + if (context->free_cbk) { + (context->free_cbk)(memory, memory_size); + } else { + free(memory); + } +} + +#if defined(__OpenBSD__) +#define HAVE_EXPLICIT_BZERO 1 +#elif defined(__GLIBC__) && defined(__GLIBC_PREREQ) +#if __GLIBC_PREREQ(2,25) +#define HAVE_EXPLICIT_BZERO 1 +#endif +#endif + +void NOT_OPTIMIZED secure_wipe_memory(void *v, size_t n) { +#if defined(_MSC_VER) && VC_GE_2005(_MSC_VER) || defined(__MINGW32__) + SecureZeroMemory(v, n); +#elif defined memset_s + memset_s(v, n, 0, n); +#elif defined(HAVE_EXPLICIT_BZERO) + explicit_bzero(v, n); +#else + static void *(*const volatile memset_sec)(void *, int, size_t) = &memset; + memset_sec(v, 0, n); +#endif +} + +/* Memory clear flag defaults to true. */ +int FLAG_clear_internal_memory = 1; +void clear_internal_memory(void *v, size_t n) { + if (FLAG_clear_internal_memory && v) { + secure_wipe_memory(v, n); + } +} + +void finalize(const argon2_context *context, argon2_instance_t *instance) { + if (context != NULL && instance != NULL) { + block blockhash; + uint32_t l; + + copy_block(&blockhash, instance->memory + instance->lane_length - 1); + + /* XOR the last blocks */ + for (l = 1; l < instance->lanes; ++l) { + uint32_t last_block_in_lane = + l * instance->lane_length + (instance->lane_length - 1); + xor_block(&blockhash, instance->memory + last_block_in_lane); + } + + /* Hash the result */ + { + uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE]; + store_block(blockhash_bytes, &blockhash); + blake2b_long(context->out, context->outlen, blockhash_bytes, + ARGON2_BLOCK_SIZE); + /* clear blockhash and blockhash_bytes */ + clear_internal_memory(blockhash.v, ARGON2_BLOCK_SIZE); + clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); + } + +#ifdef GENKAT + print_tag(context->out, context->outlen); +#endif + + free_memory(context, (uint8_t *)instance->memory, + instance->memory_blocks, sizeof(block)); + } +} + +uint32_t index_alpha(const argon2_instance_t *instance, + const argon2_position_t *position, uint32_t pseudo_rand, + int same_lane) { + /* + * Pass 0: + * This lane : all already finished segments plus already constructed + * blocks in this segment + * Other lanes : all already finished segments + * Pass 1+: + * This lane : (SYNC_POINTS - 1) last segments plus already constructed + * blocks in this segment + * Other lanes : (SYNC_POINTS - 1) last segments + */ + uint32_t reference_area_size; + uint64_t relative_position; + uint32_t start_position, absolute_position; + + if (0 == position->pass) { + /* First pass */ + if (0 == position->slice) { + /* First slice */ + reference_area_size = + position->index - 1; /* all but the previous */ + } else { + if (same_lane) { + /* The same lane => add current segment */ + reference_area_size = + position->slice * instance->segment_length + + position->index - 1; + } else { + reference_area_size = + position->slice * instance->segment_length + + ((position->index == 0) ? (-1) : 0); + } + } + } else { + /* Second pass */ + if (same_lane) { + reference_area_size = instance->lane_length - + instance->segment_length + position->index - + 1; + } else { + reference_area_size = instance->lane_length - + instance->segment_length + + ((position->index == 0) ? (-1) : 0); + } + } + + /* 1.2.4. Mapping pseudo_rand to 0.. and produce + * relative position */ + relative_position = pseudo_rand; + relative_position = relative_position * relative_position >> 32; + relative_position = reference_area_size - 1 - + (reference_area_size * relative_position >> 32); + + /* 1.2.5 Computing starting position */ + start_position = 0; + + if (0 != position->pass) { + start_position = (position->slice == ARGON2_SYNC_POINTS - 1) + ? 0 + : (position->slice + 1) * instance->segment_length; + } + + /* 1.2.6. Computing absolute position */ + absolute_position = (start_position + relative_position) % + instance->lane_length; /* absolute position */ + return absolute_position; +} + +/* Single-threaded version for p=1 case */ +static int fill_memory_blocks_st(argon2_instance_t *instance) { + uint32_t r, s, l; + + for (r = 0; r < instance->passes; ++r) { + for (s = 0; s < ARGON2_SYNC_POINTS; ++s) { + for (l = 0; l < instance->lanes; ++l) { + argon2_position_t position = {r, l, (uint8_t)s, 0}; + fill_segment(instance, position); + } + } +#ifdef GENKAT + internal_kat(instance, r); /* Print all memory blocks */ +#endif + } + return ARGON2_OK; +} + +#if !defined(ARGON2_NO_THREADS) + +#ifdef _WIN32 +static unsigned __stdcall fill_segment_thr(void *thread_data) +#else +static void *fill_segment_thr(void *thread_data) +#endif +{ + argon2_thread_data *my_data = thread_data; + fill_segment(my_data->instance_ptr, my_data->pos); + argon2_thread_exit(); + return 0; +} + +/* Multi-threaded version for p > 1 case */ +static int fill_memory_blocks_mt(argon2_instance_t *instance) { + uint32_t r, s; + argon2_thread_handle_t *thread = NULL; + argon2_thread_data *thr_data = NULL; + int rc = ARGON2_OK; + + /* 1. Allocating space for threads */ + thread = calloc(instance->lanes, sizeof(argon2_thread_handle_t)); + if (thread == NULL) { + rc = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + thr_data = calloc(instance->lanes, sizeof(argon2_thread_data)); + if (thr_data == NULL) { + rc = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + for (r = 0; r < instance->passes; ++r) { + for (s = 0; s < ARGON2_SYNC_POINTS; ++s) { + uint32_t l, ll; + + /* 2. Calling threads */ + for (l = 0; l < instance->lanes; ++l) { + argon2_position_t position; + + /* 2.1 Join a thread if limit is exceeded */ + if (l >= instance->threads) { + if (argon2_thread_join(thread[l - instance->threads])) { + rc = ARGON2_THREAD_FAIL; + goto fail; + } + } + + /* 2.2 Create thread */ + position.pass = r; + position.lane = l; + position.slice = (uint8_t)s; + position.index = 0; + thr_data[l].instance_ptr = + instance; /* preparing the thread input */ + memcpy(&(thr_data[l].pos), &position, + sizeof(argon2_position_t)); + if (argon2_thread_create(&thread[l], &fill_segment_thr, + (void *)&thr_data[l])) { + /* Wait for already running threads */ + for (ll = 0; ll < l; ++ll) + argon2_thread_join(thread[ll]); + rc = ARGON2_THREAD_FAIL; + goto fail; + } + + /* fill_segment(instance, position); */ + /*Non-thread equivalent of the lines above */ + } + + /* 3. Joining remaining threads */ + for (l = instance->lanes - instance->threads; l < instance->lanes; + ++l) { + if (argon2_thread_join(thread[l])) { + rc = ARGON2_THREAD_FAIL; + goto fail; + } + } + } + +#ifdef GENKAT + internal_kat(instance, r); /* Print all memory blocks */ +#endif + } + +fail: + if (thread != NULL) { + free(thread); + } + if (thr_data != NULL) { + free(thr_data); + } + return rc; +} + +#endif /* ARGON2_NO_THREADS */ + +int fill_memory_blocks(argon2_instance_t *instance) { + if (instance == NULL || instance->lanes == 0) { + return ARGON2_INCORRECT_PARAMETER; + } +#if defined(ARGON2_NO_THREADS) + return fill_memory_blocks_st(instance); +#else + return instance->threads == 1 ? + fill_memory_blocks_st(instance) : fill_memory_blocks_mt(instance); +#endif +} + +int validate_inputs(const argon2_context *context) { + if (NULL == context) { + return ARGON2_INCORRECT_PARAMETER; + } + + if (NULL == context->out) { + return ARGON2_OUTPUT_PTR_NULL; + } + + /* Validate output length */ + if (ARGON2_MIN_OUTLEN > context->outlen) { + return ARGON2_OUTPUT_TOO_SHORT; + } + + if (ARGON2_MAX_OUTLEN < context->outlen) { + return ARGON2_OUTPUT_TOO_LONG; + } + + /* Validate password (required param) */ + if (NULL == context->pwd) { + if (0 != context->pwdlen) { + return ARGON2_PWD_PTR_MISMATCH; + } + } + + if (ARGON2_MIN_PWD_LENGTH > context->pwdlen) { + return ARGON2_PWD_TOO_SHORT; + } + + if (ARGON2_MAX_PWD_LENGTH < context->pwdlen) { + return ARGON2_PWD_TOO_LONG; + } + + /* Validate salt (required param) */ + if (NULL == context->salt) { + if (0 != context->saltlen) { + return ARGON2_SALT_PTR_MISMATCH; + } + } + + if (ARGON2_MIN_SALT_LENGTH > context->saltlen) { + return ARGON2_SALT_TOO_SHORT; + } + + if (ARGON2_MAX_SALT_LENGTH < context->saltlen) { + return ARGON2_SALT_TOO_LONG; + } + + /* Validate secret (optional param) */ + if (NULL == context->secret) { + if (0 != context->secretlen) { + return ARGON2_SECRET_PTR_MISMATCH; + } + } else { + if (ARGON2_MIN_SECRET > context->secretlen) { + return ARGON2_SECRET_TOO_SHORT; + } + if (ARGON2_MAX_SECRET < context->secretlen) { + return ARGON2_SECRET_TOO_LONG; + } + } + + /* Validate associated data (optional param) */ + if (NULL == context->ad) { + if (0 != context->adlen) { + return ARGON2_AD_PTR_MISMATCH; + } + } else { + if (ARGON2_MIN_AD_LENGTH > context->adlen) { + return ARGON2_AD_TOO_SHORT; + } + if (ARGON2_MAX_AD_LENGTH < context->adlen) { + return ARGON2_AD_TOO_LONG; + } + } + + /* Validate memory cost */ + if (ARGON2_MIN_MEMORY > context->m_cost) { + return ARGON2_MEMORY_TOO_LITTLE; + } + + if (ARGON2_MAX_MEMORY < context->m_cost) { + return ARGON2_MEMORY_TOO_MUCH; + } + + if (context->m_cost < 8 * context->lanes) { + return ARGON2_MEMORY_TOO_LITTLE; + } + + /* Validate time cost */ + if (ARGON2_MIN_TIME > context->t_cost) { + return ARGON2_TIME_TOO_SMALL; + } + + if (ARGON2_MAX_TIME < context->t_cost) { + return ARGON2_TIME_TOO_LARGE; + } + + /* Validate lanes */ + if (ARGON2_MIN_LANES > context->lanes) { + return ARGON2_LANES_TOO_FEW; + } + + if (ARGON2_MAX_LANES < context->lanes) { + return ARGON2_LANES_TOO_MANY; + } + + /* Validate threads */ + if (ARGON2_MIN_THREADS > context->threads) { + return ARGON2_THREADS_TOO_FEW; + } + + if (ARGON2_MAX_THREADS < context->threads) { + return ARGON2_THREADS_TOO_MANY; + } + + if (NULL != context->allocate_cbk && NULL == context->free_cbk) { + return ARGON2_FREE_MEMORY_CBK_NULL; + } + + if (NULL == context->allocate_cbk && NULL != context->free_cbk) { + return ARGON2_ALLOCATE_MEMORY_CBK_NULL; + } + + return ARGON2_OK; +} + +void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) { + uint32_t l; + /* Make the first and second block in each lane as G(H0||0||i) or + G(H0||1||i) */ + uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE]; + for (l = 0; l < instance->lanes; ++l) { + + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0); + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l); + blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, + ARGON2_PREHASH_SEED_LENGTH); + load_block(&instance->memory[l * instance->lane_length + 0], + blockhash_bytes); + + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1); + blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, + ARGON2_PREHASH_SEED_LENGTH); + load_block(&instance->memory[l * instance->lane_length + 1], + blockhash_bytes); + } + clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); +} + +void initial_hash(uint8_t *blockhash, argon2_context *context, + argon2_type type) { + blake2b_state BlakeHash; + uint8_t value[sizeof(uint32_t)]; + + if (NULL == context || NULL == blockhash) { + return; + } + + blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH); + + store32(&value, context->lanes); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->outlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->m_cost); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->t_cost); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->version); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, (uint32_t)type); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->pwdlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->pwd != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->pwd, + context->pwdlen); + + if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) { + secure_wipe_memory(context->pwd, context->pwdlen); + context->pwdlen = 0; + } + } + + store32(&value, context->saltlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->salt != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->salt, + context->saltlen); + } + + store32(&value, context->secretlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->secret != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->secret, + context->secretlen); + + if (context->flags & ARGON2_FLAG_CLEAR_SECRET) { + secure_wipe_memory(context->secret, context->secretlen); + context->secretlen = 0; + } + } + + store32(&value, context->adlen); + blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->ad != NULL) { + blake2b_update(&BlakeHash, (const uint8_t *)context->ad, + context->adlen); + } + + blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH); +} + +int initialize(argon2_instance_t *instance, argon2_context *context) { + uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; + int result = ARGON2_OK; + + if (instance == NULL || context == NULL) + return ARGON2_INCORRECT_PARAMETER; + instance->context_ptr = context; + + /* 1. Memory allocation */ + result = allocate_memory(context, (uint8_t **)&(instance->memory), + instance->memory_blocks, sizeof(block)); + if (result != ARGON2_OK) { + return result; + } + + /* 2. Initial hashing */ + /* H_0 + 8 extra bytes to produce the first blocks */ + /* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */ + /* Hashing all inputs */ + initial_hash(blockhash, context, instance->type); + /* Zeroing 8 extra bytes */ + clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, + ARGON2_PREHASH_SEED_LENGTH - + ARGON2_PREHASH_DIGEST_LENGTH); + +#ifdef GENKAT + initial_kat(blockhash, context, instance->type); +#endif + + /* 3. Creating first blocks, we always have at least two blocks in a slice + */ + fill_first_blocks(blockhash, instance); + /* Clearing the hash */ + clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH); + + return ARGON2_OK; +} diff --git a/Blastproof/keygen/argon2/src/core.h b/Blastproof/keygen/argon2/src/core.h new file mode 100644 index 0000000..59e2564 --- /dev/null +++ b/Blastproof/keygen/argon2/src/core.h @@ -0,0 +1,228 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_CORE_H +#define ARGON2_CORE_H + +#include "argon2.h" + +#define CONST_CAST(x) (x)(uintptr_t) + +/**********************Argon2 internal constants*******************************/ + +enum argon2_core_constants { + /* Memory block size in bytes */ + ARGON2_BLOCK_SIZE = 1024, + ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8, + ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16, + ARGON2_HWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 32, + ARGON2_512BIT_WORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 64, + + /* Number of pseudo-random values generated by one call to Blake in Argon2i + to + generate reference block positions */ + ARGON2_ADDRESSES_IN_BLOCK = 128, + + /* Pre-hashing digest length and its extension*/ + ARGON2_PREHASH_DIGEST_LENGTH = 64, + ARGON2_PREHASH_SEED_LENGTH = 72 +}; + +/*************************Argon2 internal data types***********************/ + +/* + * Structure for the (1KB) memory block implemented as 128 64-bit words. + * Memory blocks can be copied, XORed. Internal words can be accessed by [] (no + * bounds checking). + */ +typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block; + +/*****************Functions that work with the block******************/ + +/* Initialize each byte of the block with @in */ +void init_block_value(block *b, uint8_t in); + +/* Copy block @src to block @dst */ +void copy_block(block *dst, const block *src); + +/* XOR @src onto @dst bytewise */ +void xor_block(block *dst, const block *src); + +/* + * Argon2 instance: memory pointer, number of passes, amount of memory, type, + * and derived values. + * Used to evaluate the number and location of blocks to construct in each + * thread + */ +typedef struct Argon2_instance_t { + block *memory; /* Memory pointer */ + uint32_t version; + uint32_t passes; /* Number of passes */ + uint32_t memory_blocks; /* Number of blocks in memory */ + uint32_t segment_length; + uint32_t lane_length; + uint32_t lanes; + uint32_t threads; + argon2_type type; + int print_internals; /* whether to print the memory blocks */ + argon2_context *context_ptr; /* points back to original context */ +} argon2_instance_t; + +/* + * Argon2 position: where we construct the block right now. Used to distribute + * work between threads. + */ +typedef struct Argon2_position_t { + uint32_t pass; + uint32_t lane; + uint8_t slice; + uint32_t index; +} argon2_position_t; + +/*Struct that holds the inputs for thread handling FillSegment*/ +typedef struct Argon2_thread_data { + argon2_instance_t *instance_ptr; + argon2_position_t pos; +} argon2_thread_data; + +/*************************Argon2 core functions********************************/ + +/* Allocates memory to the given pointer, uses the appropriate allocator as + * specified in the context. Total allocated memory is num*size. + * @param context argon2_context which specifies the allocator + * @param memory pointer to the pointer to the memory + * @param size the size in bytes for each element to be allocated + * @param num the number of elements to be allocated + * @return ARGON2_OK if @memory is a valid pointer and memory is allocated + */ +int allocate_memory(const argon2_context *context, uint8_t **memory, + size_t num, size_t size); + +/* + * Frees memory at the given pointer, uses the appropriate deallocator as + * specified in the context. Also cleans the memory using clear_internal_memory. + * @param context argon2_context which specifies the deallocator + * @param memory pointer to buffer to be freed + * @param size the size in bytes for each element to be deallocated + * @param num the number of elements to be deallocated + */ +void free_memory(const argon2_context *context, uint8_t *memory, + size_t num, size_t size); + +/* Function that securely cleans the memory. This ignores any flags set + * regarding clearing memory. Usually one just calls clear_internal_memory. + * @param mem Pointer to the memory + * @param s Memory size in bytes + */ +void secure_wipe_memory(void *v, size_t n); + +/* Function that securely clears the memory if FLAG_clear_internal_memory is + * set. If the flag isn't set, this function does nothing. + * @param mem Pointer to the memory + * @param s Memory size in bytes + */ +void clear_internal_memory(void *v, size_t n); + +/* + * Computes absolute position of reference block in the lane following a skewed + * distribution and using a pseudo-random value as input + * @param instance Pointer to the current instance + * @param position Pointer to the current position + * @param pseudo_rand 32-bit pseudo-random value used to determine the position + * @param same_lane Indicates if the block will be taken from the current lane. + * If so we can reference the current segment + * @pre All pointers must be valid + */ +uint32_t index_alpha(const argon2_instance_t *instance, + const argon2_position_t *position, uint32_t pseudo_rand, + int same_lane); + +/* + * Function that validates all inputs against predefined restrictions and return + * an error code + * @param context Pointer to current Argon2 context + * @return ARGON2_OK if everything is all right, otherwise one of error codes + * (all defined in + */ +int validate_inputs(const argon2_context *context); + +/* + * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears + * password and secret if needed + * @param context Pointer to the Argon2 internal structure containing memory + * pointer, and parameters for time and space requirements. + * @param blockhash Buffer for pre-hashing digest + * @param type Argon2 type + * @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes + * allocated + */ +void initial_hash(uint8_t *blockhash, argon2_context *context, + argon2_type type); + +/* + * Function creates first 2 blocks per lane + * @param instance Pointer to the current instance + * @param blockhash Pointer to the pre-hashing digest + * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values + */ +void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance); + +/* + * Function allocates memory, hashes the inputs with Blake, and creates first + * two blocks. Returns the pointer to the main memory with 2 blocks per lane + * initialized + * @param context Pointer to the Argon2 internal structure containing memory + * pointer, and parameters for time and space requirements. + * @param instance Current Argon2 instance + * @return Zero if successful, -1 if memory failed to allocate. @context->state + * will be modified if successful. + */ +int initialize(argon2_instance_t *instance, argon2_context *context); + +/* + * XORing the last block of each lane, hashing it, making the tag. Deallocates + * the memory. + * @param context Pointer to current Argon2 context (use only the out parameters + * from it) + * @param instance Pointer to current instance of Argon2 + * @pre instance->state must point to necessary amount of memory + * @pre context->out must point to outlen bytes of memory + * @pre if context->free_cbk is not NULL, it should point to a function that + * deallocates memory + */ +void finalize(const argon2_context *context, argon2_instance_t *instance); + +/* + * Function that fills the segment using previous segments also from other + * threads + * @param context current context + * @param instance Pointer to the current instance + * @param position Current position + * @pre all block pointers must be valid + */ +void fill_segment(const argon2_instance_t *instance, + argon2_position_t position); + +/* + * Function that fills the entire memory t_cost times based on the first two + * blocks in each lane + * @param instance Pointer to the current instance + * @return ARGON2_OK if successful, @context->state + */ +int fill_memory_blocks(argon2_instance_t *instance); + +#endif diff --git a/Blastproof/keygen/argon2/src/core.o b/Blastproof/keygen/argon2/src/core.o new file mode 100644 index 0000000..9b9904c Binary files /dev/null and b/Blastproof/keygen/argon2/src/core.o differ diff --git a/Blastproof/keygen/argon2/src/encoding.c b/Blastproof/keygen/argon2/src/encoding.c new file mode 100644 index 0000000..771a440 --- /dev/null +++ b/Blastproof/keygen/argon2/src/encoding.c @@ -0,0 +1,463 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include +#include +#include "encoding.h" +#include "core.h" + +/* + * Example code for a decoder and encoder of "hash strings", with Argon2 + * parameters. + * + * This code comprises three sections: + * + * -- The first section contains generic Base64 encoding and decoding + * functions. It is conceptually applicable to any hash function + * implementation that uses Base64 to encode and decode parameters, + * salts and outputs. It could be made into a library, provided that + * the relevant functions are made public (non-static) and be given + * reasonable names to avoid collisions with other functions. + * + * -- The second section is specific to Argon2. It encodes and decodes + * the parameters, salts and outputs. It does not compute the hash + * itself. + * + * The code was originally written by Thomas Pornin , + * to whom comments and remarks may be sent. It is released under what + * should amount to Public Domain or its closest equivalent; the + * following mantra is supposed to incarnate that fact with all the + * proper legal rituals: + * + * --------------------------------------------------------------------- + * This file is provided under the terms of Creative Commons CC0 1.0 + * Public Domain Dedication. To the extent possible under law, the + * author (Thomas Pornin) has waived all copyright and related or + * neighboring rights to this file. This work is published from: Canada. + * --------------------------------------------------------------------- + * + * Copyright (c) 2015 Thomas Pornin + */ + +/* ==================================================================== */ +/* + * Common code; could be shared between different hash functions. + * + * Note: the Base64 functions below assume that uppercase letters (resp. + * lowercase letters) have consecutive numerical codes, that fit on 8 + * bits. All modern systems use ASCII-compatible charsets, where these + * properties are true. If you are stuck with a dinosaur of a system + * that still defaults to EBCDIC then you already have much bigger + * interoperability issues to deal with. + */ + +/* + * Some macros for constant-time comparisons. These work over values in + * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true". + */ +#define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF) +#define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF) +#define GE(x, y) (GT(y, x) ^ 0xFF) +#define LT(x, y) GT(y, x) +#define LE(x, y) GE(y, x) + +/* + * Convert value x (0..63) to corresponding Base64 character. + */ +static int b64_byte_to_char(unsigned x) { + return (LT(x, 26) & (x + 'A')) | + (GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) | + (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+') | + (EQ(x, 63) & '/'); +} + +/* + * Convert character c to the corresponding 6-bit value. If character c + * is not a Base64 character, then 0xFF (255) is returned. + */ +static unsigned b64_char_to_byte(int c) { + unsigned x; + + x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) | + (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) | + (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) | + (EQ(c, '/') & 63); + return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF)); +} + +/* + * Convert some bytes to Base64. 'dst_len' is the length (in characters) + * of the output buffer 'dst'; if that buffer is not large enough to + * receive the result (including the terminating 0), then (size_t)-1 + * is returned. Otherwise, the zero-terminated Base64 string is written + * in the buffer, and the output length (counted WITHOUT the terminating + * zero) is returned. + */ +static size_t to_base64(char *dst, size_t dst_len, const void *src, + size_t src_len) { + size_t olen; + const unsigned char *buf; + unsigned acc, acc_len; + + olen = (src_len / 3) << 2; + switch (src_len % 3) { + case 2: + olen++; + /* fall through */ + case 1: + olen += 2; + break; + } + if (dst_len <= olen) { + return (size_t)-1; + } + acc = 0; + acc_len = 0; + buf = (const unsigned char *)src; + while (src_len-- > 0) { + acc = (acc << 8) + (*buf++); + acc_len += 8; + while (acc_len >= 6) { + acc_len -= 6; + *dst++ = (char)b64_byte_to_char((acc >> acc_len) & 0x3F); + } + } + if (acc_len > 0) { + *dst++ = (char)b64_byte_to_char((acc << (6 - acc_len)) & 0x3F); + } + *dst++ = 0; + return olen; +} + +/* + * Decode Base64 chars into bytes. The '*dst_len' value must initially + * contain the length of the output buffer '*dst'; when the decoding + * ends, the actual number of decoded bytes is written back in + * '*dst_len'. + * + * Decoding stops when a non-Base64 character is encountered, or when + * the output buffer capacity is exceeded. If an error occurred (output + * buffer is too small, invalid last characters leading to unprocessed + * buffered bits), then NULL is returned; otherwise, the returned value + * points to the first non-Base64 character in the source stream, which + * may be the terminating zero. + */ +static const char *from_base64(void *dst, size_t *dst_len, const char *src) { + size_t len; + unsigned char *buf; + unsigned acc, acc_len; + + buf = (unsigned char *)dst; + len = 0; + acc = 0; + acc_len = 0; + for (;;) { + unsigned d; + + d = b64_char_to_byte(*src); + if (d == 0xFF) { + break; + } + src++; + acc = (acc << 6) + d; + acc_len += 6; + if (acc_len >= 8) { + acc_len -= 8; + if ((len++) >= *dst_len) { + return NULL; + } + *buf++ = (acc >> acc_len) & 0xFF; + } + } + + /* + * If the input length is equal to 1 modulo 4 (which is + * invalid), then there will remain 6 unprocessed bits; + * otherwise, only 0, 2 or 4 bits are buffered. The buffered + * bits must also all be zero. + */ + if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) { + return NULL; + } + *dst_len = len; + return src; +} + +/* + * Decode decimal integer from 'str'; the value is written in '*v'. + * Returned value is a pointer to the next non-decimal character in the + * string. If there is no digit at all, or the value encoding is not + * minimal (extra leading zeros), or the value does not fit in an + * 'unsigned long', then NULL is returned. + */ +static const char *decode_decimal(const char *str, unsigned long *v) { + const char *orig; + unsigned long acc; + + acc = 0; + for (orig = str;; str++) { + int c; + + c = *str; + if (c < '0' || c > '9') { + break; + } + c -= '0'; + if (acc > (ULONG_MAX / 10)) { + return NULL; + } + acc *= 10; + if ((unsigned long)c > (ULONG_MAX - acc)) { + return NULL; + } + acc += (unsigned long)c; + } + if (str == orig || (*orig == '0' && str != (orig + 1))) { + return NULL; + } + *v = acc; + return str; +} + +/* ==================================================================== */ +/* + * Code specific to Argon2. + * + * The code below applies the following format: + * + * $argon2[$v=]$m=,t=,p=$$ + * + * where is either 'd', 'id', or 'i', is a decimal integer (positive, + * fits in an 'unsigned long'), and is Base64-encoded data (no '=' padding + * characters, no newline or whitespace). + * + * The last two binary chunks (encoded in Base64) are, in that order, + * the salt and the output. Both are required. The binary salt length and the + * output length must be in the allowed ranges defined in argon2.h. + * + * The ctx struct must contain buffers large enough to hold the salt and pwd + * when it is fed into decode_string. + */ + +int decode_string(argon2_context *ctx, const char *str, argon2_type type) { + +/* check for prefix */ +#define CC(prefix) \ + do { \ + size_t cc_len = strlen(prefix); \ + if (strncmp(str, prefix, cc_len) != 0) { \ + return ARGON2_DECODING_FAIL; \ + } \ + str += cc_len; \ + } while ((void)0, 0) + +/* optional prefix checking with supplied code */ +#define CC_opt(prefix, code) \ + do { \ + size_t cc_len = strlen(prefix); \ + if (strncmp(str, prefix, cc_len) == 0) { \ + str += cc_len; \ + { code; } \ + } \ + } while ((void)0, 0) + +/* Decoding prefix into decimal */ +#define DECIMAL(x) \ + do { \ + unsigned long dec_x; \ + str = decode_decimal(str, &dec_x); \ + if (str == NULL) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (x) = dec_x; \ + } while ((void)0, 0) + + +/* Decoding prefix into uint32_t decimal */ +#define DECIMAL_U32(x) \ + do { \ + unsigned long dec_x; \ + str = decode_decimal(str, &dec_x); \ + if (str == NULL || dec_x > UINT32_MAX) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (x) = (uint32_t)dec_x; \ + } while ((void)0, 0) + + +/* Decoding base64 into a binary buffer */ +#define BIN(buf, max_len, len) \ + do { \ + size_t bin_len = (max_len); \ + str = from_base64(buf, &bin_len, str); \ + if (str == NULL || bin_len > UINT32_MAX) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (len) = (uint32_t)bin_len; \ + } while ((void)0, 0) + + size_t maxsaltlen = ctx->saltlen; + size_t maxoutlen = ctx->outlen; + int validation_result; + const char* type_string; + + /* We should start with the argon2_type we are using */ + type_string = argon2_type2string(type, 0); + if (!type_string) { + return ARGON2_INCORRECT_TYPE; + } + + CC("$"); + CC(type_string); + + /* Reading the version number if the default is suppressed */ + ctx->version = ARGON2_VERSION_10; + CC_opt("$v=", DECIMAL_U32(ctx->version)); + + CC("$m="); + DECIMAL_U32(ctx->m_cost); + CC(",t="); + DECIMAL_U32(ctx->t_cost); + CC(",p="); + DECIMAL_U32(ctx->lanes); + ctx->threads = ctx->lanes; + + CC("$"); + BIN(ctx->salt, maxsaltlen, ctx->saltlen); + CC("$"); + BIN(ctx->out, maxoutlen, ctx->outlen); + + /* The rest of the fields get the default values */ + ctx->secret = NULL; + ctx->secretlen = 0; + ctx->ad = NULL; + ctx->adlen = 0; + ctx->allocate_cbk = NULL; + ctx->free_cbk = NULL; + ctx->flags = ARGON2_DEFAULT_FLAGS; + + /* On return, must have valid context */ + validation_result = validate_inputs(ctx); + if (validation_result != ARGON2_OK) { + return validation_result; + } + + /* Can't have any additional characters */ + if (*str == 0) { + return ARGON2_OK; + } else { + return ARGON2_DECODING_FAIL; + } +#undef CC +#undef CC_opt +#undef DECIMAL +#undef BIN +} + +int encode_string(char *dst, size_t dst_len, argon2_context *ctx, + argon2_type type) { +#define SS(str) \ + do { \ + size_t pp_len = strlen(str); \ + if (pp_len >= dst_len) { \ + return ARGON2_ENCODING_FAIL; \ + } \ + memcpy(dst, str, pp_len + 1); \ + dst += pp_len; \ + dst_len -= pp_len; \ + } while ((void)0, 0) + +#define SX(x) \ + do { \ + char tmp[30]; \ + sprintf(tmp, "%lu", (unsigned long)(x)); \ + SS(tmp); \ + } while ((void)0, 0) + +#define SB(buf, len) \ + do { \ + size_t sb_len = to_base64(dst, dst_len, buf, len); \ + if (sb_len == (size_t)-1) { \ + return ARGON2_ENCODING_FAIL; \ + } \ + dst += sb_len; \ + dst_len -= sb_len; \ + } while ((void)0, 0) + + const char* type_string = argon2_type2string(type, 0); + int validation_result = validate_inputs(ctx); + + if (!type_string) { + return ARGON2_ENCODING_FAIL; + } + + if (validation_result != ARGON2_OK) { + return validation_result; + } + + + SS("$"); + SS(type_string); + + SS("$v="); + SX(ctx->version); + + SS("$m="); + SX(ctx->m_cost); + SS(",t="); + SX(ctx->t_cost); + SS(",p="); + SX(ctx->lanes); + + SS("$"); + SB(ctx->salt, ctx->saltlen); + + SS("$"); + SB(ctx->out, ctx->outlen); + return ARGON2_OK; + +#undef SS +#undef SX +#undef SB +} + +size_t b64len(uint32_t len) { + size_t olen = ((size_t)len / 3) << 2; + + switch (len % 3) { + case 2: + olen++; + /* fall through */ + case 1: + olen += 2; + break; + } + + return olen; +} + +size_t numlen(uint32_t num) { + size_t len = 1; + while (num >= 10) { + ++len; + num = num / 10; + } + return len; +} + diff --git a/Blastproof/keygen/argon2/src/encoding.h b/Blastproof/keygen/argon2/src/encoding.h new file mode 100644 index 0000000..5b8b2dd --- /dev/null +++ b/Blastproof/keygen/argon2/src/encoding.h @@ -0,0 +1,57 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ENCODING_H +#define ENCODING_H +#include "argon2.h" + +#define ARGON2_MAX_DECODED_LANES UINT32_C(255) +#define ARGON2_MIN_DECODED_SALT_LEN UINT32_C(8) +#define ARGON2_MIN_DECODED_OUT_LEN UINT32_C(12) + +/* +* encode an Argon2 hash string into the provided buffer. 'dst_len' +* contains the size, in characters, of the 'dst' buffer; if 'dst_len' +* is less than the number of required characters (including the +* terminating 0), then this function returns ARGON2_ENCODING_ERROR. +* +* on success, ARGON2_OK is returned. +*/ +int encode_string(char *dst, size_t dst_len, argon2_context *ctx, + argon2_type type); + +/* +* Decodes an Argon2 hash string into the provided structure 'ctx'. +* The only fields that must be set prior to this call are ctx.saltlen and +* ctx.outlen (which must be the maximal salt and out length values that are +* allowed), ctx.salt and ctx.out (which must be buffers of the specified +* length), and ctx.pwd and ctx.pwdlen which must hold a valid password. +* +* Invalid input string causes an error. On success, the ctx is valid and all +* fields have been initialized. +* +* Returned value is ARGON2_OK on success, other ARGON2_ codes on error. +*/ +int decode_string(argon2_context *ctx, const char *str, argon2_type type); + +/* Returns the length of the encoded byte stream with length len */ +size_t b64len(uint32_t len); + +/* Returns the length of the encoded number num */ +size_t numlen(uint32_t num); + +#endif diff --git a/Blastproof/keygen/argon2/src/encoding.o b/Blastproof/keygen/argon2/src/encoding.o new file mode 100644 index 0000000..541a732 Binary files /dev/null and b/Blastproof/keygen/argon2/src/encoding.o differ diff --git a/Blastproof/keygen/argon2/src/genkat.c b/Blastproof/keygen/argon2/src/genkat.c new file mode 100644 index 0000000..dbbc072 --- /dev/null +++ b/Blastproof/keygen/argon2/src/genkat.c @@ -0,0 +1,213 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include +#include "argon2.h" +#include "core.h" +#ifdef __MINGW32__ +#include +#else +/* Don't use (it's not C89) */ +#define PRIx64 "llx" +#endif + +void initial_kat(const uint8_t *blockhash, const argon2_context *context, + argon2_type type) { + unsigned i; + + if (blockhash != NULL && context != NULL) { + printf("=======================================\n"); + + printf("%s version number %d\n", argon2_type2string(type, 1), + context->version); + + printf("=======================================\n"); + + + printf("Memory: %u KiB, Iterations: %u, Parallelism: %u lanes, Tag " + "length: %u bytes\n", + context->m_cost, context->t_cost, context->lanes, + context->outlen); + + printf("Password[%u]: ", context->pwdlen); + + if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) { + printf("CLEARED\n"); + } else { + for (i = 0; i < context->pwdlen; ++i) { + printf("%2.2x ", ((unsigned char *)context->pwd)[i]); + } + + printf("\n"); + } + + printf("Salt[%u]: ", context->saltlen); + + for (i = 0; i < context->saltlen; ++i) { + printf("%2.2x ", ((unsigned char *)context->salt)[i]); + } + + printf("\n"); + + printf("Secret[%u]: ", context->secretlen); + + if (context->flags & ARGON2_FLAG_CLEAR_SECRET) { + printf("CLEARED\n"); + } else { + for (i = 0; i < context->secretlen; ++i) { + printf("%2.2x ", ((unsigned char *)context->secret)[i]); + } + + printf("\n"); + } + + printf("Associated data[%u]: ", context->adlen); + + for (i = 0; i < context->adlen; ++i) { + printf("%2.2x ", ((unsigned char *)context->ad)[i]); + } + + printf("\n"); + + printf("Pre-hashing digest: "); + + for (i = 0; i < ARGON2_PREHASH_DIGEST_LENGTH; ++i) { + printf("%2.2x ", ((unsigned char *)blockhash)[i]); + } + + printf("\n"); + } +} + +void print_tag(const void *out, uint32_t outlen) { + unsigned i; + if (out != NULL) { + printf("Tag: "); + + for (i = 0; i < outlen; ++i) { + printf("%2.2x ", ((uint8_t *)out)[i]); + } + + printf("\n"); + } +} + +void internal_kat(const argon2_instance_t *instance, uint32_t pass) { + + if (instance != NULL) { + uint32_t i, j; + printf("\n After pass %u:\n", pass); + + for (i = 0; i < instance->memory_blocks; ++i) { + uint32_t how_many_words = + (instance->memory_blocks > ARGON2_QWORDS_IN_BLOCK) + ? 1 + : ARGON2_QWORDS_IN_BLOCK; + + for (j = 0; j < how_many_words; ++j) + printf("Block %.4u [%3u]: %016" PRIx64 "\n", i, j, + (unsigned long long)instance->memory[i].v[j]); + } + } +} + +static void fatal(const char *error) { + fprintf(stderr, "Error: %s\n", error); + exit(1); +} + +static void generate_testvectors(argon2_type type, const uint32_t version) { +#define TEST_OUTLEN 32 +#define TEST_PWDLEN 32 +#define TEST_SALTLEN 16 +#define TEST_SECRETLEN 8 +#define TEST_ADLEN 12 + argon2_context context; + + unsigned char out[TEST_OUTLEN]; + unsigned char pwd[TEST_PWDLEN]; + unsigned char salt[TEST_SALTLEN]; + unsigned char secret[TEST_SECRETLEN]; + unsigned char ad[TEST_ADLEN]; + const allocate_fptr myown_allocator = NULL; + const deallocate_fptr myown_deallocator = NULL; + + unsigned t_cost = 3; + unsigned m_cost = 32; + unsigned lanes = 4; + + memset(pwd, 1, TEST_OUTLEN); + memset(salt, 2, TEST_SALTLEN); + memset(secret, 3, TEST_SECRETLEN); + memset(ad, 4, TEST_ADLEN); + + context.out = out; + context.outlen = TEST_OUTLEN; + context.version = version; + context.pwd = pwd; + context.pwdlen = TEST_PWDLEN; + context.salt = salt; + context.saltlen = TEST_SALTLEN; + context.secret = secret; + context.secretlen = TEST_SECRETLEN; + context.ad = ad; + context.adlen = TEST_ADLEN; + context.t_cost = t_cost; + context.m_cost = m_cost; + context.lanes = lanes; + context.threads = lanes; + context.allocate_cbk = myown_allocator; + context.free_cbk = myown_deallocator; + context.flags = ARGON2_DEFAULT_FLAGS; + +#undef TEST_OUTLEN +#undef TEST_PWDLEN +#undef TEST_SALTLEN +#undef TEST_SECRETLEN +#undef TEST_ADLEN + + argon2_ctx(&context, type); +} + +int main(int argc, char *argv[]) { + /* Get and check Argon2 type */ + const char *type_str = (argc > 1) ? argv[1] : "i"; + argon2_type type = Argon2_i; + uint32_t version = ARGON2_VERSION_NUMBER; + if (!strcmp(type_str, "d")) { + type = Argon2_d; + } else if (!strcmp(type_str, "i")) { + type = Argon2_i; + } else if (!strcmp(type_str, "id")) { + type = Argon2_id; + } else { + fatal("wrong Argon2 type"); + } + + /* Get and check Argon2 version number */ + if (argc > 2) { + version = strtoul(argv[2], NULL, 10); + } + if (ARGON2_VERSION_10 != version && ARGON2_VERSION_NUMBER != version) { + fatal("wrong Argon2 version number"); + } + + generate_testvectors(type, version); + return ARGON2_OK; +} diff --git a/Blastproof/keygen/argon2/src/genkat.h b/Blastproof/keygen/argon2/src/genkat.h new file mode 100644 index 0000000..3a7162a --- /dev/null +++ b/Blastproof/keygen/argon2/src/genkat.h @@ -0,0 +1,51 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_KAT_H +#define ARGON2_KAT_H + +#include "core.h" + +/* + * Initial KAT function that prints the inputs to the file + * @param blockhash Array that contains pre-hashing digest + * @param context Holds inputs + * @param type Argon2 type + * @pre blockhash must point to INPUT_INITIAL_HASH_LENGTH bytes + * @pre context member pointers must point to allocated memory of size according + * to the length values + */ +void initial_kat(const uint8_t *blockhash, const argon2_context *context, + argon2_type type); + +/* + * Function that prints the output tag + * @param out output array pointer + * @param outlen digest length + * @pre out must point to @a outlen bytes + **/ +void print_tag(const void *out, uint32_t outlen); + +/* + * Function that prints the internal state at given moment + * @param instance pointer to the current instance + * @param pass current pass number + * @pre instance must have necessary memory allocated + **/ +void internal_kat(const argon2_instance_t *instance, uint32_t pass); + +#endif diff --git a/Blastproof/keygen/argon2/src/opt.c b/Blastproof/keygen/argon2/src/opt.c new file mode 100644 index 0000000..6c5e403 --- /dev/null +++ b/Blastproof/keygen/argon2/src/opt.c @@ -0,0 +1,283 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include + +#include "argon2.h" +#include "core.h" + +#include "blake2/blake2.h" +#include "blake2/blamka-round-opt.h" + +/* + * Function fills a new memory block and optionally XORs the old block over the new one. + * Memory must be initialized. + * @param state Pointer to the just produced block. Content will be updated(!) + * @param ref_block Pointer to the reference block + * @param next_block Pointer to the block to be XORed over. May coincide with @ref_block + * @param with_xor Whether to XOR into the new block (1) or just overwrite (0) + * @pre all block pointers must be valid + */ +#if defined(__AVX512F__) +static void fill_block(__m512i *state, const block *ref_block, + block *next_block, int with_xor) { + __m512i block_XY[ARGON2_512BIT_WORDS_IN_BLOCK]; + unsigned int i; + + if (with_xor) { + for (i = 0; i < ARGON2_512BIT_WORDS_IN_BLOCK; i++) { + state[i] = _mm512_xor_si512( + state[i], _mm512_loadu_si512((const __m512i *)ref_block->v + i)); + block_XY[i] = _mm512_xor_si512( + state[i], _mm512_loadu_si512((const __m512i *)next_block->v + i)); + } + } else { + for (i = 0; i < ARGON2_512BIT_WORDS_IN_BLOCK; i++) { + block_XY[i] = state[i] = _mm512_xor_si512( + state[i], _mm512_loadu_si512((const __m512i *)ref_block->v + i)); + } + } + + for (i = 0; i < 2; ++i) { + BLAKE2_ROUND_1( + state[8 * i + 0], state[8 * i + 1], state[8 * i + 2], state[8 * i + 3], + state[8 * i + 4], state[8 * i + 5], state[8 * i + 6], state[8 * i + 7]); + } + + for (i = 0; i < 2; ++i) { + BLAKE2_ROUND_2( + state[2 * 0 + i], state[2 * 1 + i], state[2 * 2 + i], state[2 * 3 + i], + state[2 * 4 + i], state[2 * 5 + i], state[2 * 6 + i], state[2 * 7 + i]); + } + + for (i = 0; i < ARGON2_512BIT_WORDS_IN_BLOCK; i++) { + state[i] = _mm512_xor_si512(state[i], block_XY[i]); + _mm512_storeu_si512((__m512i *)next_block->v + i, state[i]); + } +} +#elif defined(__AVX2__) +static void fill_block(__m256i *state, const block *ref_block, + block *next_block, int with_xor) { + __m256i block_XY[ARGON2_HWORDS_IN_BLOCK]; + unsigned int i; + + if (with_xor) { + for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) { + state[i] = _mm256_xor_si256( + state[i], _mm256_loadu_si256((const __m256i *)ref_block->v + i)); + block_XY[i] = _mm256_xor_si256( + state[i], _mm256_loadu_si256((const __m256i *)next_block->v + i)); + } + } else { + for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) { + block_XY[i] = state[i] = _mm256_xor_si256( + state[i], _mm256_loadu_si256((const __m256i *)ref_block->v + i)); + } + } + + for (i = 0; i < 4; ++i) { + BLAKE2_ROUND_1(state[8 * i + 0], state[8 * i + 4], state[8 * i + 1], state[8 * i + 5], + state[8 * i + 2], state[8 * i + 6], state[8 * i + 3], state[8 * i + 7]); + } + + for (i = 0; i < 4; ++i) { + BLAKE2_ROUND_2(state[ 0 + i], state[ 4 + i], state[ 8 + i], state[12 + i], + state[16 + i], state[20 + i], state[24 + i], state[28 + i]); + } + + for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) { + state[i] = _mm256_xor_si256(state[i], block_XY[i]); + _mm256_storeu_si256((__m256i *)next_block->v + i, state[i]); + } +} +#else +static void fill_block(__m128i *state, const block *ref_block, + block *next_block, int with_xor) { + __m128i block_XY[ARGON2_OWORDS_IN_BLOCK]; + unsigned int i; + + if (with_xor) { + for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) { + state[i] = _mm_xor_si128( + state[i], _mm_loadu_si128((const __m128i *)ref_block->v + i)); + block_XY[i] = _mm_xor_si128( + state[i], _mm_loadu_si128((const __m128i *)next_block->v + i)); + } + } else { + for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) { + block_XY[i] = state[i] = _mm_xor_si128( + state[i], _mm_loadu_si128((const __m128i *)ref_block->v + i)); + } + } + + for (i = 0; i < 8; ++i) { + BLAKE2_ROUND(state[8 * i + 0], state[8 * i + 1], state[8 * i + 2], + state[8 * i + 3], state[8 * i + 4], state[8 * i + 5], + state[8 * i + 6], state[8 * i + 7]); + } + + for (i = 0; i < 8; ++i) { + BLAKE2_ROUND(state[8 * 0 + i], state[8 * 1 + i], state[8 * 2 + i], + state[8 * 3 + i], state[8 * 4 + i], state[8 * 5 + i], + state[8 * 6 + i], state[8 * 7 + i]); + } + + for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) { + state[i] = _mm_xor_si128(state[i], block_XY[i]); + _mm_storeu_si128((__m128i *)next_block->v + i, state[i]); + } +} +#endif + +static void next_addresses(block *address_block, block *input_block) { + /*Temporary zero-initialized blocks*/ +#if defined(__AVX512F__) + __m512i zero_block[ARGON2_512BIT_WORDS_IN_BLOCK]; + __m512i zero2_block[ARGON2_512BIT_WORDS_IN_BLOCK]; +#elif defined(__AVX2__) + __m256i zero_block[ARGON2_HWORDS_IN_BLOCK]; + __m256i zero2_block[ARGON2_HWORDS_IN_BLOCK]; +#else + __m128i zero_block[ARGON2_OWORDS_IN_BLOCK]; + __m128i zero2_block[ARGON2_OWORDS_IN_BLOCK]; +#endif + + memset(zero_block, 0, sizeof(zero_block)); + memset(zero2_block, 0, sizeof(zero2_block)); + + /*Increasing index counter*/ + input_block->v[6]++; + + /*First iteration of G*/ + fill_block(zero_block, input_block, address_block, 0); + + /*Second iteration of G*/ + fill_block(zero2_block, address_block, address_block, 0); +} + +void fill_segment(const argon2_instance_t *instance, + argon2_position_t position) { + block *ref_block = NULL, *curr_block = NULL; + block address_block, input_block; + uint64_t pseudo_rand, ref_index, ref_lane; + uint32_t prev_offset, curr_offset; + uint32_t starting_index, i; +#if defined(__AVX512F__) + __m512i state[ARGON2_512BIT_WORDS_IN_BLOCK]; +#elif defined(__AVX2__) + __m256i state[ARGON2_HWORDS_IN_BLOCK]; +#else + __m128i state[ARGON2_OWORDS_IN_BLOCK]; +#endif + int data_independent_addressing; + + if (instance == NULL) { + return; + } + + data_independent_addressing = + (instance->type == Argon2_i) || + (instance->type == Argon2_id && (position.pass == 0) && + (position.slice < ARGON2_SYNC_POINTS / 2)); + + if (data_independent_addressing) { + init_block_value(&input_block, 0); + + input_block.v[0] = position.pass; + input_block.v[1] = position.lane; + input_block.v[2] = position.slice; + input_block.v[3] = instance->memory_blocks; + input_block.v[4] = instance->passes; + input_block.v[5] = instance->type; + } + + starting_index = 0; + + if ((0 == position.pass) && (0 == position.slice)) { + starting_index = 2; /* we have already generated the first two blocks */ + + /* Don't forget to generate the first block of addresses: */ + if (data_independent_addressing) { + next_addresses(&address_block, &input_block); + } + } + + /* Offset of the current block */ + curr_offset = position.lane * instance->lane_length + + position.slice * instance->segment_length + starting_index; + + if (0 == curr_offset % instance->lane_length) { + /* Last block in this lane */ + prev_offset = curr_offset + instance->lane_length - 1; + } else { + /* Previous block */ + prev_offset = curr_offset - 1; + } + + memcpy(state, ((instance->memory + prev_offset)->v), ARGON2_BLOCK_SIZE); + + for (i = starting_index; i < instance->segment_length; + ++i, ++curr_offset, ++prev_offset) { + /*1.1 Rotating prev_offset if needed */ + if (curr_offset % instance->lane_length == 1) { + prev_offset = curr_offset - 1; + } + + /* 1.2 Computing the index of the reference block */ + /* 1.2.1 Taking pseudo-random value from the previous block */ + if (data_independent_addressing) { + if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) { + next_addresses(&address_block, &input_block); + } + pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK]; + } else { + pseudo_rand = instance->memory[prev_offset].v[0]; + } + + /* 1.2.2 Computing the lane of the reference block */ + ref_lane = ((pseudo_rand >> 32)) % instance->lanes; + + if ((position.pass == 0) && (position.slice == 0)) { + /* Can not reference other lanes yet */ + ref_lane = position.lane; + } + + /* 1.2.3 Computing the number of possible reference block within the + * lane. + */ + position.index = i; + ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF, + ref_lane == position.lane); + + /* 2 Creating a new block */ + ref_block = + instance->memory + instance->lane_length * ref_lane + ref_index; + curr_block = instance->memory + curr_offset; + if (ARGON2_VERSION_10 == instance->version) { + /* version 1.2.1 and earlier: overwrite, not XOR */ + fill_block(state, ref_block, curr_block, 0); + } else { + if(0 == position.pass) { + fill_block(state, ref_block, curr_block, 0); + } else { + fill_block(state, ref_block, curr_block, 1); + } + } + } +} diff --git a/Blastproof/keygen/argon2/src/opt.o b/Blastproof/keygen/argon2/src/opt.o new file mode 100644 index 0000000..95b6e0d Binary files /dev/null and b/Blastproof/keygen/argon2/src/opt.o differ diff --git a/Blastproof/keygen/argon2/src/ref.c b/Blastproof/keygen/argon2/src/ref.c new file mode 100644 index 0000000..10e45eb --- /dev/null +++ b/Blastproof/keygen/argon2/src/ref.c @@ -0,0 +1,194 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include + +#include "argon2.h" +#include "core.h" + +#include "blake2/blamka-round-ref.h" +#include "blake2/blake2-impl.h" +#include "blake2/blake2.h" + + +/* + * Function fills a new memory block and optionally XORs the old block over the new one. + * @next_block must be initialized. + * @param prev_block Pointer to the previous block + * @param ref_block Pointer to the reference block + * @param next_block Pointer to the block to be constructed + * @param with_xor Whether to XOR into the new block (1) or just overwrite (0) + * @pre all block pointers must be valid + */ +static void fill_block(const block *prev_block, const block *ref_block, + block *next_block, int with_xor) { + block blockR, block_tmp; + unsigned i; + + copy_block(&blockR, ref_block); + xor_block(&blockR, prev_block); + copy_block(&block_tmp, &blockR); + /* Now blockR = ref_block + prev_block and block_tmp = ref_block + prev_block */ + if (with_xor) { + /* Saving the next block contents for XOR over: */ + xor_block(&block_tmp, next_block); + /* Now blockR = ref_block + prev_block and + block_tmp = ref_block + prev_block + next_block */ + } + + /* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then + (16,17,..31)... finally (112,113,...127) */ + for (i = 0; i < 8; ++i) { + BLAKE2_ROUND_NOMSG( + blockR.v[16 * i], blockR.v[16 * i + 1], blockR.v[16 * i + 2], + blockR.v[16 * i + 3], blockR.v[16 * i + 4], blockR.v[16 * i + 5], + blockR.v[16 * i + 6], blockR.v[16 * i + 7], blockR.v[16 * i + 8], + blockR.v[16 * i + 9], blockR.v[16 * i + 10], blockR.v[16 * i + 11], + blockR.v[16 * i + 12], blockR.v[16 * i + 13], blockR.v[16 * i + 14], + blockR.v[16 * i + 15]); + } + + /* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then + (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */ + for (i = 0; i < 8; i++) { + BLAKE2_ROUND_NOMSG( + blockR.v[2 * i], blockR.v[2 * i + 1], blockR.v[2 * i + 16], + blockR.v[2 * i + 17], blockR.v[2 * i + 32], blockR.v[2 * i + 33], + blockR.v[2 * i + 48], blockR.v[2 * i + 49], blockR.v[2 * i + 64], + blockR.v[2 * i + 65], blockR.v[2 * i + 80], blockR.v[2 * i + 81], + blockR.v[2 * i + 96], blockR.v[2 * i + 97], blockR.v[2 * i + 112], + blockR.v[2 * i + 113]); + } + + copy_block(next_block, &block_tmp); + xor_block(next_block, &blockR); +} + +static void next_addresses(block *address_block, block *input_block, + const block *zero_block) { + input_block->v[6]++; + fill_block(zero_block, input_block, address_block, 0); + fill_block(zero_block, address_block, address_block, 0); +} + +void fill_segment(const argon2_instance_t *instance, + argon2_position_t position) { + block *ref_block = NULL, *curr_block = NULL; + block address_block, input_block, zero_block; + uint64_t pseudo_rand, ref_index, ref_lane; + uint32_t prev_offset, curr_offset; + uint32_t starting_index; + uint32_t i; + int data_independent_addressing; + + if (instance == NULL) { + return; + } + + data_independent_addressing = + (instance->type == Argon2_i) || + (instance->type == Argon2_id && (position.pass == 0) && + (position.slice < ARGON2_SYNC_POINTS / 2)); + + if (data_independent_addressing) { + init_block_value(&zero_block, 0); + init_block_value(&input_block, 0); + + input_block.v[0] = position.pass; + input_block.v[1] = position.lane; + input_block.v[2] = position.slice; + input_block.v[3] = instance->memory_blocks; + input_block.v[4] = instance->passes; + input_block.v[5] = instance->type; + } + + starting_index = 0; + + if ((0 == position.pass) && (0 == position.slice)) { + starting_index = 2; /* we have already generated the first two blocks */ + + /* Don't forget to generate the first block of addresses: */ + if (data_independent_addressing) { + next_addresses(&address_block, &input_block, &zero_block); + } + } + + /* Offset of the current block */ + curr_offset = position.lane * instance->lane_length + + position.slice * instance->segment_length + starting_index; + + if (0 == curr_offset % instance->lane_length) { + /* Last block in this lane */ + prev_offset = curr_offset + instance->lane_length - 1; + } else { + /* Previous block */ + prev_offset = curr_offset - 1; + } + + for (i = starting_index; i < instance->segment_length; + ++i, ++curr_offset, ++prev_offset) { + /*1.1 Rotating prev_offset if needed */ + if (curr_offset % instance->lane_length == 1) { + prev_offset = curr_offset - 1; + } + + /* 1.2 Computing the index of the reference block */ + /* 1.2.1 Taking pseudo-random value from the previous block */ + if (data_independent_addressing) { + if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) { + next_addresses(&address_block, &input_block, &zero_block); + } + pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK]; + } else { + pseudo_rand = instance->memory[prev_offset].v[0]; + } + + /* 1.2.2 Computing the lane of the reference block */ + ref_lane = ((pseudo_rand >> 32)) % instance->lanes; + + if ((position.pass == 0) && (position.slice == 0)) { + /* Can not reference other lanes yet */ + ref_lane = position.lane; + } + + /* 1.2.3 Computing the number of possible reference block within the + * lane. + */ + position.index = i; + ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF, + ref_lane == position.lane); + + /* 2 Creating a new block */ + ref_block = + instance->memory + instance->lane_length * ref_lane + ref_index; + curr_block = instance->memory + curr_offset; + if (ARGON2_VERSION_10 == instance->version) { + /* version 1.2.1 and earlier: overwrite, not XOR */ + fill_block(instance->memory + prev_offset, ref_block, curr_block, 0); + } else { + if(0 == position.pass) { + fill_block(instance->memory + prev_offset, ref_block, + curr_block, 0); + } else { + fill_block(instance->memory + prev_offset, ref_block, + curr_block, 1); + } + } + } +} diff --git a/Blastproof/keygen/argon2/src/run.c b/Blastproof/keygen/argon2/src/run.c new file mode 100644 index 0000000..702b618 --- /dev/null +++ b/Blastproof/keygen/argon2/src/run.c @@ -0,0 +1,337 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include + +#include "argon2.h" +#include "core.h" + +#define T_COST_DEF 3 +#define LOG_M_COST_DEF 12 /* 2^12 = 4 MiB */ +#define LANES_DEF 1 +#define THREADS_DEF 1 +#define OUTLEN_DEF 32 +#define MAX_PASS_LEN 128 + +#define UNUSED_PARAMETER(x) (void)(x) + +static void usage(const char *cmd) { + printf("Usage: %s [-h] salt [-i|-d|-id] [-t iterations] " + "[-m log2(memory in KiB) | -k memory in KiB] [-p parallelism] " + "[-l hash length] [-e|-r] [-v (10|13)]\n", + cmd); + printf("\tPassword is read from stdin\n"); + printf("Parameters:\n"); + printf("\tsalt\t\tThe salt to use, at least 8 characters\n"); + printf("\t-i\t\tUse Argon2i (this is the default)\n"); + printf("\t-d\t\tUse Argon2d instead of Argon2i\n"); + printf("\t-id\t\tUse Argon2id instead of Argon2i\n"); + printf("\t-t N\t\tSets the number of iterations to N (default = %d)\n", + T_COST_DEF); + printf("\t-m N\t\tSets the memory usage of 2^N KiB (default %d)\n", + LOG_M_COST_DEF); + printf("\t-k N\t\tSets the memory usage of N KiB (default %d)\n", + 1 << LOG_M_COST_DEF); + printf("\t-p N\t\tSets parallelism to N threads (default %d)\n", + THREADS_DEF); + printf("\t-l N\t\tSets hash output length to N bytes (default %d)\n", + OUTLEN_DEF); + printf("\t-e\t\tOutput only encoded hash\n"); + printf("\t-r\t\tOutput only the raw bytes of the hash\n"); + printf("\t-v (10|13)\tArgon2 version (defaults to the most recent version, currently %x)\n", + ARGON2_VERSION_NUMBER); + printf("\t-h\t\tPrint %s usage\n", cmd); +} + +static void fatal(const char *error) { + fprintf(stderr, "Error: %s\n", error); + exit(1); +} + +static void print_hex(uint8_t *bytes, size_t bytes_len) { + size_t i; + for (i = 0; i < bytes_len; ++i) { + printf("%02x", bytes[i]); + } + printf("\n"); +} + +/* +Runs Argon2 with certain inputs and parameters, inputs not cleared. Prints the +Base64-encoded hash string +@out output array with at least 32 bytes allocated +@pwd NULL-terminated string, presumably from argv[] +@salt salt array +@t_cost number of iterations +@m_cost amount of requested memory in KB +@lanes amount of requested parallelism +@threads actual parallelism +@type Argon2 type we want to run +@encoded_only display only the encoded hash +@raw_only display only the hexadecimal of the hash +@version Argon2 version +*/ +static void run(uint32_t outlen, char *pwd, size_t pwdlen, char *salt, uint32_t t_cost, + uint32_t m_cost, uint32_t lanes, uint32_t threads, + argon2_type type, int encoded_only, int raw_only, uint32_t version) { + clock_t start_time, stop_time; + size_t saltlen, encodedlen; + int result; + unsigned char * out = NULL; + char * encoded = NULL; + + start_time = clock(); + + if (!pwd) { + fatal("password missing"); + } + + if (!salt) { + clear_internal_memory(pwd, pwdlen); + fatal("salt missing"); + } + + saltlen = strlen(salt); + if(UINT32_MAX < saltlen) { + fatal("salt is too long"); + } + + UNUSED_PARAMETER(lanes); + + out = malloc(outlen + 1); + if (!out) { + clear_internal_memory(pwd, pwdlen); + fatal("could not allocate memory for output"); + } + + encodedlen = argon2_encodedlen(t_cost, m_cost, lanes, (uint32_t)saltlen, outlen, type); + encoded = malloc(encodedlen + 1); + if (!encoded) { + clear_internal_memory(pwd, pwdlen); + fatal("could not allocate memory for hash"); + } + + result = argon2_hash(t_cost, m_cost, threads, pwd, pwdlen, salt, saltlen, + out, outlen, encoded, encodedlen, type, + version); + if (result != ARGON2_OK) + fatal(argon2_error_message(result)); + + stop_time = clock(); + + if (encoded_only) + puts(encoded); + + if (raw_only) + print_hex(out, outlen); + + if (encoded_only || raw_only) { + free(out); + free(encoded); + return; + } + + printf("Hash:\t\t"); + print_hex(out, outlen); + free(out); + + printf("Encoded:\t%s\n", encoded); + + printf("%2.3f seconds\n", + ((double)stop_time - start_time) / (CLOCKS_PER_SEC)); + + result = argon2_verify(encoded, pwd, pwdlen, type); + if (result != ARGON2_OK) + fatal(argon2_error_message(result)); + printf("Verification ok\n"); + free(encoded); +} + +int main(int argc, char *argv[]) { + uint32_t outlen = OUTLEN_DEF; + uint32_t m_cost = 1 << LOG_M_COST_DEF; + uint32_t t_cost = T_COST_DEF; + uint32_t lanes = LANES_DEF; + uint32_t threads = THREADS_DEF; + argon2_type type = Argon2_i; /* Argon2i is the default type */ + int types_specified = 0; + int m_cost_specified = 0; + int encoded_only = 0; + int raw_only = 0; + uint32_t version = ARGON2_VERSION_NUMBER; + int i; + size_t pwdlen; + char pwd[MAX_PASS_LEN], *salt; + + if (argc < 2) { + usage(argv[0]); + return ARGON2_MISSING_ARGS; + } else if (argc >= 2 && strcmp(argv[1], "-h") == 0) { + usage(argv[0]); + return 1; + } + + /* get password from stdin */ + pwdlen = fread(pwd, 1, sizeof pwd, stdin); + if(pwdlen < 1) { + fatal("no password read"); + } + if(pwdlen == MAX_PASS_LEN) { + fatal("Provided password longer than supported in command line utility"); + } + + salt = argv[1]; + + /* parse options */ + for (i = 2; i < argc; i++) { + const char *a = argv[i]; + unsigned long input = 0; + if (!strcmp(a, "-h")) { + usage(argv[0]); + return 1; + } else if (!strcmp(a, "-m")) { + if (m_cost_specified) { + fatal("-m or -k can only be used once"); + } + m_cost_specified = 1; + if (i < argc - 1) { + i++; + input = strtoul(argv[i], NULL, 10); + if (input == 0 || input == ULONG_MAX || + input > ARGON2_MAX_MEMORY_BITS) { + fatal("bad numeric input for -m"); + } + m_cost = ARGON2_MIN(UINT64_C(1) << input, UINT32_C(0xFFFFFFFF)); + if (m_cost > ARGON2_MAX_MEMORY) { + fatal("m_cost overflow"); + } + continue; + } else { + fatal("missing -m argument"); + } + } else if (!strcmp(a, "-k")) { + if (m_cost_specified) { + fatal("-m or -k can only be used once"); + } + m_cost_specified = 1; + if (i < argc - 1) { + i++; + input = strtoul(argv[i], NULL, 10); + if (input == 0 || input == ULONG_MAX) { + fatal("bad numeric input for -k"); + } + m_cost = ARGON2_MIN(input, UINT32_C(0xFFFFFFFF)); + if (m_cost > ARGON2_MAX_MEMORY) { + fatal("m_cost overflow"); + } + continue; + } else { + fatal("missing -k argument"); + } + } else if (!strcmp(a, "-t")) { + if (i < argc - 1) { + i++; + input = strtoul(argv[i], NULL, 10); + if (input == 0 || input == ULONG_MAX || + input > ARGON2_MAX_TIME) { + fatal("bad numeric input for -t"); + } + t_cost = input; + continue; + } else { + fatal("missing -t argument"); + } + } else if (!strcmp(a, "-p")) { + if (i < argc - 1) { + i++; + input = strtoul(argv[i], NULL, 10); + if (input == 0 || input == ULONG_MAX || + input > ARGON2_MAX_THREADS || input > ARGON2_MAX_LANES) { + fatal("bad numeric input for -p"); + } + threads = input; + lanes = threads; + continue; + } else { + fatal("missing -p argument"); + } + } else if (!strcmp(a, "-l")) { + if (i < argc - 1) { + i++; + input = strtoul(argv[i], NULL, 10); + outlen = input; + continue; + } else { + fatal("missing -l argument"); + } + } else if (!strcmp(a, "-i")) { + type = Argon2_i; + ++types_specified; + } else if (!strcmp(a, "-d")) { + type = Argon2_d; + ++types_specified; + } else if (!strcmp(a, "-id")) { + type = Argon2_id; + ++types_specified; + } else if (!strcmp(a, "-e")) { + encoded_only = 1; + } else if (!strcmp(a, "-r")) { + raw_only = 1; + } else if (!strcmp(a, "-v")) { + if (i < argc - 1) { + i++; + if (!strcmp(argv[i], "10")) { + version = ARGON2_VERSION_10; + } else if (!strcmp(argv[i], "13")) { + version = ARGON2_VERSION_13; + } else { + fatal("invalid Argon2 version"); + } + } else { + fatal("missing -v argument"); + } + } else { + fatal("unknown argument"); + } + } + + if (types_specified > 1) { + fatal("cannot specify multiple Argon2 types"); + } + + if(encoded_only && raw_only) + fatal("cannot provide both -e and -r"); + + if(!encoded_only && !raw_only) { + printf("Type:\t\t%s\n", argon2_type2string(type, 1)); + printf("Iterations:\t%u\n", t_cost); + printf("Memory:\t\t%u KiB\n", m_cost); + printf("Parallelism:\t%u\n", lanes); + } + + run(outlen, pwd, pwdlen, salt, t_cost, m_cost, lanes, threads, type, + encoded_only, raw_only, version); + + return ARGON2_OK; +} + diff --git a/Blastproof/keygen/argon2/src/test.c b/Blastproof/keygen/argon2/src/test.c new file mode 100644 index 0000000..055c19a --- /dev/null +++ b/Blastproof/keygen/argon2/src/test.c @@ -0,0 +1,289 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include +#include +#include +#include + +#include "argon2.h" + +#define OUT_LEN 32 +#define ENCODED_LEN 108 + +/* Test harness will assert: + * argon2_hash() returns ARGON2_OK + * HEX output matches expected + * encoded output matches expected + * argon2_verify() correctly verifies value + */ + +void hashtest(uint32_t version, uint32_t t, uint32_t m, uint32_t p, char *pwd, + char *salt, char *hexref, char *mcfref, argon2_type type) { + unsigned char out[OUT_LEN]; + unsigned char hex_out[OUT_LEN * 2 + 4]; + char encoded[ENCODED_LEN]; + int ret, i; + + printf("Hash test: $v=%d t=%d, m=%d, p=%d, pass=%s, salt=%s: ", version, + t, m, p, pwd, salt); + + ret = argon2_hash(t, 1 << m, p, pwd, strlen(pwd), salt, strlen(salt), out, + OUT_LEN, encoded, ENCODED_LEN, type, version); + assert(ret == ARGON2_OK); + + for (i = 0; i < OUT_LEN; ++i) + sprintf((char *)(hex_out + i * 2), "%02x", out[i]); + assert(memcmp(hex_out, hexref, OUT_LEN * 2) == 0); + + if (ARGON2_VERSION_NUMBER == version) { + assert(memcmp(encoded, mcfref, strlen(mcfref)) == 0); + } + + ret = argon2_verify(encoded, pwd, strlen(pwd), type); + assert(ret == ARGON2_OK); + ret = argon2_verify(mcfref, pwd, strlen(pwd), type); + assert(ret == ARGON2_OK); + + printf("PASS\n"); +} + +int main() { + int ret; + unsigned char out[OUT_LEN]; + char const *msg; + int version; + + version = ARGON2_VERSION_10; + printf("Test Argon2i version number: %02x\n", version); + + /* Multiple test cases for various input values */ + hashtest(version, 2, 16, 1, "password", "somesalt", + "f6c4db4a54e2a370627aff3db6176b94a2a209a62c8e36152711802f7b30c694", + "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ" + "$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ", Argon2_i); +#ifdef TEST_LARGE_RAM + hashtest(version, 2, 20, 1, "password", "somesalt", + "9690ec55d28d3ed32562f2e73ea62b02b018757643a2ae6e79528459de8106e9", + "$argon2i$m=1048576,t=2,p=1$c29tZXNhbHQ" + "$lpDsVdKNPtMlYvLnPqYrArAYdXZDoq5ueVKEWd6BBuk", Argon2_i); +#endif + hashtest(version, 2, 18, 1, "password", "somesalt", + "3e689aaa3d28a77cf2bc72a51ac53166761751182f1ee292e3f677a7da4c2467", + "$argon2i$m=262144,t=2,p=1$c29tZXNhbHQ" + "$Pmiaqj0op3zyvHKlGsUxZnYXURgvHuKS4/Z3p9pMJGc", Argon2_i); + hashtest(version, 2, 8, 1, "password", "somesalt", + "fd4dd83d762c49bdeaf57c47bdcd0c2f1babf863fdeb490df63ede9975fccf06", + "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ" + "$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY", Argon2_i); + hashtest(version, 2, 8, 2, "password", "somesalt", + "b6c11560a6a9d61eac706b79a2f97d68b4463aa3ad87e00c07e2b01e90c564fb", + "$argon2i$m=256,t=2,p=2$c29tZXNhbHQ" + "$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs", Argon2_i); + hashtest(version, 1, 16, 1, "password", "somesalt", + "81630552b8f3b1f48cdb1992c4c678643d490b2b5eb4ff6c4b3438b5621724b2", + "$argon2i$m=65536,t=1,p=1$c29tZXNhbHQ" + "$gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI", Argon2_i); + hashtest(version, 4, 16, 1, "password", "somesalt", + "f212f01615e6eb5d74734dc3ef40ade2d51d052468d8c69440a3a1f2c1c2847b", + "$argon2i$m=65536,t=4,p=1$c29tZXNhbHQ" + "$8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs", Argon2_i); + hashtest(version, 2, 16, 1, "differentpassword", "somesalt", + "e9c902074b6754531a3a0be519e5baf404b30ce69b3f01ac3bf21229960109a3", + "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ" + "$6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM", Argon2_i); + hashtest(version, 2, 16, 1, "password", "diffsalt", + "79a103b90fe8aef8570cb31fc8b22259778916f8336b7bdac3892569d4f1c497", + "$argon2i$m=65536,t=2,p=1$ZGlmZnNhbHQ" + "$eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc", Argon2_i); + + /* Error state tests */ + + /* Handle an invalid encoding correctly (it is missing a $) */ + ret = argon2_verify("$argon2i$m=65536,t=2,p=1c29tZXNhbHQ" + "$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ", + "password", strlen("password"), Argon2_i); + assert(ret == ARGON2_DECODING_FAIL); + printf("Recognise an invalid encoding: PASS\n"); + + /* Handle an invalid encoding correctly (it is missing a $) */ + ret = argon2_verify("$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ" + "9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ", + "password", strlen("password"), Argon2_i); + assert(ret == ARGON2_DECODING_FAIL); + printf("Recognise an invalid encoding: PASS\n"); + + /* Handle an invalid encoding correctly (salt is too short) */ + ret = argon2_verify("$argon2i$m=65536,t=2,p=1$" + "$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ", + "password", strlen("password"), Argon2_i); + assert(ret == ARGON2_SALT_TOO_SHORT); + printf("Recognise an invalid salt in encoding: PASS\n"); + + /* Handle an mismatching hash (the encoded password is "passwore") */ + ret = argon2_verify("$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ" + "$b2G3seW+uPzerwQQC+/E1K50CLLO7YXy0JRcaTuswRo", + "password", strlen("password"), Argon2_i); + assert(ret == ARGON2_VERIFY_MISMATCH); + printf("Verify with mismatched password: PASS\n"); + + msg = argon2_error_message(ARGON2_DECODING_FAIL); + assert(strcmp(msg, "Decoding failed") == 0); + printf("Decode an error message: PASS\n"); + + printf("\n"); + + version = ARGON2_VERSION_NUMBER; + printf("Test Argon2i version number: %02x\n", version); + + /* Multiple test cases for various input values */ + hashtest(version, 2, 16, 1, "password", "somesalt", + "c1628832147d9720c5bd1cfd61367078729f6dfb6f8fea9ff98158e0d7816ed0", + "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" + "$wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA", Argon2_i); +#ifdef TEST_LARGE_RAM + hashtest(version, 2, 20, 1, "password", "somesalt", + "d1587aca0922c3b5d6a83edab31bee3c4ebaef342ed6127a55d19b2351ad1f41", + "$argon2i$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ" + "$0Vh6ygkiw7XWqD7asxvuPE667zQu1hJ6VdGbI1GtH0E", Argon2_i); +#endif + hashtest(version, 2, 18, 1, "password", "somesalt", + "296dbae80b807cdceaad44ae741b506f14db0959267b183b118f9b24229bc7cb", + "$argon2i$v=19$m=262144,t=2,p=1$c29tZXNhbHQ" + "$KW266AuAfNzqrUSudBtQbxTbCVkmexg7EY+bJCKbx8s", Argon2_i); + hashtest(version, 2, 8, 1, "password", "somesalt", + "89e9029f4637b295beb027056a7336c414fadd43f6b208645281cb214a56452f", + "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ" + "$iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8", Argon2_i); + hashtest(version, 2, 8, 2, "password", "somesalt", + "4ff5ce2769a1d7f4c8a491df09d41a9fbe90e5eb02155a13e4c01e20cd4eab61", + "$argon2i$v=19$m=256,t=2,p=2$c29tZXNhbHQ" + "$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E", Argon2_i); + hashtest(version, 1, 16, 1, "password", "somesalt", + "d168075c4d985e13ebeae560cf8b94c3b5d8a16c51916b6f4ac2da3ac11bbecf", + "$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQ" + "$0WgHXE2YXhPr6uVgz4uUw7XYoWxRkWtvSsLaOsEbvs8", Argon2_i); + hashtest(version, 4, 16, 1, "password", "somesalt", + "aaa953d58af3706ce3df1aefd4a64a84e31d7f54175231f1285259f88174ce5b", + "$argon2i$v=19$m=65536,t=4,p=1$c29tZXNhbHQ" + "$qqlT1YrzcGzj3xrv1KZKhOMdf1QXUjHxKFJZ+IF0zls", Argon2_i); + hashtest(version, 2, 16, 1, "differentpassword", "somesalt", + "14ae8da01afea8700c2358dcef7c5358d9021282bd88663a4562f59fb74d22ee", + "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" + "$FK6NoBr+qHAMI1jc73xTWNkCEoK9iGY6RWL1n7dNIu4", Argon2_i); + hashtest(version, 2, 16, 1, "password", "diffsalt", + "b0357cccfbef91f3860b0dba447b2348cbefecadaf990abfe9cc40726c521271", + "$argon2i$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ" + "$sDV8zPvvkfOGCw26RHsjSMvv7K2vmQq/6cxAcmxSEnE", Argon2_i); + + + /* Error state tests */ + + /* Handle an invalid encoding correctly (it is missing a $) */ + ret = argon2_verify("$argon2i$v=19$m=65536,t=2,p=1c29tZXNhbHQ" + "$wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA", + "password", strlen("password"), Argon2_i); + assert(ret == ARGON2_DECODING_FAIL); + printf("Recognise an invalid encoding: PASS\n"); + + /* Handle an invalid encoding correctly (it is missing a $) */ + ret = argon2_verify("$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" + "wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA", + "password", strlen("password"), Argon2_i); + assert(ret == ARGON2_DECODING_FAIL); + printf("Recognise an invalid encoding: PASS\n"); + + /* Handle an invalid encoding correctly (salt is too short) */ + ret = argon2_verify("$argon2i$v=19$m=65536,t=2,p=1$" + "$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ", + "password", strlen("password"), Argon2_i); + assert(ret == ARGON2_SALT_TOO_SHORT); + printf("Recognise an invalid salt in encoding: PASS\n"); + + /* Handle an mismatching hash (the encoded password is "passwore") */ + ret = argon2_verify("$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" + "$8iIuixkI73Js3G1uMbezQXD0b8LG4SXGsOwoQkdAQIM", + "password", strlen("password"), Argon2_i); + assert(ret == ARGON2_VERIFY_MISMATCH); + printf("Verify with mismatched password: PASS\n"); + + msg = argon2_error_message(ARGON2_DECODING_FAIL); + assert(strcmp(msg, "Decoding failed") == 0); + printf("Decode an error message: PASS\n\n"); + + printf("Test Argon2id version number: %02x\n", version); + + /* Multiple test cases for various input values */ + hashtest(version, 2, 16, 1, "password", "somesalt", + "09316115d5cf24ed5a15a31a3ba326e5cf32edc24702987c02b6566f61913cf7", + "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" + "$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc", Argon2_id); + hashtest(version, 2, 18, 1, "password", "somesalt", + "78fe1ec91fb3aa5657d72e710854e4c3d9b9198c742f9616c2f085bed95b2e8c", + "$argon2id$v=19$m=262144,t=2,p=1$c29tZXNhbHQ" + "$eP4eyR+zqlZX1y5xCFTkw9m5GYx0L5YWwvCFvtlbLow", Argon2_id); + hashtest(version, 2, 8, 1, "password", "somesalt", + "9dfeb910e80bad0311fee20f9c0e2b12c17987b4cac90c2ef54d5b3021c68bfe", + "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ" + "$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4", Argon2_id); + hashtest(version, 2, 8, 2, "password", "somesalt", + "6d093c501fd5999645e0ea3bf620d7b8be7fd2db59c20d9fff9539da2bf57037", + "$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ" + "$bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc", Argon2_id); + hashtest(version, 1, 16, 1, "password", "somesalt", + "f6a5adc1ba723dddef9b5ac1d464e180fcd9dffc9d1cbf76cca2fed795d9ca98", + "$argon2id$v=19$m=65536,t=1,p=1$c29tZXNhbHQ" + "$9qWtwbpyPd3vm1rB1GThgPzZ3/ydHL92zKL+15XZypg", Argon2_id); + hashtest(version, 4, 16, 1, "password", "somesalt", + "9025d48e68ef7395cca9079da4c4ec3affb3c8911fe4f86d1a2520856f63172c", + "$argon2id$v=19$m=65536,t=4,p=1$c29tZXNhbHQ" + "$kCXUjmjvc5XMqQedpMTsOv+zyJEf5PhtGiUghW9jFyw", Argon2_id); + hashtest(version, 2, 16, 1, "differentpassword", "somesalt", + "0b84d652cf6b0c4beaef0dfe278ba6a80df6696281d7e0d2891b817d8c458fde", + "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" + "$C4TWUs9rDEvq7w3+J4umqA32aWKB1+DSiRuBfYxFj94", Argon2_id); + hashtest(version, 2, 16, 1, "password", "diffsalt", + "bdf32b05ccc42eb15d58fd19b1f856b113da1e9a5874fdcc544308565aa8141c", + "$argon2id$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ" + "$vfMrBczELrFdWP0ZsfhWsRPaHppYdP3MVEMIVlqoFBw", Argon2_id); + + /* Common error state tests */ + + printf("\n"); + printf("Common error state tests\n"); + + ret = argon2_hash(2, 1, 1, "password", strlen("password"), + "diffsalt", strlen("diffsalt"), + out, OUT_LEN, NULL, 0, Argon2_id, version); + assert(ret == ARGON2_MEMORY_TOO_LITTLE); + printf("Fail on invalid memory: PASS\n"); + + ret = argon2_hash(2, 1 << 12, 1, NULL, strlen("password"), + "diffsalt", strlen("diffsalt"), + out, OUT_LEN, NULL, 0, Argon2_id, version); + assert(ret == ARGON2_PWD_PTR_MISMATCH); + printf("Fail on invalid null pointer: PASS\n"); + + ret = argon2_hash(2, 1 << 12, 1, "password", strlen("password"), "s", 1, + out, OUT_LEN, NULL, 0, Argon2_id, version); + assert(ret == ARGON2_SALT_TOO_SHORT); + printf("Fail on salt too short: PASS\n"); + + return 0; +} diff --git a/Blastproof/keygen/argon2/src/thread.c b/Blastproof/keygen/argon2/src/thread.c new file mode 100644 index 0000000..3ae2fb2 --- /dev/null +++ b/Blastproof/keygen/argon2/src/thread.c @@ -0,0 +1,57 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#if !defined(ARGON2_NO_THREADS) + +#include "thread.h" +#if defined(_WIN32) +#include +#endif + +int argon2_thread_create(argon2_thread_handle_t *handle, + argon2_thread_func_t func, void *args) { + if (NULL == handle || func == NULL) { + return -1; + } +#if defined(_WIN32) + *handle = _beginthreadex(NULL, 0, func, args, 0, NULL); + return *handle != 0 ? 0 : -1; +#else + return pthread_create(handle, NULL, func, args); +#endif +} + +int argon2_thread_join(argon2_thread_handle_t handle) { +#if defined(_WIN32) + if (WaitForSingleObject((HANDLE)handle, INFINITE) == WAIT_OBJECT_0) { + return CloseHandle((HANDLE)handle) != 0 ? 0 : -1; + } + return -1; +#else + return pthread_join(handle, NULL); +#endif +} + +void argon2_thread_exit(void) { +#if defined(_WIN32) + _endthreadex(0); +#else + pthread_exit(NULL); +#endif +} + +#endif /* ARGON2_NO_THREADS */ diff --git a/Blastproof/keygen/argon2/src/thread.h b/Blastproof/keygen/argon2/src/thread.h new file mode 100644 index 0000000..d4ca10c --- /dev/null +++ b/Blastproof/keygen/argon2/src/thread.h @@ -0,0 +1,67 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_THREAD_H +#define ARGON2_THREAD_H + +#if !defined(ARGON2_NO_THREADS) + +/* + Here we implement an abstraction layer for the simpĺe requirements + of the Argon2 code. We only require 3 primitives---thread creation, + joining, and termination---so full emulation of the pthreads API + is unwarranted. Currently we wrap pthreads and Win32 threads. + + The API defines 2 types: the function pointer type, + argon2_thread_func_t, + and the type of the thread handle---argon2_thread_handle_t. +*/ +#if defined(_WIN32) +#include +typedef unsigned(__stdcall *argon2_thread_func_t)(void *); +typedef uintptr_t argon2_thread_handle_t; +#else +#include +typedef void *(*argon2_thread_func_t)(void *); +typedef pthread_t argon2_thread_handle_t; +#endif + +/* Creates a thread + * @param handle pointer to a thread handle, which is the output of this + * function. Must not be NULL. + * @param func A function pointer for the thread's entry point. Must not be + * NULL. + * @param args Pointer that is passed as an argument to @func. May be NULL. + * @return 0 if @handle and @func are valid pointers and a thread is successfully + * created. + */ +int argon2_thread_create(argon2_thread_handle_t *handle, + argon2_thread_func_t func, void *args); + +/* Waits for a thread to terminate + * @param handle Handle to a thread created with argon2_thread_create. + * @return 0 if @handle is a valid handle, and joining completed successfully. +*/ +int argon2_thread_join(argon2_thread_handle_t handle); + +/* Terminate the current thread. Must be run inside a thread created by + * argon2_thread_create. +*/ +void argon2_thread_exit(void); + +#endif /* ARGON2_NO_THREADS */ +#endif diff --git a/Blastproof/keygen/argon2/src/thread.o b/Blastproof/keygen/argon2/src/thread.o new file mode 100644 index 0000000..0146928 Binary files /dev/null and b/Blastproof/keygen/argon2/src/thread.o differ diff --git a/Blastproof/keygen/argon2/vs2015/Argon2Opt/Argon2Opt.vcxproj b/Blastproof/keygen/argon2/vs2015/Argon2Opt/Argon2Opt.vcxproj new file mode 100644 index 0000000..357b4bd --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2Opt/Argon2Opt.vcxproj @@ -0,0 +1,231 @@ + + + + + Debug + Win32 + + + ReleaseStatic + Win32 + + + ReleaseStatic + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CAA75C57-998C-494E-B8A5-5894EF0FC528} + Argon2Opt + 8.1 + + + + + v100 + + + $(DefaultPlatformToolset) + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Blastproof/keygen/argon2/vs2015/Argon2Opt/Argon2Opt.vcxproj.filters b/Blastproof/keygen/argon2/vs2015/Argon2Opt/Argon2Opt.vcxproj.filters new file mode 100644 index 0000000..536602b --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2Opt/Argon2Opt.vcxproj.filters @@ -0,0 +1,69 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/Blastproof/keygen/argon2/vs2015/Argon2OptBench/Argon2OptBench.vcxproj b/Blastproof/keygen/argon2/vs2015/Argon2OptBench/Argon2OptBench.vcxproj new file mode 100644 index 0000000..e5ba5b3 --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2OptBench/Argon2OptBench.vcxproj @@ -0,0 +1,231 @@ + + + + + Debug + Win32 + + + ReleaseStatic + Win32 + + + ReleaseStatic + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4} + Argon2OptBench + 8.1 + + + + + v100 + + + $(DefaultPlatformToolset) + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Blastproof/keygen/argon2/vs2015/Argon2OptBench/Argon2OptBench.vcxproj.filters b/Blastproof/keygen/argon2/vs2015/Argon2OptBench/Argon2OptBench.vcxproj.filters new file mode 100644 index 0000000..0cc0d95 --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2OptBench/Argon2OptBench.vcxproj.filters @@ -0,0 +1,69 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/Blastproof/keygen/argon2/vs2015/Argon2OptDll/Argon2OptDll.vcxproj b/Blastproof/keygen/argon2/vs2015/Argon2OptDll/Argon2OptDll.vcxproj new file mode 100644 index 0000000..71eb33d --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2OptDll/Argon2OptDll.vcxproj @@ -0,0 +1,230 @@ + + + + + Debug + Win32 + + + ReleaseStatic + Win32 + + + ReleaseStatic + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {3A898DD8-ACAE-4269-ADFE-EB7260D71583} + Argon2OptDll + 8.1 + + + + + v100 + + + $(DefaultPlatformToolset) + + + DynamicLibrary + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + DynamicLibrary + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Blastproof/keygen/argon2/vs2015/Argon2OptDll/Argon2OptDll.vcxproj.filters b/Blastproof/keygen/argon2/vs2015/Argon2OptDll/Argon2OptDll.vcxproj.filters new file mode 100644 index 0000000..c7a7788 --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2OptDll/Argon2OptDll.vcxproj.filters @@ -0,0 +1,66 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + diff --git a/Blastproof/keygen/argon2/vs2015/Argon2OptGenKAT/Argon2OptGenKAT.vcxproj b/Blastproof/keygen/argon2/vs2015/Argon2OptGenKAT/Argon2OptGenKAT.vcxproj new file mode 100644 index 0000000..44c97d1 --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2OptGenKAT/Argon2OptGenKAT.vcxproj @@ -0,0 +1,244 @@ + + + + + Debug + Win32 + + + ReleaseStatic + Win32 + + + ReleaseStatic + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {DBBAAAE6-4560-4D11-8280-30A6650A82EF} + Argon2OptGenKAT + 8.1 + + + + + v100 + + + $(DefaultPlatformToolset) + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + + + + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + + + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + + + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + + + MultiThreaded + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + + + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + + + MultiThreaded + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Blastproof/keygen/argon2/vs2015/Argon2OptGenKAT/Argon2OptGenKAT.vcxproj.filters b/Blastproof/keygen/argon2/vs2015/Argon2OptGenKAT/Argon2OptGenKAT.vcxproj.filters new file mode 100644 index 0000000..ff85955 --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2OptGenKAT/Argon2OptGenKAT.vcxproj.filters @@ -0,0 +1,72 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/Blastproof/keygen/argon2/vs2015/Argon2OptTestCI/Argon2OptTestCI.vcxproj b/Blastproof/keygen/argon2/vs2015/Argon2OptTestCI/Argon2OptTestCI.vcxproj new file mode 100644 index 0000000..43cab0a --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2OptTestCI/Argon2OptTestCI.vcxproj @@ -0,0 +1,235 @@ + + + + + Debug + Win32 + + + ReleaseStatic + Win32 + + + ReleaseStatic + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {12956597-5E42-433A-93F3-D4EFF50AA207} + Argon2 + 8.1 + Argon2OptTestCI + + + + + v100 + + + $(DefaultPlatformToolset) + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Blastproof/keygen/argon2/vs2015/Argon2OptTestCI/Argon2OptTestCI.vcxproj.filters b/Blastproof/keygen/argon2/vs2015/Argon2OptTestCI/Argon2OptTestCI.vcxproj.filters new file mode 100644 index 0000000..bcd4dab --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2OptTestCI/Argon2OptTestCI.vcxproj.filters @@ -0,0 +1,69 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/Blastproof/keygen/argon2/vs2015/Argon2Ref/Argon2Ref.vcxproj b/Blastproof/keygen/argon2/vs2015/Argon2Ref/Argon2Ref.vcxproj new file mode 100644 index 0000000..a25cd23 --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2Ref/Argon2Ref.vcxproj @@ -0,0 +1,243 @@ + + + + + Debug + Win32 + + + ReleaseStatic + Win32 + + + ReleaseStatic + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2} + Argon2Ref + 8.1 + + + + + v100 + + + $(DefaultPlatformToolset) + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ProgramDatabase + + + true + + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ProgramDatabase + + + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ProgramDatabase + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + ProgramDatabase + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ProgramDatabase + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + ProgramDatabase + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Blastproof/keygen/argon2/vs2015/Argon2Ref/Argon2Ref.vcxproj.filters b/Blastproof/keygen/argon2/vs2015/Argon2Ref/Argon2Ref.vcxproj.filters new file mode 100644 index 0000000..379cf2e --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2Ref/Argon2Ref.vcxproj.filters @@ -0,0 +1,69 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/Blastproof/keygen/argon2/vs2015/Argon2RefBench/Argon2RefBench.vcxproj b/Blastproof/keygen/argon2/vs2015/Argon2RefBench/Argon2RefBench.vcxproj new file mode 100644 index 0000000..0106eb7 --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2RefBench/Argon2RefBench.vcxproj @@ -0,0 +1,231 @@ + + + + + Debug + Win32 + + + ReleaseStatic + Win32 + + + ReleaseStatic + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {99203F6A-6E8C-42FC-8C7C-C07E8913D539} + Argon2RefBench + 8.1 + + + + + v100 + + + $(DefaultPlatformToolset) + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Blastproof/keygen/argon2/vs2015/Argon2RefBench/Argon2RefBench.vcxproj.filters b/Blastproof/keygen/argon2/vs2015/Argon2RefBench/Argon2RefBench.vcxproj.filters new file mode 100644 index 0000000..222279a --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2RefBench/Argon2RefBench.vcxproj.filters @@ -0,0 +1,69 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/Blastproof/keygen/argon2/vs2015/Argon2RefDll/Argon2RefDll.vcxproj b/Blastproof/keygen/argon2/vs2015/Argon2RefDll/Argon2RefDll.vcxproj new file mode 100644 index 0000000..623aaac --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2RefDll/Argon2RefDll.vcxproj @@ -0,0 +1,230 @@ + + + + + Debug + Win32 + + + ReleaseStatic + Win32 + + + ReleaseStatic + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {19D911A1-533C-4475-B313-F372481A35D4} + Argon2RefDll + 8.1 + + + + + v100 + + + $(DefaultPlatformToolset) + + + DynamicLibrary + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + DynamicLibrary + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + DynamicLibrary + false + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Blastproof/keygen/argon2/vs2015/Argon2RefDll/Argon2RefDll.vcxproj.filters b/Blastproof/keygen/argon2/vs2015/Argon2RefDll/Argon2RefDll.vcxproj.filters new file mode 100644 index 0000000..ad43f91 --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2RefDll/Argon2RefDll.vcxproj.filters @@ -0,0 +1,66 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + diff --git a/Blastproof/keygen/argon2/vs2015/Argon2RefGenKAT/Argon2RefGenKAT.vcxproj b/Blastproof/keygen/argon2/vs2015/Argon2RefGenKAT/Argon2RefGenKAT.vcxproj new file mode 100644 index 0000000..f591439 --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2RefGenKAT/Argon2RefGenKAT.vcxproj @@ -0,0 +1,232 @@ + + + + + Debug + Win32 + + + ReleaseStatic + Win32 + + + ReleaseStatic + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {71921B4C-A795-4A37-95A3-99D600E01211} + Argon2RefGenKAT + 8.1 + + + + + v100 + + + $(DefaultPlatformToolset) + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + $(SolutionDir)include;$(IncludePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;GENKAT;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Blastproof/keygen/argon2/vs2015/Argon2RefGenKAT/Argon2RefGenKAT.vcxproj.filters b/Blastproof/keygen/argon2/vs2015/Argon2RefGenKAT/Argon2RefGenKAT.vcxproj.filters new file mode 100644 index 0000000..8490c8a --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2RefGenKAT/Argon2RefGenKAT.vcxproj.filters @@ -0,0 +1,72 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/Blastproof/keygen/argon2/vs2015/Argon2RefTestCI/Argon2RefTestCI.vcxproj b/Blastproof/keygen/argon2/vs2015/Argon2RefTestCI/Argon2RefTestCI.vcxproj new file mode 100644 index 0000000..9e9fa08 --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2RefTestCI/Argon2RefTestCI.vcxproj @@ -0,0 +1,231 @@ + + + + + Debug + Win32 + + + ReleaseStatic + Win32 + + + ReleaseStatic + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9} + Argon2RefTestCI + 8.1 + + + + + v100 + + + $(DefaultPlatformToolset) + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + $(SolutionDir)include;$(IncludePath) + $(SolutionDir)vs2015\build\ + $(SolutionDir)vs2015\build\$(ProjectName)\ + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Blastproof/keygen/argon2/vs2015/Argon2RefTestCI/Argon2RefTestCI.vcxproj.filters b/Blastproof/keygen/argon2/vs2015/Argon2RefTestCI/Argon2RefTestCI.vcxproj.filters new file mode 100644 index 0000000..32bca82 --- /dev/null +++ b/Blastproof/keygen/argon2/vs2015/Argon2RefTestCI/Argon2RefTestCI.vcxproj.filters @@ -0,0 +1,69 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/Blastproof/keygen/build.sh b/Blastproof/keygen/build.sh new file mode 100755 index 0000000..10c3b4a --- /dev/null +++ b/Blastproof/keygen/build.sh @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MPL-2.0 +gcc -c address.c fors.c hash_sha2.c merkle.c randombytes.c sign.c utils.c utilsx1.c wots.c wotsx1.c thash_sha2_robust.c sha2.c sha3.c +g++ keygen.cpp address.o fors.o hash_sha2.o merkle.o randombytes.o sign.o utils.o utilsx1.o wots.o wotsx1.o thash_sha2_robust.o sha2.o sha3.o -o keygen -lcrypto -L. -largon2 diff --git a/Blastproof/keygen/context.h b/Blastproof/keygen/context.h new file mode 100644 index 0000000..aded564 --- /dev/null +++ b/Blastproof/keygen/context.h @@ -0,0 +1,28 @@ +#ifndef SPX_CONTEXT_H +#define SPX_CONTEXT_H + +#include + +#include "params.h" + +typedef struct { + uint8_t pub_seed[SPX_N]; + uint8_t sk_seed[SPX_N]; + +#ifdef SPX_SHA2 + // sha256 state that absorbed pub_seed + uint8_t state_seeded[40]; + +# if SPX_SHA512 + // sha512 state that absorbed pub_seed + uint8_t state_seeded_512[72]; +# endif +#endif + +#ifdef SPX_HARAKA + uint64_t tweaked512_rc64[10][8]; + uint32_t tweaked256_rc32[10][8]; +#endif +} spx_ctx; + +#endif diff --git a/Blastproof/keygen/fors.c b/Blastproof/keygen/fors.c new file mode 100644 index 0000000..e6aa4b4 --- /dev/null +++ b/Blastproof/keygen/fors.c @@ -0,0 +1,161 @@ +#include +#include +#include + +#include "fors.h" +#include "utils.h" +#include "utilsx1.h" +#include "hash.h" +#include "thash.h" +#include "address.h" + +static void fors_gen_sk(unsigned char *sk, const spx_ctx *ctx, + uint32_t fors_leaf_addr[8]) +{ + prf_addr(sk, ctx, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const spx_ctx *ctx, + uint32_t fors_leaf_addr[8]) +{ + thash(leaf, sk, 1, ctx, fors_leaf_addr); +} + +struct fors_gen_leaf_info { + uint32_t leaf_addrx[8]; +}; + +static void fors_gen_leafx1(unsigned char *leaf, + const spx_ctx *ctx, + uint32_t addr_idx, void *info) +{ + struct fors_gen_leaf_info *fors_info = info; + uint32_t *fors_leaf_addr = fors_info->leaf_addrx; + + /* Only set the parts that the caller doesn't set */ + set_tree_index(fors_leaf_addr, addr_idx); + set_type(fors_leaf_addr, SPX_ADDR_TYPE_FORSPRF); + fors_gen_sk(leaf, ctx, fors_leaf_addr); + + set_type(fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + fors_sk_to_leaf(leaf, leaf, + ctx, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) +{ + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= ((m[offset >> 3] >> (offset & 0x7)) & 1u) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void fors_sign(unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const spx_ctx *ctx, + const uint32_t fors_addr[8]) +{ + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + struct fors_gen_leaf_info fors_info = {0}; + uint32_t *fors_leaf_addr = fors_info.leaf_addrx; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + copy_keypair_addr(fors_tree_addr, fors_addr); + copy_keypair_addr(fors_leaf_addr, fors_addr); + + copy_keypair_addr(fors_pk_addr, fors_addr); + set_type(fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + set_tree_height(fors_tree_addr, 0); + set_tree_index(fors_tree_addr, indices[i] + idx_offset); + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSPRF); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, ctx, fors_tree_addr); + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + treehashx1(roots + i*SPX_N, sig, ctx, + indices[i], idx_offset, SPX_FORS_HEIGHT, fors_gen_leafx1, + fors_tree_addr, &fors_info); + + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + thash(pk, roots, SPX_FORS_TREES, ctx, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void fors_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const spx_ctx* ctx, + const uint32_t fors_addr[8]) +{ + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + copy_keypair_addr(fors_tree_addr, fors_addr); + copy_keypair_addr(fors_pk_addr, fors_addr); + + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + set_type(fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + set_tree_height(fors_tree_addr, 0); + set_tree_index(fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, ctx, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + compute_root(roots + i*SPX_N, leaf, indices[i], idx_offset, + sig, SPX_FORS_HEIGHT, ctx, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + thash(pk, roots, SPX_FORS_TREES, ctx, fors_pk_addr); +} diff --git a/Blastproof/keygen/fors.h b/Blastproof/keygen/fors.h new file mode 100644 index 0000000..8d98017 --- /dev/null +++ b/Blastproof/keygen/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "context.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +#define fors_sign SPX_NAMESPACE(fors_sign) +void fors_sign(unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const spx_ctx* ctx, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +#define fors_pk_from_sig SPX_NAMESPACE(fors_pk_from_sig) +void fors_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const spx_ctx* ctx, + const uint32_t fors_addr[8]); + +#endif diff --git a/Blastproof/keygen/fors.o b/Blastproof/keygen/fors.o new file mode 100644 index 0000000..20cd3d9 Binary files /dev/null and b/Blastproof/keygen/fors.o differ diff --git a/Blastproof/keygen/hash.h b/Blastproof/keygen/hash.h new file mode 100644 index 0000000..b141f09 --- /dev/null +++ b/Blastproof/keygen/hash.h @@ -0,0 +1,27 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include +#include "context.h" +#include "params.h" + +#define initialize_hash_function SPX_NAMESPACE(initialize_hash_function) +void initialize_hash_function(spx_ctx *ctx); + +#define prf_addr SPX_NAMESPACE(prf_addr) +void prf_addr(unsigned char *out, const spx_ctx *ctx, + const uint32_t addr[8]); + +#define gen_message_random SPX_NAMESPACE(gen_message_random) +void gen_message_random(unsigned char *R, const unsigned char *sk_prf, + const unsigned char *optrand, + const unsigned char *m, unsigned long long mlen, + const spx_ctx *ctx); + +#define hash_message SPX_NAMESPACE(hash_message) +void hash_message(unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, unsigned long long mlen, + const spx_ctx *ctx); + +#endif diff --git a/Blastproof/keygen/hash_sha2.c b/Blastproof/keygen/hash_sha2.c new file mode 100644 index 0000000..67098e6 --- /dev/null +++ b/Blastproof/keygen/hash_sha2.c @@ -0,0 +1,197 @@ +#include +#include + +#include "address.h" +#include "utils.h" +#include "params.h" +#include "hash.h" +#include "sha2.h" + +#if SPX_N >= 24 +#define SPX_SHAX_OUTPUT_BYTES SPX_SHA512_OUTPUT_BYTES +#define SPX_SHAX_BLOCK_BYTES SPX_SHA512_BLOCK_BYTES +#define shaX_inc_init sha512_inc_init +#define shaX_inc_blocks sha512_inc_blocks +#define shaX_inc_finalize sha512_inc_finalize +#define shaX sha512 +#define mgf1_X mgf1_512 +#else +#define SPX_SHAX_OUTPUT_BYTES SPX_SHA256_OUTPUT_BYTES +#define SPX_SHAX_BLOCK_BYTES SPX_SHA256_BLOCK_BYTES +#define shaX_inc_init sha256_inc_init +#define shaX_inc_blocks sha256_inc_blocks +#define shaX_inc_finalize sha256_inc_finalize +#define shaX sha256 +#define mgf1_X mgf1_256 +#endif + + +/* For SHA, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void initialize_hash_function(spx_ctx *ctx) +{ + seed_state(ctx); +} + +/* + * Computes PRF(pk_seed, sk_seed, addr). + */ +void prf_addr(unsigned char *out, const spx_ctx *ctx, + const uint32_t addr[8]) +{ + uint8_t sha2_state[40]; + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_N]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, ctx->state_seeded, 40 * sizeof(uint8_t)); + + /* Remainder: ADDR^c ‖ SK.seed */ + memcpy(buf, addr, SPX_SHA256_ADDR_BYTES); + memcpy(buf + SPX_SHA256_ADDR_BYTES, ctx->sk_seed, SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + SPX_N); + + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHAX_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void gen_message_random(unsigned char *R, const unsigned char *sk_prf, + const unsigned char *optrand, + const unsigned char *m, unsigned long long mlen, + const spx_ctx *ctx) +{ + (void)ctx; + + unsigned char buf[SPX_SHAX_BLOCK_BYTES + SPX_SHAX_OUTPUT_BYTES]; + uint8_t state[8 + SPX_SHAX_OUTPUT_BYTES]; + int i; + +#if SPX_N > SPX_SHAX_BLOCK_BYTES + #error "Currently only supports SPX_N of at most SPX_SHAX_BLOCK_BYTES" +#endif + + /* This implements HMAC-SHA */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHAX_BLOCK_BYTES - SPX_N); + + shaX_inc_init(state); + shaX_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHAX_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + shaX_inc_finalize(buf + SPX_SHAX_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHAX_BLOCK_BYTES - SPX_N); + shaX_inc_blocks(state, buf, 1); + + m += SPX_SHAX_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHAX_BLOCK_BYTES - SPX_N; + shaX_inc_finalize(buf + SPX_SHAX_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHAX_BLOCK_BYTES - SPX_N); + + shaX(buf, buf, SPX_SHAX_BLOCK_BYTES + SPX_SHAX_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void hash_message(unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, unsigned long long mlen, + const spx_ctx *ctx) +{ + (void)ctx; +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[2*SPX_N + SPX_SHAX_OUTPUT_BYTES]; + + /* Round to nearest multiple of SPX_SHAX_BLOCK_BYTES */ +#if (SPX_SHAX_BLOCK_BYTES & (SPX_SHAX_BLOCK_BYTES-1)) != 0 + #error "Assumes that SPX_SHAX_BLOCK_BYTES is a power of 2" +#endif +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHAX_BLOCK_BYTES - 1) & \ + -SPX_SHAX_BLOCK_BYTES) / SPX_SHAX_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[8 + SPX_SHAX_OUTPUT_BYTES]; + + shaX_inc_init(state); + + // seed: SHA-X(R ‖ PK.seed ‖ PK.root ‖ M) + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + shaX_inc_finalize(seed + 2*SPX_N, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + shaX_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + shaX_inc_finalize(seed + 2*SPX_N, state, m, mlen); + } + + // H_msg: MGF1-SHA-X(R ‖ PK.seed ‖ seed) + memcpy(seed, R, SPX_N); + memcpy(seed + SPX_N, pk, SPX_N); + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + mgf1_X(bufp, SPX_DGST_BYTES, seed, 2*SPX_N + SPX_SHAX_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + +#if SPX_TREE_BITS > 64 + #error For given height and depth, 64 bits cannot represent all subtrees +#endif + + if (SPX_D == 1) { + *tree = 0; + } else { + *tree = bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + } + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)bytes_to_ull(bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} + + diff --git a/Blastproof/keygen/hash_sha2.o b/Blastproof/keygen/hash_sha2.o new file mode 100644 index 0000000..7bafb7b Binary files /dev/null and b/Blastproof/keygen/hash_sha2.o differ diff --git a/Blastproof/keygen/keygen.cpp b/Blastproof/keygen/keygen.cpp new file mode 100644 index 0000000..c04e224 --- /dev/null +++ b/Blastproof/keygen/keygen.cpp @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: MPL-2.0 +#include +#include +extern "C" { +#include "api.h" +#include "argon2.h" +#include "sha3.h" +} +#undef str +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; +namespace fs=filesystem; +const map azerty_to_qwerty={ + {"a","q"}, + {"A","Q"}, + {"z","w"}, + {"Z","W"}, + {"q","a"}, + {"Q","A"}, + {"m",";"}, + {"M",":"}, + {"w","z"}, + {"W","Z"}, + {",","m"}, + {"?","M"}, + {"²","`"}, + {"&","1"}, + {"é","2"}, + {"\"","3"}, + {"'","4"}, + {"(","5"}, + {"-","6"}, + {"è","7"}, + {"_","8"}, + {"ç","9"}, + {"à","0"}, + {"~","2"}, + {"#","3"}, + {"{","4"}, + {"[","5"}, + {"|","6"}, + {"`","7"}, + {"\\","8"}, + {"^","9"}, + {"@","0"}, + {"1","!"}, + {"2","@"}, + {"3","#"}, + {"4","$"}, + {"5","%"}, + {"6","^"}, + {"7","&"}, + {"8","*"}, + {"9","("}, + {"0",")"}, + {";",","}, + {".","<"}, + {":","."}, + {"/",">"}, + {"!","/"}, + {"§","?"}, + {"<","\\"}, + {">","|"}, + {"ù","'"}, + {"%","\""}, + {"^","["}, + {"¨","{"}, + {"$","]"}, + {"£","}"}, + {"µ","|"} +}; +void secure_erase(void *address,size_t size) { + explicit_bzero(address,size); +} +vector get_urandom(size_t n) { + vector out(n); + ifstream urandom("/dev/urandom",ios::binary); + if (!urandom.is_open()) { + throw runtime_error("open"); + } + urandom.read(reinterpret_cast(out.data()),n); + if (urandom.gcount()!=static_cast(n)) { + throw runtime_error("error"); + } + return out; +} +int main(int argc,char **argv) { + ofstream core("/proc/self/coredump_filter"); + if (!core) { + cout<<"[Keygen] Error: can't setup security."< files; + if (argc>1) { + for (int i=1;i fbuf; + vector sigsbuf(files.size()*CRYPTO_BYTES); + vector pksbuf(files.size()*CRYPTO_PUBLICKEYBYTES); + if (mlock(sigsbuf.data(),sigsbuf.size())!=0) { + cout<<"[Keygen] Error: can't setup security."<(fbuf.data()),size)) { + cout<<"[Keygen] Error: can't read file: "< hash(64); + sha3(fbuf.data(),size,hash.data(),hash.size()); + if (crypto_sign_signature(sig,&siglen,hash.data(),hash.size(),sk)) { + cout<<"[Keygen] Error: can't generate signature for file: "<(sigsbuf.data()+i*CRYPTO_BYTES),CRYPTO_BYTES); + sigfile.close(); + } + secure_erase(sigsbuf.data(),sigsbuf.size()); + cout<<"[Keygen] You are about to define a boot password. This password will be asked everytime you boot this instance of Vystem."< passhex; + passhex.resize(num); + try { + passhex=get_urandom(num); + } catch (const exception e) { + if (string(e.what())=="open") { + cout<<"[Keygen] Error: can't open secure source of randomness."<(byte); + } + password=oss.str(); + secure_erase(passhex.data(),passhex.size()); + } else if (strlen(pwd)<=512){ + password=string(pwd); + char* confirm_pwd=getpass("[Keygen] Confirm password: "); + string confirm_password(confirm_pwd); + if (confirm_password!=password) { + cout<<"[Keygen] Error: password don't correspond. Please try again."<=0x80) { + cout<<"[Keygen] Error: one character isn't ASCII."< passutf16(password.size()*2); + for (int i=0;i salt(32); + salt=get_urandom(32); + vector seed; + seed.resize(CRYPTO_SEEDBYTES); + if (argon2id_hash_raw(3,262144,1,passutf16.data(),passutf16.size(),salt.data(),salt.size(),seed.data(),CRYPTO_SEEDBYTES)!=0) { + cout<<"[Keygen] Error: can't generate seed from password."<\nUINT8 bp_key_mainsig["< +#include + +#include "utils.h" +#include "utilsx1.h" +#include "wots.h" +#include "wotsx1.h" +#include "merkle.h" +#include "address.h" +#include "params.h" + +/* + * This generates a Merkle signature (WOTS signature followed by the Merkle + * authentication path). This is in this file because most of the complexity + * is involved with the WOTS signature; the Merkle authentication path logic + * is mostly hidden in treehashx4 + */ +void merkle_sign(uint8_t *sig, unsigned char *root, + const spx_ctx *ctx, + uint32_t wots_addr[8], uint32_t tree_addr[8], + uint32_t idx_leaf) +{ + unsigned char *auth_path = sig + SPX_WOTS_BYTES; + struct leaf_info_x1 info = { 0 }; + unsigned steps[ SPX_WOTS_LEN ]; + + info.wots_sig = sig; + chain_lengths(steps, root); + info.wots_steps = steps; + + set_type(&tree_addr[0], SPX_ADDR_TYPE_HASHTREE); + set_type(&info.pk_addr[0], SPX_ADDR_TYPE_WOTSPK); + copy_subtree_addr(&info.leaf_addr[0], wots_addr); + copy_subtree_addr(&info.pk_addr[0], wots_addr); + + info.wots_sign_leaf = idx_leaf; + + treehashx1(root, auth_path, ctx, + idx_leaf, 0, + SPX_TREE_HEIGHT, + wots_gen_leafx1, + tree_addr, &info); +} + +/* Compute root node of the top-most subtree. */ +void merkle_gen_root(unsigned char *root, const spx_ctx *ctx) +{ + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N + SPX_WOTS_BYTES]; + uint32_t top_tree_addr[8] = {0}; + uint32_t wots_addr[8] = {0}; + + set_layer_addr(top_tree_addr, SPX_D - 1); + set_layer_addr(wots_addr, SPX_D - 1); + + merkle_sign(auth_path, root, ctx, + wots_addr, top_tree_addr, + (uint32_t)~0 /* ~0 means "don't bother generating an auth path */ ); +} diff --git a/Blastproof/keygen/merkle.h b/Blastproof/keygen/merkle.h new file mode 100644 index 0000000..9ac2759 --- /dev/null +++ b/Blastproof/keygen/merkle.h @@ -0,0 +1,18 @@ +#if !defined( MERKLE_H_ ) +#define MERKLE_H_ + +#include + +/* Generate a Merkle signature (WOTS signature followed by the Merkle */ +/* authentication path) */ +#define merkle_sign SPX_NAMESPACE(merkle_sign) +void merkle_sign(uint8_t *sig, unsigned char *root, + const spx_ctx* ctx, + uint32_t wots_addr[8], uint32_t tree_addr[8], + uint32_t idx_leaf); + +/* Compute the root node of the top-most subtree. */ +#define merkle_gen_root SPX_NAMESPACE(merkle_gen_root) +void merkle_gen_root(unsigned char *root, const spx_ctx* ctx); + +#endif /* MERKLE_H_ */ diff --git a/Blastproof/keygen/merkle.o b/Blastproof/keygen/merkle.o new file mode 100644 index 0000000..0146c87 Binary files /dev/null and b/Blastproof/keygen/merkle.o differ diff --git a/Blastproof/keygen/params.h b/Blastproof/keygen/params.h new file mode 100644 index 0000000..6dc6974 --- /dev/null +++ b/Blastproof/keygen/params.h @@ -0,0 +1,3 @@ +#define str(s) #s +#define xstr(s) str(s) +#include "params/params-sphincs-sha2-256f.h" diff --git a/Blastproof/keygen/params/params-sphincs-sha2-256f.h b/Blastproof/keygen/params/params-sphincs-sha2-256f.h new file mode 100644 index 0000000..53c5bef --- /dev/null +++ b/Blastproof/keygen/params/params-sphincs-sha2-256f.h @@ -0,0 +1,85 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +#define SPX_NAMESPACE(s) SPX_##s + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 68 +/* Number of subtree layer. */ +#define SPX_D 17 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 9 +#define SPX_FORS_TREES 35 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* This is a SHA2-based parameter set, hence whether we use SHA-256 + * exclusively or we use both SHA-256 and SHA-512 is controlled by + * the following #define */ +#define SPX_SHA512 1 /* Use SHA-512 for H and T_l, l >= 2 */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#if SPX_WOTS_W == 256 + #define SPX_WOTS_LOGW 8 +#elif SPX_WOTS_W == 16 + #define SPX_WOTS_LOGW 4 +#else + #error SPX_WOTS_W assumed 16 or 256 +#endif + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#if SPX_WOTS_W == 256 + #if SPX_N <= 1 + #define SPX_WOTS_LEN2 1 + #elif SPX_N <= 256 + #define SPX_WOTS_LEN2 2 + #else + #error Did not precompute SPX_WOTS_LEN2 for n outside {2, .., 256} + #endif +#elif SPX_WOTS_W == 16 + #if SPX_N <= 8 + #define SPX_WOTS_LEN2 2 + #elif SPX_N <= 136 + #define SPX_WOTS_LEN2 3 + #elif SPX_N <= 256 + #define SPX_WOTS_LEN2 4 + #else + #error Did not precompute SPX_WOTS_LEN2 for n outside {2, .., 256} + #endif +#endif + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +#if SPX_TREE_HEIGHT * SPX_D != SPX_FULL_HEIGHT + #error SPX_D should always divide SPX_FULL_HEIGHT +#endif + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +#include "../sha2_offsets.h" + +#endif diff --git a/Blastproof/keygen/randombytes.c b/Blastproof/keygen/randombytes.c new file mode 100644 index 0000000..cfbca17 --- /dev/null +++ b/Blastproof/keygen/randombytes.c @@ -0,0 +1,43 @@ +/* +This code was taken from the SPHINCS reference implementation and is public domain. +*/ + +#include +#include + +#include "randombytes.h" + +static int fd = -1; + +void randombytes(unsigned char *x, unsigned long long xlen) +{ + unsigned long long i; + + if (fd == -1) { + for (;;) { + fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + break; + } + sleep(1); + } + } + + while (xlen > 0) { + if (xlen < 1048576) { + i = xlen; + } + else { + i = 1048576; + } + + i = (unsigned long long)read(fd, x, i); + if (i < 1) { + sleep(1); + continue; + } + + x += i; + xlen -= i; + } +} diff --git a/Blastproof/keygen/randombytes.h b/Blastproof/keygen/randombytes.h new file mode 100644 index 0000000..671c1b1 --- /dev/null +++ b/Blastproof/keygen/randombytes.h @@ -0,0 +1,6 @@ +#ifndef SPX_RANDOMBYTES_H +#define SPX_RANDOMBYTES_H + +extern void randombytes(unsigned char * x,unsigned long long xlen); + +#endif diff --git a/Blastproof/keygen/randombytes.o b/Blastproof/keygen/randombytes.o new file mode 100644 index 0000000..26c3531 Binary files /dev/null and b/Blastproof/keygen/randombytes.o differ diff --git a/Blastproof/keygen/sha2.c b/Blastproof/keygen/sha2.c new file mode 100644 index 0000000..ef73047 --- /dev/null +++ b/Blastproof/keygen/sha2.c @@ -0,0 +1,700 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "utils.h" +#include "sha2.h" + +static uint32_t load_bigendian_32(const uint8_t *x) { + return (uint32_t)(x[3]) | (((uint32_t)(x[2])) << 8) | + (((uint32_t)(x[1])) << 16) | (((uint32_t)(x[0])) << 24); +} + +static uint64_t load_bigendian_64(const uint8_t *x) { + return (uint64_t)(x[7]) | (((uint64_t)(x[6])) << 8) | + (((uint64_t)(x[5])) << 16) | (((uint64_t)(x[4])) << 24) | + (((uint64_t)(x[3])) << 32) | (((uint64_t)(x[2])) << 40) | + (((uint64_t)(x[1])) << 48) | (((uint64_t)(x[0])) << 56); +} + +static void store_bigendian_32(uint8_t *x, uint64_t u) { + x[3] = (uint8_t) u; + u >>= 8; + x[2] = (uint8_t) u; + u >>= 8; + x[1] = (uint8_t) u; + u >>= 8; + x[0] = (uint8_t) u; +} + +static void store_bigendian_64(uint8_t *x, uint64_t u) { + x[7] = (uint8_t) u; + u >>= 8; + x[6] = (uint8_t) u; + u >>= 8; + x[5] = (uint8_t) u; + u >>= 8; + x[4] = (uint8_t) u; + u >>= 8; + x[3] = (uint8_t) u; + u >>= 8; + x[2] = (uint8_t) u; + u >>= 8; + x[1] = (uint8_t) u; + u >>= 8; + x[0] = (uint8_t) u; +} + +#define SHR(x, c) ((x) >> (c)) +#define ROTR_32(x, c) (((x) >> (c)) | ((x) << (32 - (c)))) +#define ROTR_64(x,c) (((x) >> (c)) | ((x) << (64 - (c)))) + +#define Ch(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +#define Sigma0_32(x) (ROTR_32(x, 2) ^ ROTR_32(x,13) ^ ROTR_32(x,22)) +#define Sigma1_32(x) (ROTR_32(x, 6) ^ ROTR_32(x,11) ^ ROTR_32(x,25)) +#define sigma0_32(x) (ROTR_32(x, 7) ^ ROTR_32(x,18) ^ SHR(x, 3)) +#define sigma1_32(x) (ROTR_32(x,17) ^ ROTR_32(x,19) ^ SHR(x,10)) + +#define Sigma0_64(x) (ROTR_64(x,28) ^ ROTR_64(x,34) ^ ROTR_64(x,39)) +#define Sigma1_64(x) (ROTR_64(x,14) ^ ROTR_64(x,18) ^ ROTR_64(x,41)) +#define sigma0_64(x) (ROTR_64(x, 1) ^ ROTR_64(x, 8) ^ SHR(x,7)) +#define sigma1_64(x) (ROTR_64(x,19) ^ ROTR_64(x,61) ^ SHR(x,6)) + +#define M_32(w0, w14, w9, w1) w0 = sigma1_32(w14) + (w9) + sigma0_32(w1) + (w0); +#define M_64(w0, w14, w9, w1) w0 = sigma1_64(w14) + (w9) + sigma0_64(w1) + (w0); + +#define EXPAND_32 \ + M_32(w0, w14, w9, w1) \ + M_32(w1, w15, w10, w2) \ + M_32(w2, w0, w11, w3) \ + M_32(w3, w1, w12, w4) \ + M_32(w4, w2, w13, w5) \ + M_32(w5, w3, w14, w6) \ + M_32(w6, w4, w15, w7) \ + M_32(w7, w5, w0, w8) \ + M_32(w8, w6, w1, w9) \ + M_32(w9, w7, w2, w10) \ + M_32(w10, w8, w3, w11) \ + M_32(w11, w9, w4, w12) \ + M_32(w12, w10, w5, w13) \ + M_32(w13, w11, w6, w14) \ + M_32(w14, w12, w7, w15) \ + M_32(w15, w13, w8, w0) + +#define EXPAND_64 \ + M_64(w0 ,w14,w9 ,w1 ) \ + M_64(w1 ,w15,w10,w2 ) \ + M_64(w2 ,w0 ,w11,w3 ) \ + M_64(w3 ,w1 ,w12,w4 ) \ + M_64(w4 ,w2 ,w13,w5 ) \ + M_64(w5 ,w3 ,w14,w6 ) \ + M_64(w6 ,w4 ,w15,w7 ) \ + M_64(w7 ,w5 ,w0 ,w8 ) \ + M_64(w8 ,w6 ,w1 ,w9 ) \ + M_64(w9 ,w7 ,w2 ,w10) \ + M_64(w10,w8 ,w3 ,w11) \ + M_64(w11,w9 ,w4 ,w12) \ + M_64(w12,w10,w5 ,w13) \ + M_64(w13,w11,w6 ,w14) \ + M_64(w14,w12,w7 ,w15) \ + M_64(w15,w13,w8 ,w0 ) + +#define F_32(w, k) \ + T1 = h + Sigma1_32(e) + Ch(e, f, g) + (k) + (w); \ + T2 = Sigma0_32(a) + Maj(a, b, c); \ + h = g; \ + g = f; \ + f = e; \ + e = d + T1; \ + d = c; \ + c = b; \ + b = a; \ + a = T1 + T2; + +#define F_64(w,k) \ + T1 = h + Sigma1_64(e) + Ch(e,f,g) + k + w; \ + T2 = Sigma0_64(a) + Maj(a,b,c); \ + h = g; \ + g = f; \ + f = e; \ + e = d + T1; \ + d = c; \ + c = b; \ + b = a; \ + a = T1 + T2; + +static size_t crypto_hashblocks_sha256(uint8_t *statebytes, + const uint8_t *in, size_t inlen) { + uint32_t state[8]; + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + uint32_t e; + uint32_t f; + uint32_t g; + uint32_t h; + uint32_t T1; + uint32_t T2; + + a = load_bigendian_32(statebytes + 0); + state[0] = a; + b = load_bigendian_32(statebytes + 4); + state[1] = b; + c = load_bigendian_32(statebytes + 8); + state[2] = c; + d = load_bigendian_32(statebytes + 12); + state[3] = d; + e = load_bigendian_32(statebytes + 16); + state[4] = e; + f = load_bigendian_32(statebytes + 20); + state[5] = f; + g = load_bigendian_32(statebytes + 24); + state[6] = g; + h = load_bigendian_32(statebytes + 28); + state[7] = h; + + while (inlen >= 64) { + uint32_t w0 = load_bigendian_32(in + 0); + uint32_t w1 = load_bigendian_32(in + 4); + uint32_t w2 = load_bigendian_32(in + 8); + uint32_t w3 = load_bigendian_32(in + 12); + uint32_t w4 = load_bigendian_32(in + 16); + uint32_t w5 = load_bigendian_32(in + 20); + uint32_t w6 = load_bigendian_32(in + 24); + uint32_t w7 = load_bigendian_32(in + 28); + uint32_t w8 = load_bigendian_32(in + 32); + uint32_t w9 = load_bigendian_32(in + 36); + uint32_t w10 = load_bigendian_32(in + 40); + uint32_t w11 = load_bigendian_32(in + 44); + uint32_t w12 = load_bigendian_32(in + 48); + uint32_t w13 = load_bigendian_32(in + 52); + uint32_t w14 = load_bigendian_32(in + 56); + uint32_t w15 = load_bigendian_32(in + 60); + + F_32(w0, 0x428a2f98) + F_32(w1, 0x71374491) + F_32(w2, 0xb5c0fbcf) + F_32(w3, 0xe9b5dba5) + F_32(w4, 0x3956c25b) + F_32(w5, 0x59f111f1) + F_32(w6, 0x923f82a4) + F_32(w7, 0xab1c5ed5) + F_32(w8, 0xd807aa98) + F_32(w9, 0x12835b01) + F_32(w10, 0x243185be) + F_32(w11, 0x550c7dc3) + F_32(w12, 0x72be5d74) + F_32(w13, 0x80deb1fe) + F_32(w14, 0x9bdc06a7) + F_32(w15, 0xc19bf174) + + EXPAND_32 + + F_32(w0, 0xe49b69c1) + F_32(w1, 0xefbe4786) + F_32(w2, 0x0fc19dc6) + F_32(w3, 0x240ca1cc) + F_32(w4, 0x2de92c6f) + F_32(w5, 0x4a7484aa) + F_32(w6, 0x5cb0a9dc) + F_32(w7, 0x76f988da) + F_32(w8, 0x983e5152) + F_32(w9, 0xa831c66d) + F_32(w10, 0xb00327c8) + F_32(w11, 0xbf597fc7) + F_32(w12, 0xc6e00bf3) + F_32(w13, 0xd5a79147) + F_32(w14, 0x06ca6351) + F_32(w15, 0x14292967) + + EXPAND_32 + + F_32(w0, 0x27b70a85) + F_32(w1, 0x2e1b2138) + F_32(w2, 0x4d2c6dfc) + F_32(w3, 0x53380d13) + F_32(w4, 0x650a7354) + F_32(w5, 0x766a0abb) + F_32(w6, 0x81c2c92e) + F_32(w7, 0x92722c85) + F_32(w8, 0xa2bfe8a1) + F_32(w9, 0xa81a664b) + F_32(w10, 0xc24b8b70) + F_32(w11, 0xc76c51a3) + F_32(w12, 0xd192e819) + F_32(w13, 0xd6990624) + F_32(w14, 0xf40e3585) + F_32(w15, 0x106aa070) + + EXPAND_32 + + F_32(w0, 0x19a4c116) + F_32(w1, 0x1e376c08) + F_32(w2, 0x2748774c) + F_32(w3, 0x34b0bcb5) + F_32(w4, 0x391c0cb3) + F_32(w5, 0x4ed8aa4a) + F_32(w6, 0x5b9cca4f) + F_32(w7, 0x682e6ff3) + F_32(w8, 0x748f82ee) + F_32(w9, 0x78a5636f) + F_32(w10, 0x84c87814) + F_32(w11, 0x8cc70208) + F_32(w12, 0x90befffa) + F_32(w13, 0xa4506ceb) + F_32(w14, 0xbef9a3f7) + F_32(w15, 0xc67178f2) + + a += state[0]; + b += state[1]; + c += state[2]; + d += state[3]; + e += state[4]; + f += state[5]; + g += state[6]; + h += state[7]; + + state[0] = a; + state[1] = b; + state[2] = c; + state[3] = d; + state[4] = e; + state[5] = f; + state[6] = g; + state[7] = h; + + in += 64; + inlen -= 64; + } + + store_bigendian_32(statebytes + 0, state[0]); + store_bigendian_32(statebytes + 4, state[1]); + store_bigendian_32(statebytes + 8, state[2]); + store_bigendian_32(statebytes + 12, state[3]); + store_bigendian_32(statebytes + 16, state[4]); + store_bigendian_32(statebytes + 20, state[5]); + store_bigendian_32(statebytes + 24, state[6]); + store_bigendian_32(statebytes + 28, state[7]); + + return inlen; +} + +static int crypto_hashblocks_sha512(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen) +{ + uint64_t state[8]; + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; + uint64_t e; + uint64_t f; + uint64_t g; + uint64_t h; + uint64_t T1; + uint64_t T2; + + a = load_bigendian_64(statebytes + 0); state[0] = a; + b = load_bigendian_64(statebytes + 8); state[1] = b; + c = load_bigendian_64(statebytes + 16); state[2] = c; + d = load_bigendian_64(statebytes + 24); state[3] = d; + e = load_bigendian_64(statebytes + 32); state[4] = e; + f = load_bigendian_64(statebytes + 40); state[5] = f; + g = load_bigendian_64(statebytes + 48); state[6] = g; + h = load_bigendian_64(statebytes + 56); state[7] = h; + + while (inlen >= 128) { + uint64_t w0 = load_bigendian_64(in + 0); + uint64_t w1 = load_bigendian_64(in + 8); + uint64_t w2 = load_bigendian_64(in + 16); + uint64_t w3 = load_bigendian_64(in + 24); + uint64_t w4 = load_bigendian_64(in + 32); + uint64_t w5 = load_bigendian_64(in + 40); + uint64_t w6 = load_bigendian_64(in + 48); + uint64_t w7 = load_bigendian_64(in + 56); + uint64_t w8 = load_bigendian_64(in + 64); + uint64_t w9 = load_bigendian_64(in + 72); + uint64_t w10 = load_bigendian_64(in + 80); + uint64_t w11 = load_bigendian_64(in + 88); + uint64_t w12 = load_bigendian_64(in + 96); + uint64_t w13 = load_bigendian_64(in + 104); + uint64_t w14 = load_bigendian_64(in + 112); + uint64_t w15 = load_bigendian_64(in + 120); + + F_64(w0 ,0x428a2f98d728ae22ULL) + F_64(w1 ,0x7137449123ef65cdULL) + F_64(w2 ,0xb5c0fbcfec4d3b2fULL) + F_64(w3 ,0xe9b5dba58189dbbcULL) + F_64(w4 ,0x3956c25bf348b538ULL) + F_64(w5 ,0x59f111f1b605d019ULL) + F_64(w6 ,0x923f82a4af194f9bULL) + F_64(w7 ,0xab1c5ed5da6d8118ULL) + F_64(w8 ,0xd807aa98a3030242ULL) + F_64(w9 ,0x12835b0145706fbeULL) + F_64(w10,0x243185be4ee4b28cULL) + F_64(w11,0x550c7dc3d5ffb4e2ULL) + F_64(w12,0x72be5d74f27b896fULL) + F_64(w13,0x80deb1fe3b1696b1ULL) + F_64(w14,0x9bdc06a725c71235ULL) + F_64(w15,0xc19bf174cf692694ULL) + + EXPAND_64 + + F_64(w0 ,0xe49b69c19ef14ad2ULL) + F_64(w1 ,0xefbe4786384f25e3ULL) + F_64(w2 ,0x0fc19dc68b8cd5b5ULL) + F_64(w3 ,0x240ca1cc77ac9c65ULL) + F_64(w4 ,0x2de92c6f592b0275ULL) + F_64(w5 ,0x4a7484aa6ea6e483ULL) + F_64(w6 ,0x5cb0a9dcbd41fbd4ULL) + F_64(w7 ,0x76f988da831153b5ULL) + F_64(w8 ,0x983e5152ee66dfabULL) + F_64(w9 ,0xa831c66d2db43210ULL) + F_64(w10,0xb00327c898fb213fULL) + F_64(w11,0xbf597fc7beef0ee4ULL) + F_64(w12,0xc6e00bf33da88fc2ULL) + F_64(w13,0xd5a79147930aa725ULL) + F_64(w14,0x06ca6351e003826fULL) + F_64(w15,0x142929670a0e6e70ULL) + + EXPAND_64 + + F_64(w0 ,0x27b70a8546d22ffcULL) + F_64(w1 ,0x2e1b21385c26c926ULL) + F_64(w2 ,0x4d2c6dfc5ac42aedULL) + F_64(w3 ,0x53380d139d95b3dfULL) + F_64(w4 ,0x650a73548baf63deULL) + F_64(w5 ,0x766a0abb3c77b2a8ULL) + F_64(w6 ,0x81c2c92e47edaee6ULL) + F_64(w7 ,0x92722c851482353bULL) + F_64(w8 ,0xa2bfe8a14cf10364ULL) + F_64(w9 ,0xa81a664bbc423001ULL) + F_64(w10,0xc24b8b70d0f89791ULL) + F_64(w11,0xc76c51a30654be30ULL) + F_64(w12,0xd192e819d6ef5218ULL) + F_64(w13,0xd69906245565a910ULL) + F_64(w14,0xf40e35855771202aULL) + F_64(w15,0x106aa07032bbd1b8ULL) + + EXPAND_64 + + F_64(w0 ,0x19a4c116b8d2d0c8ULL) + F_64(w1 ,0x1e376c085141ab53ULL) + F_64(w2 ,0x2748774cdf8eeb99ULL) + F_64(w3 ,0x34b0bcb5e19b48a8ULL) + F_64(w4 ,0x391c0cb3c5c95a63ULL) + F_64(w5 ,0x4ed8aa4ae3418acbULL) + F_64(w6 ,0x5b9cca4f7763e373ULL) + F_64(w7 ,0x682e6ff3d6b2b8a3ULL) + F_64(w8 ,0x748f82ee5defb2fcULL) + F_64(w9 ,0x78a5636f43172f60ULL) + F_64(w10,0x84c87814a1f0ab72ULL) + F_64(w11,0x8cc702081a6439ecULL) + F_64(w12,0x90befffa23631e28ULL) + F_64(w13,0xa4506cebde82bde9ULL) + F_64(w14,0xbef9a3f7b2c67915ULL) + F_64(w15,0xc67178f2e372532bULL) + + EXPAND_64 + + F_64(w0 ,0xca273eceea26619cULL) + F_64(w1 ,0xd186b8c721c0c207ULL) + F_64(w2 ,0xeada7dd6cde0eb1eULL) + F_64(w3 ,0xf57d4f7fee6ed178ULL) + F_64(w4 ,0x06f067aa72176fbaULL) + F_64(w5 ,0x0a637dc5a2c898a6ULL) + F_64(w6 ,0x113f9804bef90daeULL) + F_64(w7 ,0x1b710b35131c471bULL) + F_64(w8 ,0x28db77f523047d84ULL) + F_64(w9 ,0x32caab7b40c72493ULL) + F_64(w10,0x3c9ebe0a15c9bebcULL) + F_64(w11,0x431d67c49c100d4cULL) + F_64(w12,0x4cc5d4becb3e42b6ULL) + F_64(w13,0x597f299cfc657e2aULL) + F_64(w14,0x5fcb6fab3ad6faecULL) + F_64(w15,0x6c44198c4a475817ULL) + + a += state[0]; + b += state[1]; + c += state[2]; + d += state[3]; + e += state[4]; + f += state[5]; + g += state[6]; + h += state[7]; + + state[0] = a; + state[1] = b; + state[2] = c; + state[3] = d; + state[4] = e; + state[5] = f; + state[6] = g; + state[7] = h; + + in += 128; + inlen -= 128; + } + + store_bigendian_64(statebytes + 0,state[0]); + store_bigendian_64(statebytes + 8,state[1]); + store_bigendian_64(statebytes + 16,state[2]); + store_bigendian_64(statebytes + 24,state[3]); + store_bigendian_64(statebytes + 32,state[4]); + store_bigendian_64(statebytes + 40,state[5]); + store_bigendian_64(statebytes + 48,state[6]); + store_bigendian_64(statebytes + 56,state[7]); + + return inlen; +} + + +static const uint8_t iv_256[32] = { + 0x6a, 0x09, 0xe6, 0x67, 0xbb, 0x67, 0xae, 0x85, + 0x3c, 0x6e, 0xf3, 0x72, 0xa5, 0x4f, 0xf5, 0x3a, + 0x51, 0x0e, 0x52, 0x7f, 0x9b, 0x05, 0x68, 0x8c, + 0x1f, 0x83, 0xd9, 0xab, 0x5b, 0xe0, 0xcd, 0x19 +}; + +static const uint8_t iv_512[64] = { + 0x6a, 0x09, 0xe6, 0x67, 0xf3, 0xbc, 0xc9, 0x08, 0xbb, 0x67, 0xae, + 0x85, 0x84, 0xca, 0xa7, 0x3b, 0x3c, 0x6e, 0xf3, 0x72, 0xfe, 0x94, + 0xf8, 0x2b, 0xa5, 0x4f, 0xf5, 0x3a, 0x5f, 0x1d, 0x36, 0xf1, 0x51, + 0x0e, 0x52, 0x7f, 0xad, 0xe6, 0x82, 0xd1, 0x9b, 0x05, 0x68, 0x8c, + 0x2b, 0x3e, 0x6c, 0x1f, 0x1f, 0x83, 0xd9, 0xab, 0xfb, 0x41, 0xbd, + 0x6b, 0x5b, 0xe0, 0xcd, 0x19, 0x13, 0x7e, 0x21, 0x79 +}; + +void sha256_inc_init(uint8_t *state) { + for (size_t i = 0; i < 32; ++i) { + state[i] = iv_256[i]; + } + for (size_t i = 32; i < 40; ++i) { + state[i] = 0; + } +} + +void sha512_inc_init(uint8_t *state) { + for (size_t i = 0; i < 64; ++i) { + state[i] = iv_512[i]; + } + for (size_t i = 64; i < 72; ++i) { + state[i] = 0; + } +} + +void sha256_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks) { + uint64_t bytes = load_bigendian_64(state + 32); + + crypto_hashblocks_sha256(state, in, 64 * inblocks); + bytes += 64 * inblocks; + + store_bigendian_64(state + 32, bytes); +} + +void sha512_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks) { + uint64_t bytes = load_bigendian_64(state + 64); + + crypto_hashblocks_sha512(state, in, 128 * inblocks); + bytes += 128 * inblocks; + + store_bigendian_64(state + 64, bytes); +} + +void sha256_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen) { + uint8_t padded[128]; + uint64_t bytes = load_bigendian_64(state + 32) + inlen; + + crypto_hashblocks_sha256(state, in, inlen); + in += inlen; + inlen &= 63; + in -= inlen; + + for (size_t i = 0; i < inlen; ++i) { + padded[i] = in[i]; + } + padded[inlen] = 0x80; + + if (inlen < 56) { + for (size_t i = inlen + 1; i < 56; ++i) { + padded[i] = 0; + } + padded[56] = (uint8_t) (bytes >> 53); + padded[57] = (uint8_t) (bytes >> 45); + padded[58] = (uint8_t) (bytes >> 37); + padded[59] = (uint8_t) (bytes >> 29); + padded[60] = (uint8_t) (bytes >> 21); + padded[61] = (uint8_t) (bytes >> 13); + padded[62] = (uint8_t) (bytes >> 5); + padded[63] = (uint8_t) (bytes << 3); + crypto_hashblocks_sha256(state, padded, 64); + } else { + for (size_t i = inlen + 1; i < 120; ++i) { + padded[i] = 0; + } + padded[120] = (uint8_t) (bytes >> 53); + padded[121] = (uint8_t) (bytes >> 45); + padded[122] = (uint8_t) (bytes >> 37); + padded[123] = (uint8_t) (bytes >> 29); + padded[124] = (uint8_t) (bytes >> 21); + padded[125] = (uint8_t) (bytes >> 13); + padded[126] = (uint8_t) (bytes >> 5); + padded[127] = (uint8_t) (bytes << 3); + crypto_hashblocks_sha256(state, padded, 128); + } + + for (size_t i = 0; i < 32; ++i) { + out[i] = state[i]; + } + +} + +void sha512_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen) { + uint8_t padded[256]; + uint64_t bytes = load_bigendian_64(state + 64) + inlen; + + crypto_hashblocks_sha512(state, in, inlen); + in += inlen; + inlen &= 127; + in -= inlen; + + for (size_t i = 0; i < inlen; ++i) { + padded[i] = in[i]; + } + padded[inlen] = 0x80; + + if (inlen < 112) { + for (size_t i = inlen + 1; i < 119; ++i) { + padded[i] = 0; + } + padded[119] = (uint8_t) (bytes >> 61); + padded[120] = (uint8_t) (bytes >> 53); + padded[121] = (uint8_t) (bytes >> 45); + padded[122] = (uint8_t) (bytes >> 37); + padded[123] = (uint8_t) (bytes >> 29); + padded[124] = (uint8_t) (bytes >> 21); + padded[125] = (uint8_t) (bytes >> 13); + padded[126] = (uint8_t) (bytes >> 5); + padded[127] = (uint8_t) (bytes << 3); + crypto_hashblocks_sha512(state, padded, 128); + } else { + for (size_t i = inlen + 1; i < 247; ++i) { + padded[i] = 0; + } + padded[247] = (uint8_t) (bytes >> 61); + padded[248] = (uint8_t) (bytes >> 53); + padded[249] = (uint8_t) (bytes >> 45); + padded[250] = (uint8_t) (bytes >> 37); + padded[251] = (uint8_t) (bytes >> 29); + padded[252] = (uint8_t) (bytes >> 21); + padded[253] = (uint8_t) (bytes >> 13); + padded[254] = (uint8_t) (bytes >> 5); + padded[255] = (uint8_t) (bytes << 3); + crypto_hashblocks_sha512(state, padded, 256); + } + + for (size_t i = 0; i < 64; ++i) { + out[i] = state[i]; + } +} + +void sha256(uint8_t *out, const uint8_t *in, size_t inlen) { + uint8_t state[40]; + + sha256_inc_init(state); + sha256_inc_finalize(out, state, in, inlen); +} + +void sha512(uint8_t *out, const uint8_t *in, size_t inlen) { + uint8_t state[72]; + + sha512_inc_init(state); + sha512_inc_finalize(out, state, in, inlen); +} + +/** + * mgf1 function based on the SHA-256 hash function + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void mgf1_256(unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) +{ + SPX_VLA(uint8_t, inbuf, inlen+4); + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i+1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + u32_to_bytes(inbuf + inlen, i); + sha256(out, inbuf, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i*SPX_SHA256_OUTPUT_BYTES) { + u32_to_bytes(inbuf + inlen, i); + sha256(outbuf, inbuf, inlen + 4); + memcpy(out, outbuf, outlen - i*SPX_SHA256_OUTPUT_BYTES); + } +} + +/* + * mgf1 function based on the SHA-512 hash function + */ +void mgf1_512(unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) +{ + SPX_VLA(uint8_t, inbuf, inlen+4); + unsigned char outbuf[SPX_SHA512_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* While we can fit in at least another full block of SHA512 output.. */ + for (i = 0; (i+1)*SPX_SHA512_OUTPUT_BYTES <= outlen; i++) { + u32_to_bytes(inbuf + inlen, i); + sha512(out, inbuf, inlen + 4); + out += SPX_SHA512_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i*SPX_SHA512_OUTPUT_BYTES) { + u32_to_bytes(inbuf + inlen, i); + sha512(outbuf, inbuf, inlen + 4); + memcpy(out, outbuf, outlen - i*SPX_SHA512_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded and state_seeded_512, which can then be + * reused in thash + **/ +void seed_state(spx_ctx *ctx) { + uint8_t block[SPX_SHA512_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = ctx->pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA512_BLOCK_BYTES; ++i) { + block[i] = 0; + } + /* block has been properly initialized for both SHA-256 and SHA-512 */ + + sha256_inc_init(ctx->state_seeded); + sha256_inc_blocks(ctx->state_seeded, block, 1); +#if SPX_SHA512 + sha512_inc_init(ctx->state_seeded_512); + sha512_inc_blocks(ctx->state_seeded_512, block, 1); +#endif +} diff --git a/Blastproof/keygen/sha2.h b/Blastproof/keygen/sha2.h new file mode 100644 index 0000000..732ab4b --- /dev/null +++ b/Blastproof/keygen/sha2.h @@ -0,0 +1,43 @@ +#ifndef SPX_SHA2_H +#define SPX_SHA2_H + +#include "params.h" + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ + +#define SPX_SHA512_BLOCK_BYTES 128 +#define SPX_SHA512_OUTPUT_BYTES 64 + +#if SPX_SHA256_OUTPUT_BYTES < SPX_N + #error Linking against SHA-256 with N larger than 32 bytes is not supported +#endif + +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void sha256_inc_init(uint8_t *state); +void sha256_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks); +void sha256_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen); +void sha256(uint8_t *out, const uint8_t *in, size_t inlen); + +void sha512_inc_init(uint8_t *state); +void sha512_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks); +void sha512_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen); +void sha512(uint8_t *out, const uint8_t *in, size_t inlen); + +#define mgf1_256 SPX_NAMESPACE(mgf1_256) +void mgf1_256(unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen); + +#define mgf1_512 SPX_NAMESPACE(mgf1_512) +void mgf1_512(unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen); + +#define seed_state SPX_NAMESPACE(seed_state) +void seed_state(spx_ctx *ctx); + + +#endif diff --git a/Blastproof/keygen/sha2.o b/Blastproof/keygen/sha2.o new file mode 100644 index 0000000..089cce5 Binary files /dev/null and b/Blastproof/keygen/sha2.o differ diff --git a/Blastproof/keygen/sha2_offsets.h b/Blastproof/keygen/sha2_offsets.h new file mode 100644 index 0000000..49f7e85 --- /dev/null +++ b/Blastproof/keygen/sha2_offsets.h @@ -0,0 +1,20 @@ +#ifndef SHA2_OFFSETS_H_ +#define SHA2_OFFSETS_H_ + +/* + * Offsets of various fields in the address structure when we use SHA2 as + * the Sphincs+ hash function + */ + +#define SPX_OFFSET_LAYER 0 /* The byte used to specify the Merkle tree layer */ +#define SPX_OFFSET_TREE 1 /* The start of the 8 byte field used to specify the tree */ +#define SPX_OFFSET_TYPE 9 /* The byte used to specify the hash type (reason) */ +#define SPX_OFFSET_KP_ADDR 10 /* The start of the 4 byte field used to specify the key pair address */ +#define SPX_OFFSET_CHAIN_ADDR 17 /* The byte used to specify the chain address (which Winternitz chain) */ +#define SPX_OFFSET_HASH_ADDR 21 /* The byte used to specify the hash address (where in the Winternitz chain) */ +#define SPX_OFFSET_TREE_HGT 17 /* The byte used to specify the height of this node in the FORS or Merkle tree */ +#define SPX_OFFSET_TREE_INDEX 18 /* The start of the 4 byte field used to specify the node in the FORS or Merkle tree */ + +#define SPX_SHA2 1 + +#endif /* SHA2_OFFSETS_H_ */ diff --git a/Blastproof/keygen/sha3.c b/Blastproof/keygen/sha3.c new file mode 100644 index 0000000..fb4ef05 --- /dev/null +++ b/Blastproof/keygen/sha3.c @@ -0,0 +1,190 @@ +// sha3.c +// 19-Nov-11 Markku-Juhani O. Saarinen + +// Revised 07-Aug-15 to match with official release of FIPS PUB 202 "SHA3" +// Revised 03-Sep-15 for portability + OpenSSL - style API + +#include "sha3.h" + +// update the state with given number of rounds + +void sha3_keccakf(uint64_t st[25]) +{ + // constants + const uint64_t keccakf_rndc[24] = { + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, + 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 + }; + const int keccakf_rotc[24] = { + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 + }; + const int keccakf_piln[24] = { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 + }; + + // variables + int i, j, r; + uint64_t t, bc[5]; + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + uint8_t *v; + + // endianess conversion. this is redundant on little-endian targets + for (i = 0; i < 25; i++) { + v = (uint8_t *) &st[i]; + st[i] = ((uint64_t) v[0]) | (((uint64_t) v[1]) << 8) | + (((uint64_t) v[2]) << 16) | (((uint64_t) v[3]) << 24) | + (((uint64_t) v[4]) << 32) | (((uint64_t) v[5]) << 40) | + (((uint64_t) v[6]) << 48) | (((uint64_t) v[7]) << 56); + } +#endif + + // actual iteration + for (r = 0; r < KECCAKF_ROUNDS; r++) { + + // Theta + for (i = 0; i < 5; i++) + bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20]; + + for (i = 0; i < 5; i++) { + t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); + for (j = 0; j < 25; j += 5) + st[j + i] ^= t; + } + + // Rho Pi + t = st[1]; + for (i = 0; i < 24; i++) { + j = keccakf_piln[i]; + bc[0] = st[j]; + st[j] = ROTL64(t, keccakf_rotc[i]); + t = bc[0]; + } + + // Chi + for (j = 0; j < 25; j += 5) { + for (i = 0; i < 5; i++) + bc[i] = st[j + i]; + for (i = 0; i < 5; i++) + st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5]; + } + + // Iota + st[0] ^= keccakf_rndc[r]; + } + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + // endianess conversion. this is redundant on little-endian targets + for (i = 0; i < 25; i++) { + v = (uint8_t *) &st[i]; + t = st[i]; + v[0] = t & 0xFF; + v[1] = (t >> 8) & 0xFF; + v[2] = (t >> 16) & 0xFF; + v[3] = (t >> 24) & 0xFF; + v[4] = (t >> 32) & 0xFF; + v[5] = (t >> 40) & 0xFF; + v[6] = (t >> 48) & 0xFF; + v[7] = (t >> 56) & 0xFF; + } +#endif +} + +// Initialize the context for SHA3 + +int sha3_init(sha3_ctx_t *c, int mdlen) +{ + int i; + + for (i = 0; i < 25; i++) + c->st.q[i] = 0; + c->mdlen = mdlen; + c->rsiz = 200 - 2 * mdlen; + c->pt = 0; + + return 1; +} + +// update state with more data + +int sha3_update(sha3_ctx_t *c, const void *data, size_t len) +{ + size_t i; + int j; + + j = c->pt; + for (i = 0; i < len; i++) { + c->st.b[j++] ^= ((const uint8_t *) data)[i]; + if (j >= c->rsiz) { + sha3_keccakf(c->st.q); + j = 0; + } + } + c->pt = j; + + return 1; +} + +// finalize and output a hash + +int sha3_final(void *md, sha3_ctx_t *c) +{ + int i; + + c->st.b[c->pt] ^= 0x06; + c->st.b[c->rsiz - 1] ^= 0x80; + sha3_keccakf(c->st.q); + + for (i = 0; i < c->mdlen; i++) { + ((uint8_t *) md)[i] = c->st.b[i]; + } + + return 1; +} + +// compute a SHA-3 hash (md) of given byte length from "in" + +void *sha3(const void *in, size_t inlen, void *md, int mdlen) +{ + sha3_ctx_t sha3; + + sha3_init(&sha3, mdlen); + sha3_update(&sha3, in, inlen); + sha3_final(md, &sha3); + + return md; +} + +// SHAKE128 and SHAKE256 extensible-output functionality + +void shake_xof(sha3_ctx_t *c) +{ + c->st.b[c->pt] ^= 0x1F; + c->st.b[c->rsiz - 1] ^= 0x80; + sha3_keccakf(c->st.q); + c->pt = 0; +} + +void shake_out(sha3_ctx_t *c, void *out, size_t len) +{ + size_t i; + int j; + + j = c->pt; + for (i = 0; i < len; i++) { + if (j >= c->rsiz) { + sha3_keccakf(c->st.q); + j = 0; + } + ((uint8_t *) out)[i] = c->st.b[j++]; + } + c->pt = j; +} diff --git a/Blastproof/keygen/sha3.h b/Blastproof/keygen/sha3.h new file mode 100644 index 0000000..4431c49 --- /dev/null +++ b/Blastproof/keygen/sha3.h @@ -0,0 +1,46 @@ +// sha3.h +// 19-Nov-11 Markku-Juhani O. Saarinen + +#ifndef SHA3_H +#define SHA3_H + +#include +#include + +#ifndef KECCAKF_ROUNDS +#define KECCAKF_ROUNDS 24 +#endif + +#ifndef ROTL64 +#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) +#endif + +// state context +typedef struct { + union { // state: + uint8_t b[200]; // 8-bit bytes + uint64_t q[25]; // 64-bit words + } st; + int pt, rsiz, mdlen; // these don't overflow +} sha3_ctx_t; + +// Compression function. +void sha3_keccakf(uint64_t st[25]); + +// OpenSSL - like interfece +int sha3_init(sha3_ctx_t *c, int mdlen); // mdlen = hash output in bytes +int sha3_update(sha3_ctx_t *c, const void *data, size_t len); +int sha3_final(void *md, sha3_ctx_t *c); // digest goes to md + +// compute a sha3 hash (md) of given byte length from "in" +void *sha3(const void *in, size_t inlen, void *md, int mdlen); + +// SHAKE128 and SHAKE256 extensible-output functions +#define shake128_init(c) sha3_init(c, 16) +#define shake256_init(c) sha3_init(c, 32) +#define shake_update sha3_update + +void shake_xof(sha3_ctx_t *c); +void shake_out(sha3_ctx_t *c, void *out, size_t len); + +#endif diff --git a/Blastproof/keygen/sha3.o b/Blastproof/keygen/sha3.o new file mode 100644 index 0000000..0fe0cf1 Binary files /dev/null and b/Blastproof/keygen/sha3.o differ diff --git a/Blastproof/keygen/sign.c b/Blastproof/keygen/sign.c new file mode 100644 index 0000000..a8e0c3c --- /dev/null +++ b/Blastproof/keygen/sign.c @@ -0,0 +1,287 @@ +#include +#include +#include + +#include "api.h" +#include "params.h" +#include "wots.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "address.h" +#include "randombytes.h" +#include "utils.h" +#include "merkle.h" + +/* + * Returns the length of a secret key, in bytes + */ +unsigned long long crypto_sign_secretkeybytes(void) +{ + return CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +unsigned long long crypto_sign_publickeybytes(void) +{ + return CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +unsigned long long crypto_sign_bytes(void) +{ + return CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +unsigned long long crypto_sign_seedbytes(void) +{ + return CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk, + const unsigned char *seed) +{ + spx_ctx ctx; + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2*SPX_N, SPX_N); + + memcpy(ctx.pub_seed, pk, SPX_N); + memcpy(ctx.sk_seed, sk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + /* Compute root node of the top-most subtree. */ + merkle_gen_root(sk + 3*SPX_N, &ctx); + + memcpy(pk + SPX_N, sk + 3*SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int crypto_sign_keypair(unsigned char *pk, unsigned char *sk) +{ + unsigned char seed[CRYPTO_SEEDBYTES]; + randombytes(seed, CRYPTO_SEEDBYTES); + crypto_sign_seed_keypair(pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int crypto_sign_signature(uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) +{ + spx_ctx ctx; + + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2*SPX_N; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + memcpy(ctx.sk_seed, sk, SPX_N); + memcpy(ctx.pub_seed, pk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + set_type(wots_addr, SPX_ADDR_TYPE_WOTS); + set_type(tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + gen_message_random(sig, sk_prf, optrand, m, mlen, &ctx); + + /* Derive the message digest and leaf index from R, PK and M. */ + hash_message(mhash, &tree, &idx_leaf, sig, pk, m, mlen, &ctx); + sig += SPX_N; + + set_tree_addr(wots_addr, tree); + set_keypair_addr(wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + fors_sign(sig, root, mhash, &ctx, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + set_layer_addr(tree_addr, i); + set_tree_addr(tree_addr, tree); + + copy_subtree_addr(wots_addr, tree_addr); + set_keypair_addr(wots_addr, idx_leaf); + + merkle_sign(sig, root, &ctx, wots_addr, tree_addr, idx_leaf); + sig += SPX_WOTS_BYTES + SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT)-1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int crypto_sign_verify(const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) +{ + spx_ctx ctx; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + memcpy(ctx.pub_seed, pk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + set_type(wots_addr, SPX_ADDR_TYPE_WOTS); + set_type(tree_addr, SPX_ADDR_TYPE_HASHTREE); + set_type(wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + hash_message(mhash, &tree, &idx_leaf, sig, pk, m, mlen, &ctx); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + set_tree_addr(wots_addr, tree); + set_keypair_addr(wots_addr, idx_leaf); + + fors_pk_from_sig(root, sig, mhash, &ctx, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + set_layer_addr(tree_addr, i); + set_tree_addr(tree_addr, tree); + + copy_subtree_addr(wots_addr, tree_addr); + set_keypair_addr(wots_addr, idx_leaf); + + copy_keypair_addr(wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + wots_pk_from_sig(wots_pk, sig, root, &ctx, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + thash(leaf, wots_pk, SPX_WOTS_LEN, &ctx, wots_pk_addr); + + /* Compute the root node of this subtree. */ + compute_root(root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + &ctx, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT)-1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N)) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int crypto_sign(unsigned char *sm, unsigned long long *smlen, + const unsigned char *m, unsigned long long mlen, + const unsigned char *sk) +{ + size_t siglen; + + crypto_sign_signature(sm, &siglen, m, (size_t)mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int crypto_sign_open(unsigned char *m, unsigned long long *mlen, + const unsigned char *sm, unsigned long long smlen, + const unsigned char *pk) +{ + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (crypto_sign_verify(sm, SPX_BYTES, sm + SPX_BYTES, (size_t)*mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/Blastproof/keygen/sign.o b/Blastproof/keygen/sign.o new file mode 100644 index 0000000..b1bdbab Binary files /dev/null and b/Blastproof/keygen/sign.o differ diff --git a/Blastproof/keygen/thash.h b/Blastproof/keygen/thash.h new file mode 100644 index 0000000..8687ccf --- /dev/null +++ b/Blastproof/keygen/thash.h @@ -0,0 +1,13 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include "context.h" +#include "params.h" + +#include + +#define thash SPX_NAMESPACE(thash) +void thash(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]); + +#endif diff --git a/Blastproof/keygen/thash_sha2_robust.c b/Blastproof/keygen/thash_sha2_robust.c new file mode 100644 index 0000000..67ca3da --- /dev/null +++ b/Blastproof/keygen/thash_sha2_robust.c @@ -0,0 +1,74 @@ +#include +#include + +#include "thash.h" +#include "address.h" +#include "params.h" +#include "utils.h" +#include "sha2.h" + +#if SPX_SHA512 +static void thash_512(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]); +#endif + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +void thash(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]) +{ +#if SPX_SHA512 + if (inblocks > 1) { + thash_512(out, in, inblocks, ctx, addr); + return; + } +#endif + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + SPX_VLA(uint8_t, bitmask, inblocks * SPX_N); + SPX_VLA(uint8_t, buf, SPX_N + SPX_SHA256_OUTPUT_BYTES + inblocks*SPX_N); + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, ctx->pub_seed, SPX_N); + memcpy(buf + SPX_N, addr, SPX_SHA256_ADDR_BYTES); + mgf1_256(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, ctx->state_seeded, 40 * sizeof(uint8_t)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + memcpy(out, outbuf, SPX_N); +} + +#if SPX_SHA512 +static void thash_512(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]) +{ + unsigned char outbuf[SPX_SHA512_OUTPUT_BYTES]; + SPX_VLA(uint8_t, bitmask, inblocks * SPX_N); + SPX_VLA(uint8_t, buf, SPX_N + SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + uint8_t sha2_state[72]; + unsigned int i; + + memcpy(buf, ctx->pub_seed, SPX_N); + memcpy(buf + SPX_N, addr, SPX_SHA256_ADDR_BYTES); + mgf1_512(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, ctx->state_seeded_512, 72 * sizeof(uint8_t)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha512_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + memcpy(out, outbuf, SPX_N); +} +#endif diff --git a/Blastproof/keygen/thash_sha2_robust.o b/Blastproof/keygen/thash_sha2_robust.o new file mode 100644 index 0000000..e45cbc8 Binary files /dev/null and b/Blastproof/keygen/thash_sha2_robust.o differ diff --git a/Blastproof/keygen/thash_sha2_simple.c b/Blastproof/keygen/thash_sha2_simple.c new file mode 100644 index 0000000..da58896 --- /dev/null +++ b/Blastproof/keygen/thash_sha2_simple.c @@ -0,0 +1,59 @@ +#include +#include + +#include "thash.h" +#include "address.h" +#include "params.h" +#include "utils.h" +#include "sha2.h" + +#if SPX_SHA512 +static void thash_512(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]); +#endif + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +void thash(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]) +{ +#if SPX_SHA512 + if (inblocks > 1) { + thash_512(out, in, inblocks, ctx, addr); + return; + } +#endif + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + uint8_t sha2_state[40]; + SPX_VLA(uint8_t, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, ctx->state_seeded, 40 * sizeof(uint8_t)); + + memcpy(buf, addr, SPX_SHA256_ADDR_BYTES); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + memcpy(out, outbuf, SPX_N); +} + +#if SPX_SHA512 +static void thash_512(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]) +{ + unsigned char outbuf[SPX_SHA512_OUTPUT_BYTES]; + uint8_t sha2_state[72]; + SPX_VLA(uint8_t, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, ctx->state_seeded_512, 72 * sizeof(uint8_t)); + + memcpy(buf, addr, SPX_SHA256_ADDR_BYTES); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha512_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + memcpy(out, outbuf, SPX_N); +} +#endif diff --git a/Blastproof/keygen/utils.c b/Blastproof/keygen/utils.c new file mode 100644 index 0000000..63d52ee --- /dev/null +++ b/Blastproof/keygen/utils.c @@ -0,0 +1,154 @@ +#include + +#include "utils.h" +#include "params.h" +#include "hash.h" +#include "thash.h" +#include "address.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void ull_to_bytes(unsigned char *out, unsigned int outlen, + unsigned long long in) +{ + int i; + + /* Iterate over out in decreasing order, for big-endianness. */ + for (i = (signed int)outlen - 1; i >= 0; i--) { + out[i] = in & 0xff; + in = in >> 8; + } +} + +void u32_to_bytes(unsigned char *out, uint32_t in) +{ + out[0] = (unsigned char)(in >> 24); + out[1] = (unsigned char)(in >> 16); + out[2] = (unsigned char)(in >> 8); + out[3] = (unsigned char)in; +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long bytes_to_ull(const unsigned char *in, unsigned int inlen) +{ + unsigned long long retval = 0; + unsigned int i; + + for (i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8*(inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void compute_root(unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const spx_ctx *ctx, uint32_t addr[8]) +{ + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } + else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + set_tree_height(addr, i + 1); + set_tree_index(addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + thash(buffer + SPX_N, buffer, 2, ctx, addr); + memcpy(buffer, auth_path, SPX_N); + } + else { + thash(buffer, buffer, 2, ctx, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + set_tree_height(addr, tree_height); + set_tree_index(addr, leaf_idx + idx_offset); + thash(root, buffer, 2, ctx, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void treehash(unsigned char *root, unsigned char *auth_path, const spx_ctx* ctx, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char* /* leaf */, + const spx_ctx* /* ctx */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) +{ + SPX_VLA(uint8_t, stack, (tree_height+1)*SPX_N); + SPX_VLA(unsigned int, heights, tree_height+1); + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset*SPX_N, ctx, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + set_tree_height(tree_addr, heights[offset - 1] + 1); + set_tree_index(tree_addr, + tree_idx + (idx_offset >> (heights[offset-1] + 1))); + /* Hash the top-most nodes from the stack together. */ + thash(stack + (offset - 2)*SPX_N, + stack + (offset - 2)*SPX_N, 2, ctx, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} diff --git a/Blastproof/keygen/utils.h b/Blastproof/keygen/utils.h new file mode 100644 index 0000000..b13502c --- /dev/null +++ b/Blastproof/keygen/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include +#include "params.h" +#include "context.h" + + +/* To support MSVC use alloca() instead of VLAs. See #20. */ +#ifdef _MSC_VER +/* MSVC defines _alloca in malloc.h */ +# include +/* Note: _malloca(), which is recommended over deprecated _alloca, + requires that you call _freea(). So we stick with _alloca */ +# define SPX_VLA(__t,__x,__s) __t *__x = (__t*)_alloca((__s)*sizeof(__t)) +#else +# define SPX_VLA(__t,__x,__s) __t __x[__s] +#endif + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +#define ull_to_bytes SPX_NAMESPACE(ull_to_bytes) +void ull_to_bytes(unsigned char *out, unsigned int outlen, + unsigned long long in); +#define u32_to_bytes SPX_NAMESPACE(u32_to_bytes) +void u32_to_bytes(unsigned char *out, uint32_t in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +#define bytes_to_ull SPX_NAMESPACE(bytes_to_ull) +unsigned long long bytes_to_ull(const unsigned char *in, unsigned int inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +#define compute_root SPX_NAMESPACE(compute_root) +void compute_root(unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const spx_ctx *ctx, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +#define treehash SPX_NAMESPACE(treehash) +void treehash(unsigned char *root, unsigned char *auth_path, + const spx_ctx* ctx, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char* /* leaf */, + const spx_ctx* ctx /* ctx */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + + +#endif diff --git a/Blastproof/keygen/utils.o b/Blastproof/keygen/utils.o new file mode 100644 index 0000000..b13ec2f Binary files /dev/null and b/Blastproof/keygen/utils.o differ diff --git a/Blastproof/keygen/utilsx1.c b/Blastproof/keygen/utilsx1.c new file mode 100644 index 0000000..f6a6700 --- /dev/null +++ b/Blastproof/keygen/utilsx1.c @@ -0,0 +1,100 @@ +#include + +#include "utils.h" +#include "utilsx1.h" +#include "params.h" +#include "thash.h" +#include "address.h" + +/* + * Generate the entire Merkle tree, computing the authentication path for + * leaf_idx, and the resulting root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE) + * + * This expects tree_addr to be initialized to the addr structures for the + * Merkle tree nodes + * + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + * + * This works by using the standard Merkle tree building algorithm, + */ +void treehashx1(unsigned char *root, unsigned char *auth_path, + const spx_ctx* ctx, + uint32_t leaf_idx, uint32_t idx_offset, + uint32_t tree_height, + void (*gen_leaf)( + unsigned char* /* Where to write the leaves */, + const spx_ctx* /* ctx */, + uint32_t idx, void *info), + uint32_t tree_addr[8], + void *info) +{ + /* This is where we keep the intermediate nodes */ + SPX_VLA(uint8_t, stack, tree_height*SPX_N); + + uint32_t idx; + uint32_t max_idx = (uint32_t)((1 << tree_height) - 1); + for (idx = 0;; idx++) { + unsigned char current[2*SPX_N]; /* Current logical node is at */ + /* index[SPX_N]. We do this to minimize the number of copies */ + /* needed during a thash */ + gen_leaf( ¤t[SPX_N], ctx, idx + idx_offset, + info ); + + /* Now combine the freshly generated right node with previously */ + /* generated left ones */ + uint32_t internal_idx_offset = idx_offset; + uint32_t internal_idx = idx; + uint32_t internal_leaf = leaf_idx; + uint32_t h; /* The height we are in the Merkle tree */ + for (h=0;; h++, internal_idx >>= 1, internal_leaf >>= 1) { + + /* Check if we hit the top of the tree */ + if (h == tree_height) { + /* We hit the root; return it */ + memcpy( root, ¤t[SPX_N], SPX_N ); + return; + } + + /* + * Check if the node we have is a part of the + * authentication path; if it is, write it out + */ + if ((internal_idx ^ internal_leaf) == 0x01) { + memcpy( &auth_path[ h * SPX_N ], + ¤t[SPX_N], + SPX_N ); + } + + /* + * Check if we're at a left child; if so, stop going up the stack + * Exception: if we've reached the end of the tree, keep on going + * (so we combine the last 4 nodes into the one root node in two + * more iterations) + */ + if ((internal_idx & 1) == 0 && idx < max_idx) { + break; + } + + /* Ok, we're at a right node */ + /* Now combine the left and right logical nodes together */ + + /* Set the address of the node we're creating. */ + internal_idx_offset >>= 1; + set_tree_height(tree_addr, h + 1); + set_tree_index(tree_addr, internal_idx/2 + internal_idx_offset ); + + unsigned char *left = &stack[h * SPX_N]; + memcpy( ¤t[0], left, SPX_N ); + thash( ¤t[1 * SPX_N], + ¤t[0 * SPX_N], + 2, ctx, tree_addr); + } + + /* We've hit a left child; save the current for when we get the */ + /* corresponding right right */ + memcpy( &stack[h * SPX_N], ¤t[SPX_N], SPX_N); + } +} diff --git a/Blastproof/keygen/utilsx1.h b/Blastproof/keygen/utilsx1.h new file mode 100644 index 0000000..a7fcf15 --- /dev/null +++ b/Blastproof/keygen/utilsx1.h @@ -0,0 +1,26 @@ +#ifndef SPX_UTILSX4_H +#define SPX_UTILSX4_H + +#include +#include "params.h" +#include "context.h" + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +#define treehashx1 SPX_NAMESPACE(treehashx1) +void treehashx1(unsigned char *root, unsigned char *auth_path, + const spx_ctx* ctx, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char* /* Where to write the leaf */, + const spx_ctx* /* ctx */, + uint32_t addr_idx, void *info), + uint32_t tree_addrx4[8], void *info); + +#endif diff --git a/Blastproof/keygen/utilsx1.o b/Blastproof/keygen/utilsx1.o new file mode 100644 index 0000000..56b5e0d Binary files /dev/null and b/Blastproof/keygen/utilsx1.o differ diff --git a/Blastproof/keygen/wots.c b/Blastproof/keygen/wots.c new file mode 100644 index 0000000..df83278 --- /dev/null +++ b/Blastproof/keygen/wots.c @@ -0,0 +1,112 @@ +#include +#include + +#include "utils.h" +#include "utilsx1.h" +#include "hash.h" +#include "thash.h" +#include "wots.h" +#include "wotsx1.h" +#include "address.h" +#include "params.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const spx_ctx *ctx, uint32_t addr[8]) +{ + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start+steps) && i < SPX_WOTS_W; i++) { + set_hash_addr(addr, i); + thash(out, out, 1, ctx, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const int out_len, + const unsigned char *input) +{ + int in = 0; + int out = 0; + unsigned char total; + int bits = 0; + int consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (total >> bits) & (SPX_WOTS_W - 1); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) +{ + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << ((8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)) % 8); + ull_to_bytes(csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +void chain_lengths(unsigned int *lengths, const unsigned char *msg) +{ + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void wots_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const spx_ctx *ctx, uint32_t addr[8]) +{ + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + set_chain_addr(addr, i); + gen_chain(pk + i*SPX_N, sig + i*SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], ctx, addr); + } +} diff --git a/Blastproof/keygen/wots.h b/Blastproof/keygen/wots.h new file mode 100644 index 0000000..7e77056 --- /dev/null +++ b/Blastproof/keygen/wots.h @@ -0,0 +1,25 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include + +#include "params.h" +#include "context.h" + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +#define wots_pk_from_sig SPX_NAMESPACE(wots_pk_from_sig) +void wots_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const spx_ctx *ctx, uint32_t addr[8]); + +/* + * Compute the chain lengths needed for a given message hash + */ +#define chain_lengths SPX_NAMESPACE(chain_lengths) +void chain_lengths(unsigned int *lengths, const unsigned char *msg); + +#endif diff --git a/Blastproof/keygen/wots.o b/Blastproof/keygen/wots.o new file mode 100644 index 0000000..35ada9f Binary files /dev/null and b/Blastproof/keygen/wots.o differ diff --git a/Blastproof/keygen/wotsx1.c b/Blastproof/keygen/wotsx1.c new file mode 100644 index 0000000..dfb3780 --- /dev/null +++ b/Blastproof/keygen/wotsx1.c @@ -0,0 +1,73 @@ +#include +#include + +#include "utils.h" +#include "hash.h" +#include "thash.h" +#include "wots.h" +#include "wotsx1.h" +#include "address.h" +#include "params.h" + +/* + * This generates a WOTS public key + * It also generates the WOTS signature if leaf_info indicates + * that we're signing with this WOTS key + */ +void wots_gen_leafx1(unsigned char *dest, + const spx_ctx *ctx, + uint32_t leaf_idx, void *v_info) { + struct leaf_info_x1 *info = v_info; + uint32_t *leaf_addr = info->leaf_addr; + uint32_t *pk_addr = info->pk_addr; + unsigned int i, k; + unsigned char pk_buffer[ SPX_WOTS_BYTES ]; + unsigned char *buffer; + uint32_t wots_k_mask; + + if (leaf_idx == info->wots_sign_leaf) { + /* We're traversing the leaf that's signing; generate the WOTS */ + /* signature */ + wots_k_mask = 0; + } else { + /* Nope, we're just generating pk's; turn off the signature logic */ + wots_k_mask = (uint32_t)~0; + } + + set_keypair_addr( leaf_addr, leaf_idx ); + set_keypair_addr( pk_addr, leaf_idx ); + + for (i = 0, buffer = pk_buffer; i < SPX_WOTS_LEN; i++, buffer += SPX_N) { + uint32_t wots_k = info->wots_steps[i] | wots_k_mask; /* Set wots_k to */ + /* the step if we're generating a signature, ~0 if we're not */ + + /* Start with the secret seed */ + set_chain_addr(leaf_addr, i); + set_hash_addr(leaf_addr, 0); + set_type(leaf_addr, SPX_ADDR_TYPE_WOTSPRF); + + prf_addr(buffer, ctx, leaf_addr); + + set_type(leaf_addr, SPX_ADDR_TYPE_WOTS); + + /* Iterate down the WOTS chain */ + for (k=0;; k++) { + /* Check if this is the value that needs to be saved as a */ + /* part of the WOTS signature */ + if (k == wots_k) { + memcpy( info->wots_sig + i * SPX_N, buffer, SPX_N ); + } + + /* Check if we hit the top of the chain */ + if (k == SPX_WOTS_W - 1) break; + + /* Iterate one step on the chain */ + set_hash_addr(leaf_addr, k); + + thash(buffer, buffer, 1, ctx, leaf_addr); + } + } + + /* Do the final thash to generate the public keys */ + thash(dest, pk_buffer, SPX_WOTS_LEN, ctx, pk_addr); +} diff --git a/Blastproof/keygen/wotsx1.h b/Blastproof/keygen/wotsx1.h new file mode 100644 index 0000000..1257f81 --- /dev/null +++ b/Blastproof/keygen/wotsx1.h @@ -0,0 +1,36 @@ +#if !defined( WOTSX1_H_ ) +#define WOTSX1_H_ + +#include + +/* + * This is here to provide an interface to the internal wots_gen_leafx1 + * routine. While this routine is not referenced in the package outside of + * wots.c, it is called from the stand-alone benchmark code to characterize + * the performance + */ +struct leaf_info_x1 { + unsigned char *wots_sig; + uint32_t wots_sign_leaf; /* The index of the WOTS we're using to sign */ + uint32_t *wots_steps; + uint32_t leaf_addr[8]; + uint32_t pk_addr[8]; +}; + +/* Macro to set the leaf_info to something 'benign', that is, it would */ +/* run with the same time as it does during the real signing process */ +/* Used only by the benchmark code */ +#define INITIALIZE_LEAF_INFO_X1(info, addr, step_buffer) { \ + info.wots_sig = 0; \ + info.wots_sign_leaf = ~0u; \ + info.wots_steps = step_buffer; \ + memcpy( &info.leaf_addr[0], addr, 32 ); \ + memcpy( &info.pk_addr[0], addr, 32 ); \ +} + +#define wots_gen_leafx1 SPX_NAMESPACE(wots_gen_leafx1) +void wots_gen_leafx1(unsigned char *dest, + const spx_ctx *ctx, + uint32_t leaf_idx, void *v_info); + +#endif /* WOTSX1_H_ */ diff --git a/Blastproof/keygen/wotsx1.o b/Blastproof/keygen/wotsx1.o new file mode 100644 index 0000000..f577190 Binary files /dev/null and b/Blastproof/keygen/wotsx1.o differ diff --git a/Blastproof/src/keycard.asm b/Blastproof/src/keycard.asm new file mode 100644 index 0000000..88a0c88 --- /dev/null +++ b/Blastproof/src/keycard.asm @@ -0,0 +1,12 @@ +; SPDX-License-Identifier: MPL-2.0 +BITS 64 +global keycard +keycard: + mov cr3, rbx + mov rsp, rax + and rsp, -16 + sub rsp, 8 + mov rax, rcx + jmp rax + cli + hlt diff --git a/Blastproof/src/libs/argon2/argon2.c b/Blastproof/src/libs/argon2/argon2.c new file mode 100644 index 0000000..8b5b798 --- /dev/null +++ b/Blastproof/src/libs/argon2/argon2.c @@ -0,0 +1,456 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + #include + #include +#include +#include +#include +#include +#include "argon2.h" +#include "encoding.h" +#include "core.h" + +#ifndef UINT32_MAX +#define UINT32_MAX 0xFFFFFFFFU +#endif + +const CHAR8 *argon2_type2string(argon2_type type, INTN uppercase) { + switch (type) { + case Argon2_d: + return uppercase ? "Argon2d" : "argon2d"; + case Argon2_i: + return uppercase ? "Argon2i" : "argon2i"; + case Argon2_id: + return uppercase ? "Argon2id" : "argon2id"; + } + + return NULL; +} + +INTN argon2_ctx(argon2_context *context, argon2_type type) { + /* 1. Validate all inputs */ + INTN result = validate_inputs(context); + UINT32 memory_blocks, segment_length; + argon2_instance_t instance; + + if (ARGON2_OK != result) { + return result; + } + + if (Argon2_d != type && Argon2_i != type && Argon2_id != type) { + return ARGON2_INCORRECT_TYPE; + } + + /* 2. Align memory size */ + /* Minimum memory_blocks = 8L blocks, where L is the number of lanes */ + memory_blocks = context->m_cost; + + if (memory_blocks < 2 * ARGON2_SYNC_POINTS * context->lanes) { + memory_blocks = 2 * ARGON2_SYNC_POINTS * context->lanes; + } + + segment_length = memory_blocks / (context->lanes * ARGON2_SYNC_POINTS); + /* Ensure that all segments have equal length */ + memory_blocks = segment_length * (context->lanes * ARGON2_SYNC_POINTS); + + instance.version = context->version; + instance.memory = NULL; + instance.passes = context->t_cost; + instance.memory_blocks = memory_blocks; + instance.segment_length = segment_length; + instance.lane_length = segment_length * ARGON2_SYNC_POINTS; + instance.lanes = context->lanes; + instance.threads = context->threads; + instance.type = type; + + if (instance.threads > instance.lanes) { + instance.threads = instance.lanes; + } + + /* 3. Initialization: Hashing inputs, allocating memory, filling first + * blocks + */ + result = initialize(&instance, context); + + if (ARGON2_OK != result) { + return result; + } + + /* 4. Filling memory */ + result = fill_memory_blocks(&instance); + + if (ARGON2_OK != result) { + return result; + } + /* 5. Finalization */ + finalize(context, &instance); + + return ARGON2_OK; +} + +INTN argon2_hash(const UINT32 t_cost, const UINT32 m_cost, + const UINT32 parallelism, const VOID *pwd, + const UINTN pwdlen, const VOID *salt, const UINTN saltlen, + VOID *hash, const UINTN hashlen, CHAR8 *encoded, + const UINTN encodedlen, argon2_type type, + const UINT32 version){ + + argon2_context context; + INTN result; + UINT8 *out; + + if (pwdlen > ARGON2_MAX_PWD_LENGTH) { + return ARGON2_PWD_TOO_LONG; + } + + if (saltlen > ARGON2_MAX_SALT_LENGTH) { + return ARGON2_SALT_TOO_LONG; + } + + if (hashlen > ARGON2_MAX_OUTLEN) { + return ARGON2_OUTPUT_TOO_LONG; + } + + if (hashlen < ARGON2_MIN_OUTLEN) { + return ARGON2_OUTPUT_TOO_SHORT; + } + + out = AllocatePool(hashlen); + if (!out) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + context.out = (UINT8 *)out; + context.outlen = (UINT8)hashlen; + context.pwd = CONST_CAST(UINT8 *)pwd; + context.pwdlen = (UINT32)pwdlen; + context.salt = CONST_CAST(UINT8 *)salt; + context.saltlen = (UINT32)saltlen; + context.secret = NULL; + context.secretlen = 0; + context.ad = NULL; + context.adlen = 0; + context.t_cost = t_cost; + context.m_cost = m_cost; + context.lanes = parallelism; + context.threads = parallelism; + context.allocate_cbk = NULL; + context.free_cbk = NULL; + context.flags = ARGON2_DEFAULT_FLAGS; + context.version = version; + + result = argon2_ctx(&context, type); + + if (result != ARGON2_OK) { + clear_internal_memory(out, hashlen); + FreePool(out); + return result; + } + + /* if raw hash requested, write it */ + if (hash) { + CopyMem(hash, out, hashlen); + } + + /* if encoding requested, write it */ + if (encoded && encodedlen) { + if (encode_string(encoded, encodedlen, &context, type) != ARGON2_OK) { + clear_internal_memory(out, hashlen); /* wipe buffers if error */ + clear_internal_memory(encoded, encodedlen); + FreePool(out); + return ARGON2_ENCODING_FAIL; + } + } + clear_internal_memory(out, hashlen); + FreePool(out); + + return ARGON2_OK; +} + +INTN argon2i_hash_encoded(const UINT32 t_cost, const UINT32 m_cost, + const UINT32 parallelism, const VOID *pwd, + const UINTN pwdlen, const VOID *salt, + const UINTN saltlen, const UINTN hashlen, + CHAR8 *encoded, const UINTN encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_i, + ARGON2_VERSION_NUMBER); +} + +INTN argon2i_hash_raw(const UINT32 t_cost, const UINT32 m_cost, + const UINT32 parallelism, const VOID *pwd, + const UINTN pwdlen, const VOID *salt, + const UINTN saltlen, VOID *hash, const UINTN hashlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_i, ARGON2_VERSION_NUMBER); +} + +INTN argon2d_hash_encoded(const UINT32 t_cost, const UINT32 m_cost, + const UINT32 parallelism, const VOID *pwd, + const UINTN pwdlen, const VOID *salt, + const UINTN saltlen, const UINTN hashlen, + CHAR8 *encoded, const UINTN encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_d, + ARGON2_VERSION_NUMBER); +} + +INTN argon2d_hash_raw(const UINT32 t_cost, const UINT32 m_cost, + const UINT32 parallelism, const VOID *pwd, + const UINTN pwdlen, const VOID *salt, + const UINTN saltlen, VOID *hash, const UINTN hashlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER); +} + +INTN argon2id_hash_encoded(const UINT32 t_cost, const UINT32 m_cost, + const UINT32 parallelism, const VOID *pwd, + const UINTN pwdlen, const VOID *salt, + const UINTN saltlen, const UINTN hashlen, + CHAR8 *encoded, const UINTN encodedlen) { + + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + NULL, hashlen, encoded, encodedlen, Argon2_id, + ARGON2_VERSION_NUMBER); +} + +INTN argon2id_hash_raw(const UINT32 t_cost, const UINT32 m_cost, + const UINT32 parallelism, const VOID *pwd, + const UINTN pwdlen, const VOID *salt, + const UINTN saltlen, VOID *hash, const UINTN hashlen) { + return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, + hash, hashlen, NULL, 0, Argon2_id, + ARGON2_VERSION_NUMBER); +} + +static INTN argon2_compare(const UINT8 *b1, const UINT8 *b2, UINTN len) { + UINT8 d = 0U; + + for (INTN i = 0U; i < len; i++) { + d |= b1[i] ^ b2[i]; + } + return (int)((1 & ((d - 1) >> 8)) - 1); +} + +INTN argon2_verify(const CHAR8 *encoded, const VOID *pwd, const UINTN pwdlen, + argon2_type type) { + + argon2_context ctx; + UINT8 *desired_result = NULL; + + INTN ret = ARGON2_OK; + + UINTN encoded_len; + UINT32 max_field_len; + + if (pwdlen > ARGON2_MAX_PWD_LENGTH) { + return ARGON2_PWD_TOO_LONG; + } + + if (encoded == NULL) { + return ARGON2_DECODING_FAIL; + } + + encoded_len = AsciiStrLen(encoded); + if (encoded_len > UINT32_MAX) { + return ARGON2_DECODING_FAIL; + } + + /* No field can be longer than the encoded length */ + max_field_len = (UINT32)encoded_len; + + ctx.saltlen = max_field_len; + ctx.outlen = max_field_len; + + ctx.salt = AllocatePool(ctx.saltlen); + ctx.out = AllocatePool(ctx.outlen); + if (!ctx.salt || !ctx.out) { + ret = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + ctx.pwd = (UINT8 *)pwd; + ctx.pwdlen = (UINT32)pwdlen; + + ret = decode_string(&ctx, encoded, type); + if (ret != ARGON2_OK) { + goto fail; + } + + /* Set aside the desired result, and get a new buffer. */ + desired_result = ctx.out; + ctx.out = AllocatePool(ctx.outlen); + if (!ctx.out) { + ret = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + ret = argon2_verify_ctx(&ctx, (CHAR8 *)desired_result, type); + if (ret != ARGON2_OK) { + goto fail; + } + +fail: + FreePool(ctx.salt); + FreePool(ctx.out); + FreePool(desired_result); + + return ret; +} + +INTN argon2i_verify(const CHAR8 *encoded, const VOID *pwd, const UINTN pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_i); +} + +INTN argon2d_verify(const CHAR8 *encoded, const VOID *pwd, const UINTN pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_d); +} + +INTN argon2id_verify(const CHAR8 *encoded, const VOID *pwd, const UINTN pwdlen) { + + return argon2_verify(encoded, pwd, pwdlen, Argon2_id); +} + +INTN argon2d_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_d); +} + +INTN argon2i_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_i); +} + +INTN argon2id_ctx(argon2_context *context) { + return argon2_ctx(context, Argon2_id); +} + +INTN argon2_verify_ctx(argon2_context *context, const CHAR8 *hash, + argon2_type type) { + INTN ret = argon2_ctx(context, type); + if (ret != ARGON2_OK) { + return ret; + } + + if (argon2_compare((UINT8 *)hash, context->out, context->outlen)) { + return ARGON2_VERIFY_MISMATCH; + } + + return ARGON2_OK; +} + +INTN argon2d_verify_ctx(argon2_context *context, const CHAR8 *hash) { + return argon2_verify_ctx(context, hash, Argon2_d); +} + +INTN argon2i_verify_ctx(argon2_context *context, const CHAR8 *hash) { + return argon2_verify_ctx(context, hash, Argon2_i); +} + +INTN argon2id_verify_ctx(argon2_context *context, const CHAR8 *hash) { + return argon2_verify_ctx(context, hash, Argon2_id); +} + +const CHAR8 *argon2_error_message(INTN error_code) { + switch (error_code) { + case ARGON2_OK: + return "OK"; + case ARGON2_OUTPUT_PTR_NULL: + return "Output pointer is NULL"; + case ARGON2_OUTPUT_TOO_SHORT: + return "Output is too short"; + case ARGON2_OUTPUT_TOO_LONG: + return "Output is too long"; + case ARGON2_PWD_TOO_SHORT: + return "Password is too short"; + case ARGON2_PWD_TOO_LONG: + return "Password is too long"; + case ARGON2_SALT_TOO_SHORT: + return "Salt is too short"; + case ARGON2_SALT_TOO_LONG: + return "Salt is too long"; + case ARGON2_AD_TOO_SHORT: + return "Associated data is too short"; + case ARGON2_AD_TOO_LONG: + return "Associated data is too long"; + case ARGON2_SECRET_TOO_SHORT: + return "Secret is too short"; + case ARGON2_SECRET_TOO_LONG: + return "Secret is too long"; + case ARGON2_TIME_TOO_SMALL: + return "Time cost is too small"; + case ARGON2_TIME_TOO_LARGE: + return "Time cost is too large"; + case ARGON2_MEMORY_TOO_LITTLE: + return "Memory cost is too small"; + case ARGON2_MEMORY_TOO_MUCH: + return "Memory cost is too large"; + case ARGON2_LANES_TOO_FEW: + return "Too few lanes"; + case ARGON2_LANES_TOO_MANY: + return "Too many lanes"; + case ARGON2_PWD_PTR_MISMATCH: + return "Password pointer is NULL, but password length is not 0"; + case ARGON2_SALT_PTR_MISMATCH: + return "Salt pointer is NULL, but salt length is not 0"; + case ARGON2_SECRET_PTR_MISMATCH: + return "Secret pointer is NULL, but secret length is not 0"; + case ARGON2_AD_PTR_MISMATCH: + return "Associated data pointer is NULL, but ad length is not 0"; + case ARGON2_MEMORY_ALLOCATION_ERROR: + return "Memory allocation error"; + case ARGON2_FREE_MEMORY_CBK_NULL: + return "The free memory callback is NULL"; + case ARGON2_ALLOCATE_MEMORY_CBK_NULL: + return "The allocate memory callback is NULL"; + case ARGON2_INCORRECT_PARAMETER: + return "Argon2_Context context is NULL"; + case ARGON2_INCORRECT_TYPE: + return "There is no such version of Argon2"; + case ARGON2_OUT_PTR_MISMATCH: + return "Output pointer mismatch"; + case ARGON2_THREADS_TOO_FEW: + return "Not enough threads"; + case ARGON2_THREADS_TOO_MANY: + return "Too many threads"; + case ARGON2_MISSING_ARGS: + return "Missing arguments"; + case ARGON2_ENCODING_FAIL: + return "Encoding failed"; + case ARGON2_DECODING_FAIL: + return "Decoding failed"; + case ARGON2_THREAD_FAIL: + return "Threading failure"; + case ARGON2_DECODING_LENGTH_FAIL: + return "Some of encoded parameters are too long or too short"; + case ARGON2_VERIFY_MISMATCH: + return "The password does not match the supplied hash"; + default: + return "Unknown error code"; + } +} + +UINTN argon2_encodedlen(UINT32 t_cost, UINT32 m_cost, UINT32 parallelism, + UINT32 saltlen, UINT32 hashlen, argon2_type type) { + return AsciiStrLen("$$v=$m=,t=,p=$$") + AsciiStrLen(argon2_type2string(type, 0)) + + numlen(t_cost) + numlen(m_cost) + numlen(parallelism) + + b64len(saltlen) + b64len(hashlen) + numlen(ARGON2_VERSION_NUMBER) + 1; +} diff --git a/Blastproof/src/libs/argon2/argon2.h b/Blastproof/src/libs/argon2/argon2.h new file mode 100644 index 0000000..1d7ae3b --- /dev/null +++ b/Blastproof/src/libs/argon2/argon2.h @@ -0,0 +1,447 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_H +#define ARGON2_H + +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef UINT32_C +#define UINT32_C(x) (x##U) +#endif + +#ifndef UINT64_C +#define UINT64_C(x) (x##ULL) +#endif + +/* Symbols visibility control */ +#ifdef A2_VISCTL +#define ARGON2_PUBLIC __attribute__((visibility("default"))) +#define ARGON2_LOCAL __attribute__ ((visibility ("hidden"))) +#elif defined(_MSC_VER) +#define ARGON2_PUBLIC __declspec(dllexport) +#define ARGON2_LOCAL +#else +#define ARGON2_PUBLIC +#define ARGON2_LOCAL +#endif + +/* + * Argon2 input parameter restrictions + */ + +/* Minimum and maximum number of lanes (degree of parallelism) */ +#define ARGON2_MIN_LANES UINT32_C(1) +#define ARGON2_MAX_LANES UINT32_C(0xFFFFFF) + +/* Minimum and maximum number of threads */ +#define ARGON2_MIN_THREADS UINT32_C(1) +#define ARGON2_MAX_THREADS UINT32_C(0xFFFFFF) + +/* Number of synchronization points between lanes per pass */ +#define ARGON2_SYNC_POINTS UINT32_C(4) + +/* Minimum and maximum digest size in bytes */ +#define ARGON2_MIN_OUTLEN UINT32_C(4) +#define ARGON2_MAX_OUTLEN UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */ +#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */ + +#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b)) +/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */ +#define ARGON2_MAX_MEMORY_BITS \ + ARGON2_MIN(UINT32_C(32), (sizeof(VOID *) * CHAR_BIT - 10 - 1)) +#define ARGON2_MAX_MEMORY \ + ARGON2_MIN(UINT32_C(0xFFFFFFFF), UINT64_C(1) << ARGON2_MAX_MEMORY_BITS) + +/* Minimum and maximum number of passes */ +#define ARGON2_MIN_TIME UINT32_C(1) +#define ARGON2_MAX_TIME UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum password length in bytes */ +#define ARGON2_MIN_PWD_LENGTH UINT32_C(0) +#define ARGON2_MAX_PWD_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum associated data length in bytes */ +#define ARGON2_MIN_AD_LENGTH UINT32_C(0) +#define ARGON2_MAX_AD_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum salt length in bytes */ +#define ARGON2_MIN_SALT_LENGTH UINT32_C(8) +#define ARGON2_MAX_SALT_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum key length in bytes */ +#define ARGON2_MIN_SECRET UINT32_C(0) +#define ARGON2_MAX_SECRET UINT32_C(0xFFFFFFFF) + +/* Flags to determine which fields are securely wiped (default = no wipe). */ +#define ARGON2_DEFAULT_FLAGS UINT32_C(0) +#define ARGON2_FLAG_CLEAR_PASSWORD (UINT32_C(1) << 0) +#define ARGON2_FLAG_CLEAR_SECRET (UINT32_C(1) << 1) + +/* Global flag to determine if we are wiping internal memory buffers. This flag + * is defined in core.c and defaults to 1 (wipe internal memory). */ +extern INTN FLAG_clear_internal_memory; + +/* Error codes */ +typedef enum Argon2_ErrorCodes { + ARGON2_OK = 0, + + ARGON2_OUTPUT_PTR_NULL = -1, + + ARGON2_OUTPUT_TOO_SHORT = -2, + ARGON2_OUTPUT_TOO_LONG = -3, + + ARGON2_PWD_TOO_SHORT = -4, + ARGON2_PWD_TOO_LONG = -5, + + ARGON2_SALT_TOO_SHORT = -6, + ARGON2_SALT_TOO_LONG = -7, + + ARGON2_AD_TOO_SHORT = -8, + ARGON2_AD_TOO_LONG = -9, + + ARGON2_SECRET_TOO_SHORT = -10, + ARGON2_SECRET_TOO_LONG = -11, + + ARGON2_TIME_TOO_SMALL = -12, + ARGON2_TIME_TOO_LARGE = -13, + + ARGON2_MEMORY_TOO_LITTLE = -14, + ARGON2_MEMORY_TOO_MUCH = -15, + + ARGON2_LANES_TOO_FEW = -16, + ARGON2_LANES_TOO_MANY = -17, + + ARGON2_PWD_PTR_MISMATCH = -18, /* NULL ptr with non-zero length */ + ARGON2_SALT_PTR_MISMATCH = -19, /* NULL ptr with non-zero length */ + ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */ + ARGON2_AD_PTR_MISMATCH = -21, /* NULL ptr with non-zero length */ + + ARGON2_MEMORY_ALLOCATION_ERROR = -22, + + ARGON2_FREE_MEMORY_CBK_NULL = -23, + ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24, + + ARGON2_INCORRECT_PARAMETER = -25, + ARGON2_INCORRECT_TYPE = -26, + + ARGON2_OUT_PTR_MISMATCH = -27, + + ARGON2_THREADS_TOO_FEW = -28, + ARGON2_THREADS_TOO_MANY = -29, + + ARGON2_MISSING_ARGS = -30, + + ARGON2_ENCODING_FAIL = -31, + + ARGON2_DECODING_FAIL = -32, + + ARGON2_THREAD_FAIL = -33, + + ARGON2_DECODING_LENGTH_FAIL = -34, + + ARGON2_VERIFY_MISMATCH = -35 +} argon2_error_codes; + +/* Memory allocator types --- for external allocation */ +typedef INTN (*allocate_fptr)(UINT8 **memory, UINTN bytes_to_allocate); +typedef VOID (*deallocate_fptr)(UINT8 *memory, UINTN bytes_to_allocate); + +/* Argon2 external data structures */ + +/* + ***** + * Context: structure to hold Argon2 inputs: + * output array and its length, + * password and its length, + * salt and its length, + * secret and its length, + * associated data and its length, + * number of passes, amount of used memory (in KBytes, can be rounded up a bit) + * number of parallel threads that will be run. + * All the parameters above affect the output hash value. + * Additionally, two function pointers can be provided to allocate and + * deallocate the memory (if NULL, memory will be allocated internally). + * Also, three flags indicate whether to erase password, secret as soon as they + * are pre-hashed (and thus not needed anymore), and the entire memory + ***** + * Simplest situation: you have output array out[8], password is stored in + * pwd[32], salt is stored in salt[16], you do not have keys nor associated + * data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with + * 4 parallel lanes. + * You want to erase the password, but you're OK with last pass not being + * erased. You want to use the default memory allocator. + * Then you initialize: + Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false) + */ +typedef struct Argon2_Context { + UINT8 *out; /* output array */ + UINT32 outlen; /* digest length */ + + UINT8 *pwd; /* password array */ + UINT32 pwdlen; /* password length */ + + UINT8 *salt; /* salt array */ + UINT32 saltlen; /* salt length */ + + UINT8 *secret; /* key array */ + UINT32 secretlen; /* key length */ + + UINT8 *ad; /* associated data array */ + UINT32 adlen; /* associated data length */ + + UINT32 t_cost; /* number of passes */ + UINT32 m_cost; /* amount of memory requested (KB) */ + UINT32 lanes; /* number of lanes */ + UINT32 threads; /* maximum number of threads */ + + UINT32 version; /* version number */ + + allocate_fptr allocate_cbk; /* pointer to memory allocator */ + deallocate_fptr free_cbk; /* pointer to memory deallocator */ + + UINT32 flags; /* array of bool options */ +} argon2_context; + +/* Argon2 primitive type */ +typedef enum Argon2_type { + Argon2_d = 0, + Argon2_i = 1, + Argon2_id = 2 +} argon2_type; + +/* Version of the algorithm */ +typedef enum Argon2_version { + ARGON2_VERSION_10 = 0x10, + ARGON2_VERSION_13 = 0x13, + ARGON2_VERSION_NUMBER = ARGON2_VERSION_13 +} argon2_version; + +/* + * Function that gives the string representation of an argon2_type. + * @param type The argon2_type that we want the string for + * @param uppercase Whether the string should have the first letter uppercase + * @return NULL if invalid type, otherwise the string representation. + */ +ARGON2_PUBLIC const CHAR8 *argon2_type2string(argon2_type type, INTN uppercase); + +/* + * Function that performs memory-hard hashing with certain degree of parallelism + * @param context Pointer to the Argon2 internal structure + * @return Error code if smth is wrong, ARGON2_OK otherwise + */ +ARGON2_PUBLIC INTN argon2_ctx(argon2_context *context, argon2_type type); + +/** + * Hashes a password with Argon2i, producing an encoded hash + * @param t_cost Number of iterations + * @param m_cost Sets memory usage to m_cost kibibytes + * @param parallelism Number of threads and compute lanes + * @param pwd Pointer to password + * @param pwdlen Password size in bytes + * @param salt Pointer to salt + * @param saltlen Salt size in bytes + * @param hashlen Desired length of the hash in bytes + * @param encoded Buffer where to write the encoded hash + * @param encodedlen Size of the buffer (thus max size of the encoded hash) + * @pre Different parallelism levels will give different results + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC INTN argon2i_hash_encoded(const UINT32 t_cost, + const UINT32 m_cost, + const UINT32 parallelism, + const VOID *pwd, const UINTN pwdlen, + const VOID *salt, const UINTN saltlen, + const UINTN hashlen, CHAR8 *encoded, + const UINTN encodedlen); + +/** + * Hashes a password with Argon2i, producing a raw hash at @hash + * @param t_cost Number of iterations + * @param m_cost Sets memory usage to m_cost kibibytes + * @param parallelism Number of threads and compute lanes + * @param pwd Pointer to password + * @param pwdlen Password size in bytes + * @param salt Pointer to salt + * @param saltlen Salt size in bytes + * @param hash Buffer where to write the raw hash - updated by the function + * @param hashlen Desired length of the hash in bytes + * @pre Different parallelism levels will give different results + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC INTN argon2i_hash_raw(const UINT32 t_cost, const UINT32 m_cost, + const UINT32 parallelism, const VOID *pwd, + const UINTN pwdlen, const VOID *salt, + const UINTN saltlen, VOID *hash, + const UINTN hashlen); + +ARGON2_PUBLIC INTN argon2d_hash_encoded(const UINT32 t_cost, + const UINT32 m_cost, + const UINT32 parallelism, + const VOID *pwd, const UINTN pwdlen, + const VOID *salt, const UINTN saltlen, + const UINTN hashlen, CHAR8 *encoded, + const UINTN encodedlen); + +ARGON2_PUBLIC INTN argon2d_hash_raw(const UINT32 t_cost, const UINT32 m_cost, + const UINT32 parallelism, const VOID *pwd, + const UINTN pwdlen, const VOID *salt, + const UINTN saltlen, VOID *hash, + const UINTN hashlen); + +ARGON2_PUBLIC INTN argon2id_hash_encoded(const UINT32 t_cost, + const UINT32 m_cost, + const UINT32 parallelism, + const VOID *pwd, const UINTN pwdlen, + const VOID *salt, const UINTN saltlen, + const UINTN hashlen, CHAR8 *encoded, + const UINTN encodedlen); + +ARGON2_PUBLIC INTN argon2id_hash_raw(const UINT32 t_cost, + const UINT32 m_cost, + const UINT32 parallelism, const VOID *pwd, + const UINTN pwdlen, const VOID *salt, + const UINTN saltlen, VOID *hash, + const UINTN hashlen); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC INTN argon2_hash(const UINT32 t_cost, const UINT32 m_cost, + const UINT32 parallelism, const VOID *pwd, + const UINTN pwdlen, const VOID *salt, + const UINTN saltlen, VOID *hash, + const UINTN hashlen, CHAR8 *encoded, + const UINTN encodedlen, argon2_type type, + const UINT32 version); + +/** + * Verifies a password against an encoded string + * Encoded string is restricted as in validate_inputs() + * @param encoded String encoding parameters, salt, hash + * @param pwd Pointer to password + * @pre Returns ARGON2_OK if successful + */ +ARGON2_PUBLIC INTN argon2i_verify(const CHAR8 *encoded, const VOID *pwd, + const UINTN pwdlen); + +ARGON2_PUBLIC INTN argon2d_verify(const CHAR8 *encoded, const VOID *pwd, + const UINTN pwdlen); + +ARGON2_PUBLIC INTN argon2id_verify(const CHAR8 *encoded, const VOID *pwd, + const UINTN pwdlen); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC INTN argon2_verify(const CHAR8 *encoded, const VOID *pwd, + const UINTN pwdlen, argon2_type type); + +/** + * Argon2d: Version of Argon2 that picks memory blocks depending + * on the password and salt. Only for side-channel-free + * environment!! + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC INTN argon2d_ctx(argon2_context *context); + +/** + * Argon2i: Version of Argon2 that picks memory blocks + * independent on the password and salt. Good for side-channels, + * but worse w.r.t. tradeoff attacks if only one pass is used. + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC INTN argon2i_ctx(argon2_context *context); + +/** + * Argon2id: Version of Argon2 where the first half-pass over memory is + * password-independent, the rest are password-dependent (on the password and + * salt). OK against side channels (they reduce to 1/2-pass Argon2i), and + * better with w.r.t. tradeoff attacks (similar to Argon2d). + ***** + * @param context Pointer to current Argon2 context + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC INTN argon2id_ctx(argon2_context *context); + +/** + * Verify if a given password is correct for Argon2d hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC INTN argon2d_verify_ctx(argon2_context *context, const CHAR8 *hash); + +/** + * Verify if a given password is correct for Argon2i hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC INTN argon2i_verify_ctx(argon2_context *context, const CHAR8 *hash); + +/** + * Verify if a given password is correct for Argon2id hashing + * @param context Pointer to current Argon2 context + * @param hash The password hash to verify. The length of the hash is + * specified by the context outlen member + * @return Zero if successful, a non zero error code otherwise + */ +ARGON2_PUBLIC INTN argon2id_verify_ctx(argon2_context *context, + const CHAR8 *hash); + +/* generic function underlying the above ones */ +ARGON2_PUBLIC INTN argon2_verify_ctx(argon2_context *context, const CHAR8 *hash, + argon2_type type); + +/** + * Get the associated error message for given error code + * @return The error message associated with the given error code + */ +ARGON2_PUBLIC const CHAR8 *argon2_error_message(INTN error_code); + +/** + * Returns the encoded hash length for the given input parameters + * @param t_cost Number of iterations + * @param m_cost Memory usage in kibibytes + * @param parallelism Number of threads; used to compute lanes + * @param saltlen Salt size in bytes + * @param hashlen Hash size in bytes + * @param type The argon2_type that we want the encoded length for + * @return The encoded hash length in bytes + */ +ARGON2_PUBLIC UINTN argon2_encodedlen(UINT32 t_cost, UINT32 m_cost, + UINT32 parallelism, UINT32 saltlen, + UINT32 hashlen, argon2_type type); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/Blastproof/src/libs/argon2/blake2-impl.h b/Blastproof/src/libs/argon2/blake2-impl.h new file mode 100644 index 0000000..a3fa5b1 --- /dev/null +++ b/Blastproof/src/libs/argon2/blake2-impl.h @@ -0,0 +1,158 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + #include + #include + #include + #include + #include + +#ifndef PORTABLE_BLAKE2_IMPL_H +#define PORTABLE_BLAKE2_IMPL_H + +#ifdef _WIN32 +#define BLAKE2_INLINE __inline +#elif defined(__GNUC__) || defined(__clang__) +#define BLAKE2_INLINE __inline__ +#else +#define BLAKE2_INLINE +#endif + +/* Argon2 Team - Begin Code */ +/* + Not an exhaustive list, but should cover the majority of modern platforms + Additionally, the code will always be correct---this is only a performance + tweak. +*/ +#if (defined(__BYTE_ORDER__) && \ + (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \ + defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \ + defined(_M_ARM) +#define NATIVE_LITTLE_ENDIAN +#endif +/* Argon2 Team - End Code */ + +static BLAKE2_INLINE UINT32 load32(const VOID *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + UINT32 w; + CopyMem(&w, src, sizeof w); + return w; +#else + const UINT8 *p = (const UINT8 *)src; + UINT32 w = *p++; + w |= (UINT32)(*p++) << 8; + w |= (UINT32)(*p++) << 16; + w |= (UINT32)(*p++) << 24; + return w; +#endif +} + +static BLAKE2_INLINE UINT64 load64(const VOID *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + UINT64 w; + CopyMem(&w, src, sizeof w); + return w; +#else + const UINT8 *p = (const UINT8 *)src; + UINT64 w = *p++; + w |= (UINT64)(*p++) << 8; + w |= (UINT64)(*p++) << 16; + w |= (UINT64)(*p++) << 24; + w |= (UINT64)(*p++) << 32; + w |= (UINT64)(*p++) << 40; + w |= (UINT64)(*p++) << 48; + w |= (UINT64)(*p++) << 56; + return w; +#endif +} + +static BLAKE2_INLINE VOID store32(VOID *dst, UINT32 w) { +#if defined(NATIVE_LITTLE_ENDIAN) + CopyMem(dst, &w, sizeof w); +#else + UINT8 *p = (UINT8 *)dst; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; +#endif +} + +static BLAKE2_INLINE VOID store64(VOID *dst, UINT64 w) { +#if defined(NATIVE_LITTLE_ENDIAN) + CopyMem(dst, &w, sizeof w); +#else + UINT8 *p = (UINT8 *)dst; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; +#endif +} + +static BLAKE2_INLINE UINT64 load48(const VOID *src) { + const UINT8 *p = (const UINT8 *)src; + UINT64 w = *p++; + w |= (UINT64)(*p++) << 8; + w |= (UINT64)(*p++) << 16; + w |= (UINT64)(*p++) << 24; + w |= (UINT64)(*p++) << 32; + w |= (UINT64)(*p++) << 40; + return w; +} + +static BLAKE2_INLINE VOID store48(VOID *dst, UINT64 w) { + UINT8 *p = (UINT8 *)dst; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; + w >>= 8; + *p++ = (UINT8)w; +} + +static BLAKE2_INLINE UINT32 rotr32(const UINT32 w, const UINTN c) { + return (w >> c) | (w << (32 - c)); +} + +static BLAKE2_INLINE UINT64 rotr64(const UINT64 w, const UINTN c) { + return (w >> c) | (w << (64 - c)); +} + +VOID clear_internal_memory(VOID *v, UINTN n); + +#endif diff --git a/Blastproof/src/libs/argon2/blake2.h b/Blastproof/src/libs/argon2/blake2.h new file mode 100644 index 0000000..05e5895 --- /dev/null +++ b/Blastproof/src/libs/argon2/blake2.h @@ -0,0 +1,97 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef PORTABLE_BLAKE2_H +#define PORTABLE_BLAKE2_H +#include +#include +#include +#include +#include +#include "argon2.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +enum blake2b_constant { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 +}; + +#pragma pack(push, 1) +typedef struct __blake2b_param { + UINT8 digest_length; /* 1 */ + UINT8 key_length; /* 2 */ + UINT8 fanout; /* 3 */ + UINT8 depth; /* 4 */ + UINT32 leaf_length; /* 8 */ + UINT64 node_offset; /* 16 */ + UINT8 node_depth; /* 17 */ + UINT8 inner_length; /* 18 */ + UINT8 reserved[14]; /* 32 */ + UINT8 salt[BLAKE2B_SALTBYTES]; /* 48 */ + UINT8 personal[BLAKE2B_PERSONALBYTES]; /* 64 */ +} blake2b_param; +#pragma pack(pop) + +typedef struct __blake2b_state { + UINT64 h[8]; + UINT64 t[2]; + UINT64 f[2]; + UINT8 buf[BLAKE2B_BLOCKBYTES]; + UINTN buflen; + UINTN outlen; + UINT8 last_node; +} blake2b_state; + +/* Ensure param structs have not been wrongly padded */ +/* Poor man's static_assert */ +enum { + blake2_size_check_0 = 1 / !!(CHAR_BIT == 8), + blake2_size_check_2 = + 1 / !!(sizeof(blake2b_param) == sizeof(UINT64) * CHAR_BIT) +}; + +/* Streaming API */ +ARGON2_LOCAL INTN blake2b_init(blake2b_state *S, UINTN outlen); +ARGON2_LOCAL INTN blake2b_init_key(blake2b_state *S, UINTN outlen, const VOID *key, + UINTN keylen); +ARGON2_LOCAL INTN blake2b_init_param(blake2b_state *S, const blake2b_param *P); +ARGON2_LOCAL INTN blake2b_update(blake2b_state *S, const VOID *in, UINTN inlen); +ARGON2_LOCAL INTN blake2b_final(blake2b_state *S, VOID *out, UINTN outlen); + +/* Simple API */ +ARGON2_LOCAL INTN blake2b(VOID *out, UINTN outlen, const VOID *in, UINTN inlen, + const VOID *key, UINTN keylen); + +/* Argon2 Team - Begin Code */ +ARGON2_LOCAL INTN blake2b_long(VOID *out, UINTN outlen, const VOID *in, UINTN inlen); +/* Argon2 Team - End Code */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/Blastproof/src/libs/argon2/blake2b.c b/Blastproof/src/libs/argon2/blake2b.c new file mode 100644 index 0000000..0f19979 --- /dev/null +++ b/Blastproof/src/libs/argon2/blake2b.c @@ -0,0 +1,398 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + + #include + #include +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +#ifndef UINT32_MAX +#define UINT32_MAX 0xFFFFFFFFU +#endif + +#ifndef UINT64_C +#define UINT64_C(x) (x##ULL) +#endif + +static const UINT64 blake2b_IV[8] = { + UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b), + UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1), + UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f), + UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179)}; + +static const UINTN blake2b_sigma[12][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, +}; + +static BLAKE2_INLINE VOID blake2b_set_lastnode(blake2b_state *S) { + S->f[1] = (UINT64)-1; +} + +static BLAKE2_INLINE VOID blake2b_set_lastblock(blake2b_state *S) { + if (S->last_node) { + blake2b_set_lastnode(S); + } + S->f[0] = (UINT64)-1; +} + +static BLAKE2_INLINE VOID blake2b_increment_counter(blake2b_state *S, + UINT64 inc) { + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); +} + +static BLAKE2_INLINE VOID blake2b_invalidate_state(blake2b_state *S) { + clear_internal_memory(S, sizeof(*S)); /* wipe */ + blake2b_set_lastblock(S); /* invalidate for further use */ +} + +static BLAKE2_INLINE VOID blake2b_init0(blake2b_state *S) { + SetMem(S, sizeof(*S), 0); + CopyMem(S->h, blake2b_IV, sizeof(S->h)); +} + +INTN blake2b_init_param(blake2b_state *S, const blake2b_param *P) { + const UINT8 *p = (const UINT8 *)P; + + if (NULL == P || NULL == S) { + return -1; + } + + blake2b_init0(S); + /* IV XOR Parameter Block */ + for (INTN i = 0; i < 8; ++i) { + S->h[i] ^= load64(&p[i * sizeof(S->h[i])]); + } + S->outlen = P->digest_length; + return 0; +} + +/* Sequential blake2b initialization */ +INTN blake2b_init(blake2b_state *S, UINTN outlen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for unkeyed BLAKE2 */ + P.digest_length = (UINT8)outlen; + P.key_length = 0; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + SetMem(P.reserved, sizeof(P.reserved), 0); + SetMem(P.salt, sizeof(P.salt), 0); + SetMem(P.personal, sizeof(P.personal), 0); + + return blake2b_init_param(S, &P); +} + +INTN blake2b_init_key(blake2b_state *S, UINTN outlen, const VOID *key, + UINTN keylen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + if ((key == 0) || (keylen == 0) || (keylen > BLAKE2B_KEYBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for keyed BLAKE2 */ + P.digest_length = (UINT8)outlen; + P.key_length = (UINT8)keylen; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + SetMem(P.reserved, sizeof(P.reserved), 0); + SetMem(P.salt, sizeof(P.salt), 0); + SetMem(P.personal, sizeof(P.personal), 0); + + if (blake2b_init_param(S, &P) < 0) { + blake2b_invalidate_state(S); + return -1; + } + + { + UINT8 block[BLAKE2B_BLOCKBYTES]; + SetMem(block, BLAKE2B_BLOCKBYTES, 0); + CopyMem(block, key, keylen); + blake2b_update(S, block, BLAKE2B_BLOCKBYTES); + /* Burn the key from stack */ + clear_internal_memory(block, BLAKE2B_BLOCKBYTES); + } + return 0; +} + +static VOID blake2b_compress(blake2b_state *S, const UINT8 *block) { + UINT64 m[16]; + UINT64 v[16]; + + for (INTN i = 0; i < 16; ++i) { + m[i] = load64(block + i * sizeof(m[i])); + } + + for (INTN i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2b_IV[0]; + v[9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while ((VOID)0, 0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while ((VOID)0, 0) + + for (INTN r = 0; r < 12; ++r) { + ROUND(r); + } + + for (INTN i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } + +#undef G +#undef ROUND +} + +INTN blake2b_update(blake2b_state *S, const VOID *in, UINTN inlen) { + const UINT8 *pin = (const UINT8 *)in; + + if (inlen == 0) { + return 0; + } + + /* Sanity check */ + if (S == NULL || in == NULL) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) { + /* Complete current block */ + UINTN left = S->buflen; + UINTN fill = BLAKE2B_BLOCKBYTES - left; + CopyMem(&S->buf[left], pin, fill); + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, S->buf); + S->buflen = 0; + inlen -= fill; + pin += fill; + /* AVOID buffer copies when possible */ + while (inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, pin); + inlen -= BLAKE2B_BLOCKBYTES; + pin += BLAKE2B_BLOCKBYTES; + } + } + CopyMem(&S->buf[S->buflen], pin, inlen); + S->buflen += (UINTN)inlen; + return 0; +} + +INTN blake2b_final(blake2b_state *S, VOID *out, UINTN outlen) { + UINT8 buffer[BLAKE2B_OUTBYTES] = {0}; + UINTN i; + + /* Sanity checks */ + if (S == NULL || out == NULL || outlen < S->outlen) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + blake2b_increment_counter(S, S->buflen); + blake2b_set_lastblock(S); + SetMem(&S->buf[S->buflen], BLAKE2B_BLOCKBYTES - S->buflen, 0); /* Padding */ + blake2b_compress(S, S->buf); + + for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */ + store64(buffer + sizeof(S->h[i]) * i, S->h[i]); + } + + CopyMem(out, buffer, S->outlen); + clear_internal_memory(buffer, sizeof(buffer)); + clear_internal_memory(S->buf, sizeof(S->buf)); + clear_internal_memory(S->h, sizeof(S->h)); + return 0; +} + +INTN blake2b(VOID *out, UINTN outlen, const VOID *in, UINTN inlen, + const VOID *key, UINTN keylen) { + blake2b_state S; + INTN ret = -1; + + /* Verify parameters */ + if (NULL == in && inlen > 0) { + goto fail; + } + + if (NULL == out || outlen == 0 || outlen > BLAKE2B_OUTBYTES) { + goto fail; + } + + if ((NULL == key && keylen > 0) || keylen > BLAKE2B_KEYBYTES) { + goto fail; + } + + if (keylen > 0) { + if (blake2b_init_key(&S, outlen, key, keylen) < 0) { + goto fail; + } + } else { + if (blake2b_init(&S, outlen) < 0) { + goto fail; + } + } + + if (blake2b_update(&S, in, inlen) < 0) { + goto fail; + } + ret = blake2b_final(&S, out, outlen); + +fail: + clear_internal_memory(&S, sizeof(S)); + return ret; +} + +/* Argon2 Team - Begin Code */ +INTN blake2b_long(VOID *pout, UINTN outlen, const VOID *in, UINTN inlen) { + UINT8 *out = (UINT8 *)pout; + blake2b_state blake_state; + UINT8 outlen_bytes[sizeof(UINT32)] = {0}; + INTN ret = -1; + + if (outlen > UINT32_MAX) { + goto fail; + } + + /* Ensure little-endian byte order! */ + store32(outlen_bytes, (UINT32)outlen); + +#define TRY(statement) \ + do { \ + ret = statement; \ + if (ret < 0) { \ + goto fail; \ + } \ + } while ((VOID)0, 0) + + if (outlen <= BLAKE2B_OUTBYTES) { + TRY(blake2b_init(&blake_state, outlen)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out, outlen)); + } else { + UINT32 toproduce; + UINT8 out_buffer[BLAKE2B_OUTBYTES]; + UINT8 in_buffer[BLAKE2B_OUTBYTES]; + TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); + CopyMem(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce = (UINT32)outlen - BLAKE2B_OUTBYTES / 2; + + while (toproduce > BLAKE2B_OUTBYTES) { + CopyMem(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer, + BLAKE2B_OUTBYTES, NULL, 0)); + CopyMem(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce -= BLAKE2B_OUTBYTES / 2; + } + + CopyMem(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, + 0)); + CopyMem(out, out_buffer, toproduce); + } +fail: + clear_internal_memory(&blake_state, sizeof(blake_state)); + return ret; +#undef TRY +} +/* Argon2 Team - End Code */ diff --git a/Blastproof/src/libs/argon2/blamka-round-opt.h b/Blastproof/src/libs/argon2/blamka-round-opt.h new file mode 100644 index 0000000..86b9b73 --- /dev/null +++ b/Blastproof/src/libs/argon2/blamka-round-opt.h @@ -0,0 +1,475 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef BLAKE_ROUND_MKA_OPT_H +#define BLAKE_ROUND_MKA_OPT_H +#include +#include +#include +#include +#include +#include "blake2-impl.h" + +#include +#if defined(__SSSE3__) +#include /* for _mm_shuffle_epi8 and _mm_alignr_epi8 */ +#endif + +#if defined(__XOP__) && (defined(__GNUC__) || defined(__clang__)) +#include +#endif + +#if !defined(__AVX512F__) +#if !defined(__AVX2__) +#if !defined(__XOP__) +#if defined(__SSSE3__) +#define r16 \ + (_mm_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9)) +#define r24 \ + (_mm_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10)) +#define _mm_roti_epi64(x, c) \ + (-(c) == 32) \ + ? _mm_shuffle_epi32((x), _MM_SHUFFLE(2, 3, 0, 1)) \ + : (-(c) == 24) \ + ? _mm_shuffle_epi8((x), r24) \ + : (-(c) == 16) \ + ? _mm_shuffle_epi8((x), r16) \ + : (-(c) == 63) \ + ? _mm_xor_si128(_mm_srli_epi64((x), -(c)), \ + _mm_add_epi64((x), (x))) \ + : _mm_xor_si128(_mm_srli_epi64((x), -(c)), \ + _mm_slli_epi64((x), 64 - (-(c)))) +#else /* defined(__SSE2__) */ +#define _mm_roti_epi64(r, c) \ + _mm_xor_si128(_mm_srli_epi64((r), -(c)), _mm_slli_epi64((r), 64 - (-(c)))) +#endif +#else +#endif + +static BLAKE2_INLINE __m128i fBlaMka(__m128i x, __m128i y) { + const __m128i z = _mm_mul_epu32(x, y); + return _mm_add_epi64(_mm_add_epi64(x, y), _mm_add_epi64(z, z)); +} + +#define G1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = fBlaMka(A0, B0); \ + A1 = fBlaMka(A1, B1); \ + \ + D0 = _mm_xor_si128(D0, A0); \ + D1 = _mm_xor_si128(D1, A1); \ + \ + D0 = _mm_roti_epi64(D0, -32); \ + D1 = _mm_roti_epi64(D1, -32); \ + \ + C0 = fBlaMka(C0, D0); \ + C1 = fBlaMka(C1, D1); \ + \ + B0 = _mm_xor_si128(B0, C0); \ + B1 = _mm_xor_si128(B1, C1); \ + \ + B0 = _mm_roti_epi64(B0, -24); \ + B1 = _mm_roti_epi64(B1, -24); \ + } while ((void)0, 0) + +#define G2(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = fBlaMka(A0, B0); \ + A1 = fBlaMka(A1, B1); \ + \ + D0 = _mm_xor_si128(D0, A0); \ + D1 = _mm_xor_si128(D1, A1); \ + \ + D0 = _mm_roti_epi64(D0, -16); \ + D1 = _mm_roti_epi64(D1, -16); \ + \ + C0 = fBlaMka(C0, D0); \ + C1 = fBlaMka(C1, D1); \ + \ + B0 = _mm_xor_si128(B0, C0); \ + B1 = _mm_xor_si128(B1, C1); \ + \ + B0 = _mm_roti_epi64(B0, -63); \ + B1 = _mm_roti_epi64(B1, -63); \ + } while ((void)0, 0) + +#if defined(__SSSE3__) +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = _mm_alignr_epi8(B1, B0, 8); \ + __m128i t1 = _mm_alignr_epi8(B0, B1, 8); \ + B0 = t0; \ + B1 = t1; \ + \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + \ + t0 = _mm_alignr_epi8(D1, D0, 8); \ + t1 = _mm_alignr_epi8(D0, D1, 8); \ + D0 = t1; \ + D1 = t0; \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = _mm_alignr_epi8(B0, B1, 8); \ + __m128i t1 = _mm_alignr_epi8(B1, B0, 8); \ + B0 = t0; \ + B1 = t1; \ + \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + \ + t0 = _mm_alignr_epi8(D0, D1, 8); \ + t1 = _mm_alignr_epi8(D1, D0, 8); \ + D0 = t1; \ + D1 = t0; \ + } while ((void)0, 0) +#else /* SSE2 */ +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0 = D0; \ + __m128i t1 = B0; \ + D0 = C0; \ + C0 = C1; \ + C1 = D0; \ + D0 = _mm_unpackhi_epi64(D1, _mm_unpacklo_epi64(t0, t0)); \ + D1 = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(D1, D1)); \ + B0 = _mm_unpackhi_epi64(B0, _mm_unpacklo_epi64(B1, B1)); \ + B1 = _mm_unpackhi_epi64(B1, _mm_unpacklo_epi64(t1, t1)); \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + __m128i t0, t1; \ + t0 = C0; \ + C0 = C1; \ + C1 = t0; \ + t0 = B0; \ + t1 = D0; \ + B0 = _mm_unpackhi_epi64(B1, _mm_unpacklo_epi64(B0, B0)); \ + B1 = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(B1, B1)); \ + D0 = _mm_unpackhi_epi64(D0, _mm_unpacklo_epi64(D1, D1)); \ + D1 = _mm_unpackhi_epi64(D1, _mm_unpacklo_epi64(t1, t1)); \ + } while ((void)0, 0) +#endif + +#define BLAKE2_ROUND(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ + \ + UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + } while ((void)0, 0) +#else /* __AVX2__ */ + +#include + +#define rotr32(x) _mm256_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1)) +#define rotr24(x) _mm256_shuffle_epi8(x, _mm256_setr_epi8(3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10, 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10)) +#define rotr16(x) _mm256_shuffle_epi8(x, _mm256_setr_epi8(2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9, 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9)) +#define rotr63(x) _mm256_xor_si256(_mm256_srli_epi64((x), 63), _mm256_add_epi64((x), (x))) + +#define G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i ml = _mm256_mul_epu32(A0, B0); \ + ml = _mm256_add_epi64(ml, ml); \ + A0 = _mm256_add_epi64(A0, _mm256_add_epi64(B0, ml)); \ + D0 = _mm256_xor_si256(D0, A0); \ + D0 = rotr32(D0); \ + \ + ml = _mm256_mul_epu32(C0, D0); \ + ml = _mm256_add_epi64(ml, ml); \ + C0 = _mm256_add_epi64(C0, _mm256_add_epi64(D0, ml)); \ + \ + B0 = _mm256_xor_si256(B0, C0); \ + B0 = rotr24(B0); \ + \ + ml = _mm256_mul_epu32(A1, B1); \ + ml = _mm256_add_epi64(ml, ml); \ + A1 = _mm256_add_epi64(A1, _mm256_add_epi64(B1, ml)); \ + D1 = _mm256_xor_si256(D1, A1); \ + D1 = rotr32(D1); \ + \ + ml = _mm256_mul_epu32(C1, D1); \ + ml = _mm256_add_epi64(ml, ml); \ + C1 = _mm256_add_epi64(C1, _mm256_add_epi64(D1, ml)); \ + \ + B1 = _mm256_xor_si256(B1, C1); \ + B1 = rotr24(B1); \ + } while((void)0, 0); + +#define G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i ml = _mm256_mul_epu32(A0, B0); \ + ml = _mm256_add_epi64(ml, ml); \ + A0 = _mm256_add_epi64(A0, _mm256_add_epi64(B0, ml)); \ + D0 = _mm256_xor_si256(D0, A0); \ + D0 = rotr16(D0); \ + \ + ml = _mm256_mul_epu32(C0, D0); \ + ml = _mm256_add_epi64(ml, ml); \ + C0 = _mm256_add_epi64(C0, _mm256_add_epi64(D0, ml)); \ + B0 = _mm256_xor_si256(B0, C0); \ + B0 = rotr63(B0); \ + \ + ml = _mm256_mul_epu32(A1, B1); \ + ml = _mm256_add_epi64(ml, ml); \ + A1 = _mm256_add_epi64(A1, _mm256_add_epi64(B1, ml)); \ + D1 = _mm256_xor_si256(D1, A1); \ + D1 = rotr16(D1); \ + \ + ml = _mm256_mul_epu32(C1, D1); \ + ml = _mm256_add_epi64(ml, ml); \ + C1 = _mm256_add_epi64(C1, _mm256_add_epi64(D1, ml)); \ + B1 = _mm256_xor_si256(B1, C1); \ + B1 = rotr63(B1); \ + } while((void)0, 0); + +#define DIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm256_permute4x64_epi64(B0, _MM_SHUFFLE(0, 3, 2, 1)); \ + C0 = _mm256_permute4x64_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + D0 = _mm256_permute4x64_epi64(D0, _MM_SHUFFLE(2, 1, 0, 3)); \ + \ + B1 = _mm256_permute4x64_epi64(B1, _MM_SHUFFLE(0, 3, 2, 1)); \ + C1 = _mm256_permute4x64_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ + D1 = _mm256_permute4x64_epi64(D1, _MM_SHUFFLE(2, 1, 0, 3)); \ + } while((void)0, 0); + +#define DIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i tmp1 = _mm256_blend_epi32(B0, B1, 0xCC); \ + __m256i tmp2 = _mm256_blend_epi32(B0, B1, 0x33); \ + B1 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + B0 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + \ + tmp1 = C0; \ + C0 = C1; \ + C1 = tmp1; \ + \ + tmp1 = _mm256_blend_epi32(D0, D1, 0xCC); \ + tmp2 = _mm256_blend_epi32(D0, D1, 0x33); \ + D0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + D1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + } while(0); + +#define UNDIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm256_permute4x64_epi64(B0, _MM_SHUFFLE(2, 1, 0, 3)); \ + C0 = _mm256_permute4x64_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + D0 = _mm256_permute4x64_epi64(D0, _MM_SHUFFLE(0, 3, 2, 1)); \ + \ + B1 = _mm256_permute4x64_epi64(B1, _MM_SHUFFLE(2, 1, 0, 3)); \ + C1 = _mm256_permute4x64_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ + D1 = _mm256_permute4x64_epi64(D1, _MM_SHUFFLE(0, 3, 2, 1)); \ + } while((void)0, 0); + +#define UNDIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + __m256i tmp1 = _mm256_blend_epi32(B0, B1, 0xCC); \ + __m256i tmp2 = _mm256_blend_epi32(B0, B1, 0x33); \ + B0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + B1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + \ + tmp1 = C0; \ + C0 = C1; \ + C1 = tmp1; \ + \ + tmp1 = _mm256_blend_epi32(D0, D1, 0x33); \ + tmp2 = _mm256_blend_epi32(D0, D1, 0xCC); \ + D0 = _mm256_permute4x64_epi64(tmp1, _MM_SHUFFLE(2,3,0,1)); \ + D1 = _mm256_permute4x64_epi64(tmp2, _MM_SHUFFLE(2,3,0,1)); \ + } while((void)0, 0); + +#define BLAKE2_ROUND_1(A0, A1, B0, B1, C0, C1, D0, D1) \ + do{ \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + DIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + UNDIAGONALIZE_1(A0, B0, C0, D0, A1, B1, C1, D1) \ + } while((void)0, 0); + +#define BLAKE2_ROUND_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do{ \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + DIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + G1_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + G2_AVX2(A0, A1, B0, B1, C0, C1, D0, D1) \ + \ + UNDIAGONALIZE_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + } while((void)0, 0); + +#endif /* __AVX2__ */ + +#else /* __AVX512F__ */ + +#include + +#define ror64(x, n) _mm512_ror_epi64((x), (n)) + +static __m512i muladd(__m512i x, __m512i y) +{ + __m512i z = _mm512_mul_epu32(x, y); + return _mm512_add_epi64(_mm512_add_epi64(x, y), _mm512_add_epi64(z, z)); +} + +#define G1(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = muladd(A0, B0); \ + A1 = muladd(A1, B1); \ +\ + D0 = _mm512_xor_si512(D0, A0); \ + D1 = _mm512_xor_si512(D1, A1); \ +\ + D0 = ror64(D0, 32); \ + D1 = ror64(D1, 32); \ +\ + C0 = muladd(C0, D0); \ + C1 = muladd(C1, D1); \ +\ + B0 = _mm512_xor_si512(B0, C0); \ + B1 = _mm512_xor_si512(B1, C1); \ +\ + B0 = ror64(B0, 24); \ + B1 = ror64(B1, 24); \ + } while ((void)0, 0) + +#define G2(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + A0 = muladd(A0, B0); \ + A1 = muladd(A1, B1); \ +\ + D0 = _mm512_xor_si512(D0, A0); \ + D1 = _mm512_xor_si512(D1, A1); \ +\ + D0 = ror64(D0, 16); \ + D1 = ror64(D1, 16); \ +\ + C0 = muladd(C0, D0); \ + C1 = muladd(C1, D1); \ +\ + B0 = _mm512_xor_si512(B0, C0); \ + B1 = _mm512_xor_si512(B1, C1); \ +\ + B0 = ror64(B0, 63); \ + B1 = ror64(B1, 63); \ + } while ((void)0, 0) + +#define DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm512_permutex_epi64(B0, _MM_SHUFFLE(0, 3, 2, 1)); \ + B1 = _mm512_permutex_epi64(B1, _MM_SHUFFLE(0, 3, 2, 1)); \ +\ + C0 = _mm512_permutex_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + C1 = _mm512_permutex_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ +\ + D0 = _mm512_permutex_epi64(D0, _MM_SHUFFLE(2, 1, 0, 3)); \ + D1 = _mm512_permutex_epi64(D1, _MM_SHUFFLE(2, 1, 0, 3)); \ + } while ((void)0, 0) + +#define UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + B0 = _mm512_permutex_epi64(B0, _MM_SHUFFLE(2, 1, 0, 3)); \ + B1 = _mm512_permutex_epi64(B1, _MM_SHUFFLE(2, 1, 0, 3)); \ +\ + C0 = _mm512_permutex_epi64(C0, _MM_SHUFFLE(1, 0, 3, 2)); \ + C1 = _mm512_permutex_epi64(C1, _MM_SHUFFLE(1, 0, 3, 2)); \ +\ + D0 = _mm512_permutex_epi64(D0, _MM_SHUFFLE(0, 3, 2, 1)); \ + D1 = _mm512_permutex_epi64(D1, _MM_SHUFFLE(0, 3, 2, 1)); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1) \ + do { \ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + DIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + G1(A0, B0, C0, D0, A1, B1, C1, D1); \ + G2(A0, B0, C0, D0, A1, B1, C1, D1); \ +\ + UNDIAGONALIZE(A0, B0, C0, D0, A1, B1, C1, D1); \ + } while ((void)0, 0) + +#define SWAP_HALVES(A0, A1) \ + do { \ + __m512i t0, t1; \ + t0 = _mm512_shuffle_i64x2(A0, A1, _MM_SHUFFLE(1, 0, 1, 0)); \ + t1 = _mm512_shuffle_i64x2(A0, A1, _MM_SHUFFLE(3, 2, 3, 2)); \ + A0 = t0; \ + A1 = t1; \ + } while((void)0, 0) + +#define SWAP_QUARTERS(A0, A1) \ + do { \ + SWAP_HALVES(A0, A1); \ + A0 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A0); \ + A1 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A1); \ + } while((void)0, 0) + +#define UNSWAP_QUARTERS(A0, A1) \ + do { \ + A0 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A0); \ + A1 = _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 1, 4, 5, 2, 3, 6, 7), A1); \ + SWAP_HALVES(A0, A1); \ + } while((void)0, 0) + +#define BLAKE2_ROUND_1(A0, C0, B0, D0, A1, C1, B1, D1) \ + do { \ + SWAP_HALVES(A0, B0); \ + SWAP_HALVES(C0, D0); \ + SWAP_HALVES(A1, B1); \ + SWAP_HALVES(C1, D1); \ + BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1); \ + SWAP_HALVES(A0, B0); \ + SWAP_HALVES(C0, D0); \ + SWAP_HALVES(A1, B1); \ + SWAP_HALVES(C1, D1); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND_2(A0, A1, B0, B1, C0, C1, D0, D1) \ + do { \ + SWAP_QUARTERS(A0, A1); \ + SWAP_QUARTERS(B0, B1); \ + SWAP_QUARTERS(C0, C1); \ + SWAP_QUARTERS(D0, D1); \ + BLAKE2_ROUND(A0, B0, C0, D0, A1, B1, C1, D1); \ + UNSWAP_QUARTERS(A0, A1); \ + UNSWAP_QUARTERS(B0, B1); \ + UNSWAP_QUARTERS(C0, C1); \ + UNSWAP_QUARTERS(D0, D1); \ + } while ((void)0, 0) + +#endif /* __AVX512F__ */ +#endif /* BLAKE_ROUND_MKA_OPT_H */ diff --git a/Blastproof/src/libs/argon2/blamka-round-ref.h b/Blastproof/src/libs/argon2/blamka-round-ref.h new file mode 100644 index 0000000..0ff4f5e --- /dev/null +++ b/Blastproof/src/libs/argon2/blamka-round-ref.h @@ -0,0 +1,60 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef BLAKE_ROUND_MKA_H +#define BLAKE_ROUND_MKA_H +#include +#include +#include +#include +#include +#include "blake2.h" +#include "blake2-impl.h" + +/* designed by the Lyra PHC team */ +static BLAKE2_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) { + const uint64_t m = UINT64_C(0xFFFFFFFF); + const uint64_t xy = (x & m) * (y & m); + return x + y + 2 * xy; +} + +#define G(a, b, c, d) \ + do { \ + a = fBlaMka(a, b); \ + d = rotr64(d ^ a, 32); \ + c = fBlaMka(c, d); \ + b = rotr64(b ^ c, 24); \ + a = fBlaMka(a, b); \ + d = rotr64(d ^ a, 16); \ + c = fBlaMka(c, d); \ + b = rotr64(b ^ c, 63); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \ + v12, v13, v14, v15) \ + do { \ + G(v0, v4, v8, v12); \ + G(v1, v5, v9, v13); \ + G(v2, v6, v10, v14); \ + G(v3, v7, v11, v15); \ + G(v0, v5, v10, v15); \ + G(v1, v6, v11, v12); \ + G(v2, v7, v8, v13); \ + G(v3, v4, v9, v14); \ + } while ((void)0, 0) + +#endif diff --git a/Blastproof/src/libs/argon2/core.c b/Blastproof/src/libs/argon2/core.c new file mode 100644 index 0000000..56e47d1 --- /dev/null +++ b/Blastproof/src/libs/argon2/core.c @@ -0,0 +1,656 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +/*For memory wiping*/ +#ifdef _WIN32 +#include +#include /* For SecureZeroMemory */ +#endif +#if defined __STDC_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 +#endif +#define VC_GE_2005(version) (version >= 1400) + +/* for explicit_bzero() on glibc */ +#define _DEFAULT_SOURCE + +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "thread.h" +#include "blake2.h" +#include "blake2-impl.h" + +#ifdef GENKAT +#include "genkat.h" +#endif + +#if defined(__clang__) +#if __has_attribute(optnone) +#define NOT_OPTIMIZED __attribute__((optnone)) +#endif +#elif defined(__GNUC__) +#define GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if GCC_VERSION >= 40400 +#define NOT_OPTIMIZED __attribute__((optimize("O0"))) +#endif +#endif +#ifndef NOT_OPTIMIZED +#define NOT_OPTIMIZED +#endif + +/***************Instance and Position constructors**********/ +VOID init_block_value(block *b, UINT8 in) { SetMem(b->v, sizeof(b->v), in); } + +VOID copy_block(block *dst, const block *src) { + CopyMem(dst->v, src->v, sizeof(UINT64) * ARGON2_QWORDS_IN_BLOCK); +} + +VOID xor_block(block *dst, const block *src) { + for (INTN i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + dst->v[i] ^= src->v[i]; + } +} + +static VOID load_block(block *dst, const VOID *input) { + for (INTN i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + dst->v[i] = load64((const UINT8 *)input + i * sizeof(dst->v[i])); + } +} + +static VOID store_block(VOID *output, const block *src) { + for (INTN i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + store64((UINT8 *)output + i * sizeof(src->v[i]), src->v[i]); + } +} + +/***************Memory functions*****************/ + +INTN allocate_memory(const argon2_context *context, UINT8 **memory, + UINTN num, UINTN size) { + UINTN memory_size = num*size; + if (memory == NULL) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + /* 1. Check for multiplication overflow */ + if (size != 0 && memory_size / size != num) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + /* 2. Try to allocate with appropriate allocator */ + if (context->allocate_cbk) { + (context->allocate_cbk)(memory, memory_size); + } else { + *memory = AllocatePool(memory_size); + } + + if (*memory == NULL) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + return ARGON2_OK; +} + +VOID free_memory(const argon2_context *context, UINT8 *memory, + UINTN num, UINTN size) { + UINTN memory_size = num*size; + clear_internal_memory(memory, memory_size); + if (context->free_cbk) { + (context->free_cbk)(memory, memory_size); + } else { + FreePool(memory); + } +} + +#if defined(__OpenBSD__) +#define HAVE_EXPLICIT_BZERO 1 +#elif defined(__GLIBC__) && defined(__GLIBC_PREREQ) +#if __GLIBC_PREREQ(2,25) +#define HAVE_EXPLICIT_BZERO 1 +#endif +#endif + +VOID secure_erase(VOID *v, UINTN n) { + volatile UINT8 *p = v; + while (n--) { + *p++ = 0; + } +} + +VOID NOT_OPTIMIZED secure_wipe_memory(VOID *v, UINTN n) { +#if defined(_MSC_VER) && VC_GE_2005(_MSC_VER) || defined(__MINGW32__) + SecureZeroMemory(v, n); +#elif defined memset_s + memset_s(v, n, 0, n); +#elif defined(HAVE_EXPLICIT_BZERO) + secure_erase(v, n); +#else + secure_erase(v, n); +#endif +} + +/* Memory clear flag defaults to true. */ +INTN FLAG_clear_internal_memory = 1; +VOID clear_internal_memory(VOID *v, UINTN n) { + if (FLAG_clear_internal_memory && v) { + secure_wipe_memory(v, n); + } +} + +VOID finalize(const argon2_context *context, argon2_instance_t *instance) { + if (context != NULL && instance != NULL) { + block blockhash; + + copy_block(&blockhash, instance->memory + instance->lane_length - 1); + + /* XOR the last blocks */ + for (INTN l = 1; l < instance->lanes; ++l) { + UINT32 last_block_in_lane = + l * instance->lane_length + (instance->lane_length - 1); + xor_block(&blockhash, instance->memory + last_block_in_lane); + } + + /* Hash the result */ + { + UINT8 blockhash_bytes[ARGON2_BLOCK_SIZE]; + store_block(blockhash_bytes, &blockhash); + blake2b_long(context->out, context->outlen, blockhash_bytes, + ARGON2_BLOCK_SIZE); + /* clear blockhash and blockhash_bytes */ + clear_internal_memory(blockhash.v, ARGON2_BLOCK_SIZE); + clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); + } + +#ifdef GENKAT + print_tag(context->out, context->outlen); +#endif + + free_memory(context, (UINT8 *)instance->memory, + instance->memory_blocks, sizeof(block)); + } +} + +UINT32 index_alpha(const argon2_instance_t *instance, + const argon2_position_t *position, UINT32 pseudo_rand, + INTN same_lane) { + /* + * Pass 0: + * This lane : all already finished segments plus already constructed + * blocks in this segment + * Other lanes : all already finished segments + * Pass 1+: + * This lane : (SYNC_POINTS - 1) last segments plus already constructed + * blocks in this segment + * Other lanes : (SYNC_POINTS - 1) last segments + */ + UINT32 reference_area_size; + UINT64 relative_position; + UINT32 start_position, absolute_position; + + if (0 == position->pass) { + /* First pass */ + if (0 == position->slice) { + /* First slice */ + reference_area_size = + position->index - 1; /* all but the previous */ + } else { + if (same_lane) { + /* The same lane => add current segment */ + reference_area_size = + position->slice * instance->segment_length + + position->index - 1; + } else { + reference_area_size = + position->slice * instance->segment_length + + ((position->index == 0) ? (-1) : 0); + } + } + } else { + /* Second pass */ + if (same_lane) { + reference_area_size = instance->lane_length - + instance->segment_length + position->index - + 1; + } else { + reference_area_size = instance->lane_length - + instance->segment_length + + ((position->index == 0) ? (-1) : 0); + } + } + + /* 1.2.4. Mapping pseudo_rand to 0.. and produce + * relative position */ + relative_position = pseudo_rand; + relative_position = relative_position * relative_position >> 32; + relative_position = reference_area_size - 1 - + (reference_area_size * relative_position >> 32); + + /* 1.2.5 Computing starting position */ + start_position = 0; + + if (0 != position->pass) { + start_position = (position->slice == ARGON2_SYNC_POINTS - 1) + ? 0 + : (position->slice + 1) * instance->segment_length; + } + + /* 1.2.6. Computing absolute position */ + absolute_position = (start_position + relative_position) % + instance->lane_length; /* absolute position */ + return absolute_position; +} + +/* Single-threaded version for p=1 case */ +static INTN fill_memory_blocks_st(argon2_instance_t *instance) { + + for (INTN r = 0; r < instance->passes; ++r) { + for (INTN s = 0; s < ARGON2_SYNC_POINTS; ++s) { + for (INTN l = 0; l < instance->lanes; ++l) { + argon2_position_t position = {r, l, (UINT8)s, 0}; + fill_segment(instance, position); + } + } +#ifdef GENKAT + internal_kat(instance, r); /* PrINTN all memory blocks */ +#endif + } + return ARGON2_OK; +} +#define ARGON2_NO_THREADS +#if !defined(ARGON2_NO_THREADS) + +#ifdef _WIN32 +static unsigned __stdcall fill_segment_thr(VOID *thread_data) +#else +static VOID *fill_segment_thr(VOID *thread_data) +#endif +{ + argon2_thread_data *my_data = thread_data; + fill_segment(my_data->instance_ptr, my_data->pos); + argon2_thread_exit(); + return 0; +} + +static inline void* calloc_efi(UINTN count, UINTN size) { + void *ptr = AllocatePool(count * size); + if (ptr != NULL) { + SetMem(ptr, count * size, 0); + } + return ptr; +} +/* Multi-threaded version for p > 1 case */ +static INTN fill_memory_blocks_mt(argon2_instance_t *instance) { + argon2_thread_handle_t *thread = NULL; + argon2_thread_data *thr_data = NULL; + INTN rc = ARGON2_OK; + + /* 1. Allocating space for threads */ + thread = calloc_efi(instance->lanes, sizeof(argon2_thread_handle_t)); + if (thread == NULL) { + rc = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + thr_data = calloc_efi(instance->lanes, sizeof(argon2_thread_data)); + if (thr_data == NULL) { + rc = ARGON2_MEMORY_ALLOCATION_ERROR; + goto fail; + } + + for (INTN r = 0; r < instance->passes; ++r) { + for (INTN s = 0; s < ARGON2_SYNC_POINTS; ++s) { + + /* 2. Calling threads */ + for (INTN l = 0; l < instance->lanes; ++l) { + argon2_position_t position; + + /* 2.1 Join a thread if limit is exceeded */ + if (l >= instance->threads) { + if (argon2_thread_join(thread[l - instance->threads])) { + rc = ARGON2_THREAD_FAIL; + goto fail; + } + } + + /* 2.2 Create thread */ + position.pass = r; + position.lane = l; + position.slice = (UINT8)s; + position.index = 0; + thr_data[l].instance_ptr = + instance; /* preparing the thread input */ + CopyMem(&(thr_data[l].pos), &position, + sizeof(argon2_position_t)); + if (argon2_thread_create(&thread[l], &fill_segment_thr, + (VOID *)&thr_data[l])) { + /* Wait for already running threads */ + for (INTN ll = 0; ll < l; ++ll) + argon2_thread_join(thread[ll]); + rc = ARGON2_THREAD_FAIL; + goto fail; + } + + /* fill_segment(instance, position); */ + /*Non-thread equivalent of the lines above */ + } + + /* 3. Joining remaining threads */ + for (INTN l = instance->lanes - instance->threads; l < instance->lanes; + ++l) { + if (argon2_thread_join(thread[l])) { + rc = ARGON2_THREAD_FAIL; + goto fail; + } + } + } + +#ifdef GENKAT + internal_kat(instance, r); /* PrINTN all memory blocks */ +#endif + } + +fail: + if (thread != NULL) { + FreePool(thread); + } + if (thr_data != NULL) { + FreePool(thr_data); + } + return rc; +} + +#endif /* ARGON2_NO_THREADS */ + +INTN fill_memory_blocks(argon2_instance_t *instance) { + if (instance == NULL || instance->lanes == 0) { + return ARGON2_INCORRECT_PARAMETER; + } +#if defined(ARGON2_NO_THREADS) + return fill_memory_blocks_st(instance); +#else + return instance->threads == 1 ? + fill_memory_blocks_st(instance) : fill_memory_blocks_mt(instance); +#endif +} + +INTN validate_inputs(const argon2_context *context) { + if (NULL == context) { + return ARGON2_INCORRECT_PARAMETER; + } + + if (NULL == context->out) { + return ARGON2_OUTPUT_PTR_NULL; + } + + /* Validate output length */ + if (ARGON2_MIN_OUTLEN > context->outlen) { + return ARGON2_OUTPUT_TOO_SHORT; + } + + if (ARGON2_MAX_OUTLEN < context->outlen) { + return ARGON2_OUTPUT_TOO_LONG; + } + + /* Validate password (required param) */ + if (NULL == context->pwd) { + if (0 != context->pwdlen) { + return ARGON2_PWD_PTR_MISMATCH; + } + } + + if (ARGON2_MIN_PWD_LENGTH > context->pwdlen) { + return ARGON2_PWD_TOO_SHORT; + } + + if (ARGON2_MAX_PWD_LENGTH < context->pwdlen) { + return ARGON2_PWD_TOO_LONG; + } + + /* Validate salt (required param) */ + if (NULL == context->salt) { + if (0 != context->saltlen) { + return ARGON2_SALT_PTR_MISMATCH; + } + } + + if (ARGON2_MIN_SALT_LENGTH > context->saltlen) { + return ARGON2_SALT_TOO_SHORT; + } + + if (ARGON2_MAX_SALT_LENGTH < context->saltlen) { + return ARGON2_SALT_TOO_LONG; + } + + /* Validate secret (optional param) */ + if (NULL == context->secret) { + if (0 != context->secretlen) { + return ARGON2_SECRET_PTR_MISMATCH; + } + } else { + if (ARGON2_MIN_SECRET > context->secretlen) { + return ARGON2_SECRET_TOO_SHORT; + } + if (ARGON2_MAX_SECRET < context->secretlen) { + return ARGON2_SECRET_TOO_LONG; + } + } + + /* Validate associated data (optional param) */ + if (NULL == context->ad) { + if (0 != context->adlen) { + return ARGON2_AD_PTR_MISMATCH; + } + } else { + if (ARGON2_MIN_AD_LENGTH > context->adlen) { + return ARGON2_AD_TOO_SHORT; + } + if (ARGON2_MAX_AD_LENGTH < context->adlen) { + return ARGON2_AD_TOO_LONG; + } + } + + /* Validate memory cost */ + if (ARGON2_MIN_MEMORY > context->m_cost) { + return ARGON2_MEMORY_TOO_LITTLE; + } + + if (ARGON2_MAX_MEMORY < context->m_cost) { + return ARGON2_MEMORY_TOO_MUCH; + } + + if (context->m_cost < 8 * context->lanes) { + return ARGON2_MEMORY_TOO_LITTLE; + } + + /* Validate time cost */ + if (ARGON2_MIN_TIME > context->t_cost) { + return ARGON2_TIME_TOO_SMALL; + } + + if (ARGON2_MAX_TIME < context->t_cost) { + return ARGON2_TIME_TOO_LARGE; + } + + /* Validate lanes */ + if (ARGON2_MIN_LANES > context->lanes) { + return ARGON2_LANES_TOO_FEW; + } + + if (ARGON2_MAX_LANES < context->lanes) { + return ARGON2_LANES_TOO_MANY; + } + + /* Validate threads */ + if (ARGON2_MIN_THREADS > context->threads) { + return ARGON2_THREADS_TOO_FEW; + } + + if (ARGON2_MAX_THREADS < context->threads) { + return ARGON2_THREADS_TOO_MANY; + } + + if (NULL != context->allocate_cbk && NULL == context->free_cbk) { + return ARGON2_FREE_MEMORY_CBK_NULL; + } + + if (NULL == context->allocate_cbk && NULL != context->free_cbk) { + return ARGON2_ALLOCATE_MEMORY_CBK_NULL; + } + + return ARGON2_OK; +} + +VOID fill_first_blocks(UINT8 *blockhash, const argon2_instance_t *instance) { + /* Make the first and second block in each lane as G(H0||0||i) or + G(H0||1||i) */ + UINT8 blockhash_bytes[ARGON2_BLOCK_SIZE]; + for (INTN l = 0; l < instance->lanes; ++l) { + + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0); + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l); + blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, + ARGON2_PREHASH_SEED_LENGTH); + load_block(&instance->memory[l * instance->lane_length + 0], + blockhash_bytes); + + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1); + blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, + ARGON2_PREHASH_SEED_LENGTH); + load_block(&instance->memory[l * instance->lane_length + 1], + blockhash_bytes); + } + clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); +} + +VOID initial_hash(UINT8 *blockhash, argon2_context *context, + argon2_type type) { + blake2b_state BlakeHash; + UINT8 value[sizeof(UINT32)]; + + if (NULL == context || NULL == blockhash) { + return; + } + + blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH); + + store32(&value, context->lanes); + blake2b_update(&BlakeHash, (const UINT8 *)&value, sizeof(value)); + + store32(&value, context->outlen); + blake2b_update(&BlakeHash, (const UINT8 *)&value, sizeof(value)); + + store32(&value, context->m_cost); + blake2b_update(&BlakeHash, (const UINT8 *)&value, sizeof(value)); + + store32(&value, context->t_cost); + blake2b_update(&BlakeHash, (const UINT8 *)&value, sizeof(value)); + + store32(&value, context->version); + blake2b_update(&BlakeHash, (const UINT8 *)&value, sizeof(value)); + + store32(&value, (UINT32)type); + blake2b_update(&BlakeHash, (const UINT8 *)&value, sizeof(value)); + + store32(&value, context->pwdlen); + blake2b_update(&BlakeHash, (const UINT8 *)&value, sizeof(value)); + + if (context->pwd != NULL) { + blake2b_update(&BlakeHash, (const UINT8 *)context->pwd, + context->pwdlen); + + if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) { + secure_wipe_memory(context->pwd, context->pwdlen); + context->pwdlen = 0; + } + } + + store32(&value, context->saltlen); + blake2b_update(&BlakeHash, (const UINT8 *)&value, sizeof(value)); + + if (context->salt != NULL) { + blake2b_update(&BlakeHash, (const UINT8 *)context->salt, + context->saltlen); + } + + store32(&value, context->secretlen); + blake2b_update(&BlakeHash, (const UINT8 *)&value, sizeof(value)); + + if (context->secret != NULL) { + blake2b_update(&BlakeHash, (const UINT8 *)context->secret, + context->secretlen); + + if (context->flags & ARGON2_FLAG_CLEAR_SECRET) { + secure_wipe_memory(context->secret, context->secretlen); + context->secretlen = 0; + } + } + + store32(&value, context->adlen); + blake2b_update(&BlakeHash, (const UINT8 *)&value, sizeof(value)); + + if (context->ad != NULL) { + blake2b_update(&BlakeHash, (const UINT8 *)context->ad, + context->adlen); + } + + blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH); +} + +INTN initialize(argon2_instance_t *instance, argon2_context *context) { + UINT8 blockhash[ARGON2_PREHASH_SEED_LENGTH]; + INTN result = ARGON2_OK; + + if (instance == NULL || context == NULL) + return ARGON2_INCORRECT_PARAMETER; + instance->context_ptr = context; + + /* 1. Memory allocation */ + result = allocate_memory(context, (UINT8 **)&(instance->memory), + instance->memory_blocks, sizeof(block)); + if (result != ARGON2_OK) { + return result; + } + + /* 2. Initial hashing */ + /* H_0 + 8 extra bytes to produce the first blocks */ + /* UINT8 blockhash[ARGON2_PREHASH_SEED_LENGTH]; */ + /* Hashing all inputs */ + initial_hash(blockhash, context, instance->type); + /* Zeroing 8 extra bytes */ + clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, + ARGON2_PREHASH_SEED_LENGTH - + ARGON2_PREHASH_DIGEST_LENGTH); + +#ifdef GENKAT + initial_kat(blockhash, context, instance->type); +#endif + + /* 3. Creating first blocks, we always have at least two blocks in a slice + */ + fill_first_blocks(blockhash, instance); + /* Clearing the hash */ + clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH); + + return ARGON2_OK; +} diff --git a/Blastproof/src/libs/argon2/core.h b/Blastproof/src/libs/argon2/core.h new file mode 100644 index 0000000..12a7068 --- /dev/null +++ b/Blastproof/src/libs/argon2/core.h @@ -0,0 +1,237 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_CORE_H +#define ARGON2_CORE_H + +#include +#include +#include +#include +#include +#include "argon2.h" + +#ifndef uintptr_t +typedef unsigned long uintptr_t; +#endif + +#define CONST_CAST(x) (x)(uintptr_t) + +/**********************Argon2 internal constants*******************************/ + +enum argon2_core_constants { + /* Memory block size in bytes */ + ARGON2_BLOCK_SIZE = 1024, + ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8, + ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16, + ARGON2_HWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 32, + ARGON2_512BIT_WORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 64, + + /* Number of pseudo-random values generated by one call to Blake in Argon2i + to + generate reference block positions */ + ARGON2_ADDRESSES_IN_BLOCK = 128, + + /* Pre-hashing digest length and its extension*/ + ARGON2_PREHASH_DIGEST_LENGTH = 64, + ARGON2_PREHASH_SEED_LENGTH = 72 +}; + +/*************************Argon2 internal data types***********************/ + +/* + * Structure for the (1KB) memory block implemented as 128 64-bit words. + * Memory blocks can be copied, XORed. Internal words can be accessed by [] (no + * bounds checking). + */ +typedef struct block_ { UINT64 v[ARGON2_QWORDS_IN_BLOCK]; } block; + +/*****************Functions that work with the block******************/ + +/* Initialize each byte of the block with @in */ +VOID init_block_value(block *b, UINT8 in); + +/* Copy block @src to block @dst */ +VOID copy_block(block *dst, const block *src); + +/* XOR @src onto @dst bytewise */ +VOID xor_block(block *dst, const block *src); + +/* + * Argon2 instance: memory pointer, number of passes, amount of memory, type, + * and derived values. + * Used to evaluate the number and location of blocks to construct in each + * thread + */ +typedef struct Argon2_instance_t { + block *memory; /* Memory pointer */ + UINT32 version; + UINT32 passes; /* Number of passes */ + UINT32 memory_blocks; /* Number of blocks in memory */ + UINT32 segment_length; + UINT32 lane_length; + UINT32 lanes; + UINT32 threads; + argon2_type type; + INTN print_internals; /* whether to prINTN the memory blocks */ + argon2_context *context_ptr; /* points back to original context */ +} argon2_instance_t; + +/* + * Argon2 position: where we construct the block right now. Used to distribute + * work between threads. + */ +typedef struct Argon2_position_t { + UINT32 pass; + UINT32 lane; + UINT8 slice; + UINT32 index; +} argon2_position_t; + +/*Struct that holds the inputs for thread handling FillSegment*/ +typedef struct Argon2_thread_data { + argon2_instance_t *instance_ptr; + argon2_position_t pos; +} argon2_thread_data; + +/*************************Argon2 core functions********************************/ + +/* Allocates memory to the given pointer, uses the appropriate allocator as + * specified in the context. Total allocated memory is num*size. + * @param context argon2_context which specifies the allocator + * @param memory pointer to the pointer to the memory + * @param size the size in bytes for each element to be allocated + * @param num the number of elements to be allocated + * @return ARGON2_OK if @memory is a valid pointer and memory is allocated + */ +INTN allocate_memory(const argon2_context *context, UINT8 **memory, + UINTN num, UINTN size); + +/* + * Frees memory at the given pointer, uses the appropriate deallocator as + * specified in the context. Also cleans the memory using clear_internal_memory. + * @param context argon2_context which specifies the deallocator + * @param memory pointer to buffer to be freed + * @param size the size in bytes for each element to be deallocated + * @param num the number of elements to be deallocated + */ +VOID free_memory(const argon2_context *context, UINT8 *memory, + UINTN num, UINTN size); + +/* Function that securely cleans the memory. This ignores any flags set + * regarding clearing memory. Usually one just calls clear_internal_memory. + * @param mem Pointer to the memory + * @param s Memory size in bytes + */ +VOID secure_wipe_memory(VOID *v, UINTN n); + +/* Function that securely clears the memory if FLAG_clear_internal_memory is + * set. If the flag isn't set, this function does nothing. + * @param mem Pointer to the memory + * @param s Memory size in bytes + */ +VOID clear_internal_memory(VOID *v, UINTN n); + +/* + * Computes absolute position of reference block in the lane following a skewed + * distribution and using a pseudo-random value as input + * @param instance Pointer to the current instance + * @param position Pointer to the current position + * @param pseudo_rand 32-bit pseudo-random value used to determine the position + * @param same_lane Indicates if the block will be taken from the current lane. + * If so we can reference the current segment + * @pre All pointers must be valid + */ +UINT32 index_alpha(const argon2_instance_t *instance, + const argon2_position_t *position, UINT32 pseudo_rand, + INTN same_lane); + +/* + * Function that validates all inputs against predefined restrictions and return + * an error code + * @param context Pointer to current Argon2 context + * @return ARGON2_OK if everything is all right, otherwise one of error codes + * (all defined in + */ +INTN validate_inputs(const argon2_context *context); + +/* + * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears + * password and secret if needed + * @param context Pointer to the Argon2 internal structure containing memory + * pointer, and parameters for time and space requirements. + * @param blockhash Buffer for pre-hashing digest + * @param type Argon2 type + * @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes + * allocated + */ +VOID initial_hash(UINT8 *blockhash, argon2_context *context, + argon2_type type); + +/* + * Function creates first 2 blocks per lane + * @param instance Pointer to the current instance + * @param blockhash Pointer to the pre-hashing digest + * @pre blockhash must poINTN to @a PREHASH_SEED_LENGTH allocated values + */ +VOID fill_first_blocks(UINT8 *blockhash, const argon2_instance_t *instance); + +/* + * Function allocates memory, hashes the inputs with Blake, and creates first + * two blocks. Returns the pointer to the main memory with 2 blocks per lane + * initialized + * @param context Pointer to the Argon2 internal structure containing memory + * pointer, and parameters for time and space requirements. + * @param instance Current Argon2 instance + * @return Zero if successful, -1 if memory failed to allocate. @context->state + * will be modified if successful. + */ +INTN initialize(argon2_instance_t *instance, argon2_context *context); + +/* + * XORing the last block of each lane, hashing it, making the tag. Deallocates + * the memory. + * @param context Pointer to current Argon2 context (use only the out parameters + * from it) + * @param instance Pointer to current instance of Argon2 + * @pre instance->state must poINTN to necessary amount of memory + * @pre context->out must poINTN to outlen bytes of memory + * @pre if context->free_cbk is not NULL, it should poINTN to a function that + * deallocates memory + */ +VOID finalize(const argon2_context *context, argon2_instance_t *instance); + +/* + * Function that fills the segment using previous segments also from other + * threads + * @param context current context + * @param instance Pointer to the current instance + * @param position Current position + * @pre all block pointers must be valid + */ +VOID fill_segment(const argon2_instance_t *instance, + argon2_position_t position); + +/* + * Function that fills the entire memory t_cost times based on the first two + * blocks in each lane + * @param instance Pointer to the current instance + * @return ARGON2_OK if successful, @context->state + */ +INTN fill_memory_blocks(argon2_instance_t *instance); + +#endif diff --git a/Blastproof/src/libs/argon2/encoding.c b/Blastproof/src/libs/argon2/encoding.c new file mode 100644 index 0000000..ff7eb0a --- /dev/null +++ b/Blastproof/src/libs/argon2/encoding.c @@ -0,0 +1,473 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#include +#include +#include +#include +#include +#include +#include "encoding.h" +#include "core.h" + +#ifndef UINT32_MAX +#define UINT32_MAX 0xFFFFFFFFU +#endif + +/* + * Example code for a decoder and encoder of "hash strings", with Argon2 + * parameters. + * + * This code comprises three sections: + * + * -- The first section contains generic Base64 encoding and decoding + * functions. It is conceptually applicable to any hash function + * implementation that uses Base64 to encode and decode parameters, + * salts and outputs. It could be made into a library, provided that + * the relevant functions are made public (non-static) and be given + * reasonable names to aVOID collisions with other functions. + * + * -- The second section is specific to Argon2. It encodes and decodes + * the parameters, salts and outputs. It does not compute the hash + * itself. + * + * The code was originally written by Thomas Pornin , + * to whom comments and remarks may be sent. It is released under what + * should amount to Public Domain or its closest equivalent; the + * following mantra is supposed to incarnate that fact with all the + * proper legal rituals: + * + * --------------------------------------------------------------------- + * This file is provided under the terms of Creative Commons CC0 1.0 + * Public Domain Dedication. To the extent possible under law, the + * author (Thomas Pornin) has waived all copyright and related or + * neighboring rights to this file. This work is published from: Canada. + * --------------------------------------------------------------------- + * + * Copyright (c) 2015 Thomas Pornin + */ + +/* ==================================================================== */ +/* + * Common code; could be shared between different hash functions. + * + * Note: the Base64 functions below assume that uppercase letters (resp. + * lowercase letters) have consecutive numerical codes, that fit on 8 + * bits. All modern systems use ASCII-compatible CHAR8sets, where these + * properties are true. If you are stuck with a dinosaur of a system + * that still defaults to EBCDIC then you already have much bigger + * interoperability issues to deal with. + */ + +/* + * Some macros for constant-time comparisons. These work over values in + * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true". + */ +#define EQ(x, y) ((((0U - ((UINTN)(x) ^ (UINTN)(y))) >> 8) & 0xFF) ^ 0xFF) +#define GT(x, y) ((((UINTN)(y) - (UINTN)(x)) >> 8) & 0xFF) +#define GE(x, y) (GT(y, x) ^ 0xFF) +#define LT(x, y) GT(y, x) +#define LE(x, y) GE(y, x) + +/* + * Convert value x (0..63) to corresponding Base64 CHAR8acter. + */ +static INTN b64_byte_to_CHAR8(UINTN x) { + return (LT(x, 26) & (x + 'A')) | + (GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) | + (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+') | + (EQ(x, 63) & '/'); +} + +/* + * Convert CHAR8acter c to the corresponding 6-bit value. If CHAR8acter c + * is not a Base64 CHAR8acter, then 0xFF (255) is returned. + */ +static UINTN b64_CHAR8_to_byte(INTN c) { + UINTN x; + + x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) | + (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) | + (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) | + (EQ(c, '/') & 63); + return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF)); +} + +/* + * Convert some bytes to Base64. 'dst_len' is the length (in CHAR8acters) + * of the output buffer 'dst'; if that buffer is not large enough to + * receive the result (including the terminating 0), then (UINTN)-1 + * is returned. Otherwise, the zero-terminated Base64 string is written + * in the buffer, and the output length (counted WITHOUT the terminating + * zero) is returned. + */ +static UINTN to_base64(CHAR8 *dst, UINTN dst_len, const VOID *src, + UINTN src_len) { + UINTN olen; + const UINT8 *buf; + UINTN acc, acc_len; + + olen = (src_len / 3) << 2; + switch (src_len % 3) { + case 2: + olen++; + /* fall through */ + case 1: + olen += 2; + break; + } + if (dst_len <= olen) { + return (UINTN)-1; + } + acc = 0; + acc_len = 0; + buf = (const UINT8 *)src; + while (src_len-- > 0) { + acc = (acc << 8) + (*buf++); + acc_len += 8; + while (acc_len >= 6) { + acc_len -= 6; + *dst++ = (CHAR8)b64_byte_to_CHAR8((acc >> acc_len) & 0x3F); + } + } + if (acc_len > 0) { + *dst++ = (CHAR8)b64_byte_to_CHAR8((acc << (6 - acc_len)) & 0x3F); + } + *dst++ = 0; + return olen; +} + +/* + * Decode Base64 CHAR8s into bytes. The '*dst_len' value must initially + * contain the length of the output buffer '*dst'; when the decoding + * ends, the actual number of decoded bytes is written back in + * '*dst_len'. + * + * Decoding stops when a non-Base64 CHAR8acter is encountered, or when + * the output buffer capacity is exceeded. If an error occurred (output + * buffer is too small, invalid last CHAR8acters leading to unprocessed + * buffered bits), then NULL is returned; otherwise, the returned value + * points to the first non-Base64 CHAR8acter in the source stream, which + * may be the terminating zero. + */ +static const CHAR8 *from_base64(VOID *dst, UINTN *dst_len, const CHAR8 *src) { + UINTN len; + UINT8 *buf; + UINTN acc, acc_len; + + buf = (UINT8 *)dst; + len = 0; + acc = 0; + acc_len = 0; + for (;;) { + UINTN d; + + d = b64_CHAR8_to_byte(*src); + if (d == 0xFF) { + break; + } + src++; + acc = (acc << 6) + d; + acc_len += 6; + if (acc_len >= 8) { + acc_len -= 8; + if ((len++) >= *dst_len) { + return NULL; + } + *buf++ = (acc >> acc_len) & 0xFF; + } + } + + /* + * If the input length is equal to 1 modulo 4 (which is + * invalid), then there will remain 6 unprocessed bits; + * otherwise, only 0, 2 or 4 bits are buffered. The buffered + * bits must also all be zero. + */ + if (acc_len > 4 || (acc & (((UINTN)1 << acc_len) - 1)) != 0) { + return NULL; + } + *dst_len = len; + return src; +} + +/* + * Decode decimal integer from 'str'; the value is written in '*v'. + * Returned value is a pointer to the next non-decimal CHAR8acter in the + * string. If there is no digit at all, or the value encoding is not + * minimal (extra leading zeros), or the value does not fit in an + * 'UINTN', then NULL is returned. + */ + +#ifndef ULONG_MAX +#define ULONG_MAX ((unsigned long)(~0UL)) +#endif + +static const CHAR8 *decode_decimal(const CHAR8 *str, UINTN *v) { + const CHAR8 *orig; + UINTN acc; + + acc = 0; + for (orig = str;; str++) { + INTN c; + + c = *str; + if (c < '0' || c > '9') { + break; + } + c -= '0'; + if (acc > (ULONG_MAX / 10)) { + return NULL; + } + acc *= 10; + if ((UINTN)c > (ULONG_MAX - acc)) { + return NULL; + } + acc += (UINTN)c; + } + if (str == orig || (*orig == '0' && str != (orig + 1))) { + return NULL; + } + *v = acc; + return str; +} + +/* ==================================================================== */ +/* + * Code specific to Argon2. + * + * The code below applies the following format: + * + * $argon2[$v=]$m=,t=,p=$$ + * + * where is either 'd', 'id', or 'i', is a decimal integer (positive, + * fits in an 'UINTN'), and is Base64-encoded data (no '=' padding + * CHAR8acters, no newline or whitespace). + * + * The last two binary chunks (encoded in Base64) are, in that order, + * the salt and the output. Both are required. The binary salt length and the + * output length must be in the allowed ranges defined in argon2.h. + * + * The ctx struct must contain buffers large enough to hold the salt and pwd + * when it is fed into decode_string. + */ + +INTN decode_string(argon2_context *ctx, const CHAR8 *str, argon2_type type) { + +/* check for prefix */ +#define CC(prefix) \ + do { \ + UINTN cc_len = AsciiStrLen(prefix); \ + if (AsciiStrnCmp(str, prefix, cc_len) != 0) { \ + return ARGON2_DECODING_FAIL; \ + } \ + str += cc_len; \ + } while ((VOID)0, 0) + +/* optional prefix checking with supplied code */ +#define CC_opt(prefix, code) \ + do { \ + UINTN cc_len = AsciiStrLen(prefix); \ + if (AsciiStrnCmp(str, prefix, cc_len) == 0) { \ + str += cc_len; \ + { code; } \ + } \ + } while ((VOID)0, 0) + +/* Decoding prefix into decimal */ +#define DECIMAL(x) \ + do { \ + UINTN dec_x; \ + str = decode_decimal(str, &dec_x); \ + if (str == NULL) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (x) = dec_x; \ + } while ((VOID)0, 0) + + +/* Decoding prefix into UINT32 decimal */ +#define DECIMAL_U32(x) \ + do { \ + UINTN dec_x; \ + str = decode_decimal(str, &dec_x); \ + if (str == NULL || dec_x > UINT32_MAX) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (x) = (UINT32)dec_x; \ + } while ((VOID)0, 0) + + +/* Decoding base64 into a binary buffer */ +#define BIN(buf, max_len, len) \ + do { \ + UINTN bin_len = (max_len); \ + str = from_base64(buf, &bin_len, str); \ + if (str == NULL || bin_len > UINT32_MAX) { \ + return ARGON2_DECODING_FAIL; \ + } \ + (len) = (UINT32)bin_len; \ + } while ((VOID)0, 0) + + UINTN maxsaltlen = ctx->saltlen; + UINTN maxoutlen = ctx->outlen; + INTN validation_result; + const CHAR8* type_string; + + /* We should start with the argon2_type we are using */ + type_string = argon2_type2string(type, 0); + if (!type_string) { + return ARGON2_INCORRECT_TYPE; + } + + CC("$"); + CC(type_string); + + /* Reading the version number if the default is suppressed */ + ctx->version = ARGON2_VERSION_10; + CC_opt("$v=", DECIMAL_U32(ctx->version)); + + CC("$m="); + DECIMAL_U32(ctx->m_cost); + CC(",t="); + DECIMAL_U32(ctx->t_cost); + CC(",p="); + DECIMAL_U32(ctx->lanes); + ctx->threads = ctx->lanes; + + CC("$"); + BIN(ctx->salt, maxsaltlen, ctx->saltlen); + CC("$"); + BIN(ctx->out, maxoutlen, ctx->outlen); + + /* The rest of the fields get the default values */ + ctx->secret = NULL; + ctx->secretlen = 0; + ctx->ad = NULL; + ctx->adlen = 0; + ctx->allocate_cbk = NULL; + ctx->free_cbk = NULL; + ctx->flags = ARGON2_DEFAULT_FLAGS; + + /* On return, must have valid context */ + validation_result = validate_inputs(ctx); + if (validation_result != ARGON2_OK) { + return validation_result; + } + + /* Can't have any additional CHAR8acters */ + if (*str == 0) { + return ARGON2_OK; + } else { + return ARGON2_DECODING_FAIL; + } +#undef CC +#undef CC_opt +#undef DECIMAL +#undef BIN +} + +INTN encode_string(CHAR8 *dst, UINTN dst_len, argon2_context *ctx, + argon2_type type) { +#define SS(str) \ + do { \ + UINTN pp_len = AsciiStrLen(str); \ + if (pp_len >= dst_len) { \ + return ARGON2_ENCODING_FAIL; \ + } \ + CopyMem(dst, str, pp_len + 1); \ + dst += pp_len; \ + dst_len -= pp_len; \ + } while ((VOID)0, 0) + +#define SX(x) \ + do { \ + CHAR8 tmp[30]; \ + AsciiSPrint(tmp, sizeof(tmp), "%lu", (UINTN)(x)); \ + SS(tmp); \ + } while ((VOID)0, 0) + +#define SB(buf, len) \ + do { \ + UINTN sb_len = to_base64(dst, dst_len, buf, len); \ + if (sb_len == (UINTN)-1) { \ + return ARGON2_ENCODING_FAIL; \ + } \ + dst += sb_len; \ + dst_len -= sb_len; \ + } while ((VOID)0, 0) + + const CHAR8* type_string = argon2_type2string(type, 0); + INTN validation_result = validate_inputs(ctx); + + if (!type_string) { + return ARGON2_ENCODING_FAIL; + } + + if (validation_result != ARGON2_OK) { + return validation_result; + } + + + SS("$"); + SS(type_string); + + SS("$v="); + SX(ctx->version); + + SS("$m="); + SX(ctx->m_cost); + SS(",t="); + SX(ctx->t_cost); + SS(",p="); + SX(ctx->lanes); + + SS("$"); + SB(ctx->salt, ctx->saltlen); + + SS("$"); + SB(ctx->out, ctx->outlen); + return ARGON2_OK; + +#undef SS +#undef SX +#undef SB +} + +UINTN b64len(UINT32 len) { + UINTN olen = ((UINTN)len / 3) << 2; + + switch (len % 3) { + case 2: + olen++; + /* fall through */ + case 1: + olen += 2; + break; + } + + return olen; +} + +UINTN numlen(UINT32 num) { + UINTN len = 1; + while (num >= 10) { + ++len; + num = num / 10; + } + return len; +} diff --git a/Blastproof/src/libs/argon2/encoding.h b/Blastproof/src/libs/argon2/encoding.h new file mode 100644 index 0000000..b460e63 --- /dev/null +++ b/Blastproof/src/libs/argon2/encoding.h @@ -0,0 +1,62 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ENCODING_H +#define ENCODING_H +#include +#include +#include +#include +#include +#include "argon2.h" + +#define ARGON2_MAX_DECODED_LANES UINT32_C(255) +#define ARGON2_MIN_DECODED_SALT_LEN UINT32_C(8) +#define ARGON2_MIN_DECODED_OUT_LEN UINT32_C(12) + +/* +* encode an Argon2 hash string into the provided buffer. 'dst_len' +* contains the size, in characters, of the 'dst' buffer; if 'dst_len' +* is less than the number of required characters (including the +* terminating 0), then this function returns ARGON2_ENCODING_ERROR. +* +* on success, ARGON2_OK is returned. +*/ +INTN encode_string(CHAR8 *dst, UINTN dst_len, argon2_context *ctx, + argon2_type type); + +/* +* Decodes an Argon2 hash string into the provided structure 'ctx'. +* The only fields that must be set prior to this call are ctx.saltlen and +* ctx.outlen (which must be the maximal salt and out length values that are +* allowed), ctx.salt and ctx.out (which must be buffers of the specified +* length), and ctx.pwd and ctx.pwdlen which must hold a valid password. +* +* Invalid input string causes an error. On success, the ctx is valid and all +* fields have been initialized. +* +* Returned value is ARGON2_OK on success, other ARGON2_ codes on error. +*/ +INTN decode_string(argon2_context *ctx, const CHAR8 *str, argon2_type type); + +/* Returns the length of the encoded byte stream with length len */ +UINTN b64len(UINT32 len); + +/* Returns the length of the encoded number num */ +UINTN numlen(UINT32 num); + +#endif diff --git a/Blastproof/src/libs/argon2/opt.c b/Blastproof/src/libs/argon2/opt.c new file mode 100644 index 0000000..098be8f --- /dev/null +++ b/Blastproof/src/libs/argon2/opt.c @@ -0,0 +1,285 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + + #include + #include + #include + #include + #include + +#include "argon2.h" +#include "core.h" + +#include "blake2.h" +#include "blamka-round-opt.h" + +/* + * Function fills a new memory block and optionally XORs the old block over the new one. + * Memory must be initialized. + * @param state Pointer to the just produced block. Content will be updated(!) + * @param ref_block Pointer to the reference block + * @param next_block Pointer to the block to be XORed over. May coincide with @ref_block + * @param with_xor Whether to XOR into the new block (1) or just overwrite (0) + * @pre all block pointers must be valid + */ +#if defined(__AVX512F__) +static VOID fill_block(__m512i *state, const block *ref_block, + block *next_block, INTN with_xor) { + __m512i block_XY[ARGON2_512BIT_WORDS_IN_BLOCK]; + UINTN i; + + if (with_xor) { + for (i = 0; i < ARGON2_512BIT_WORDS_IN_BLOCK; i++) { + state[i] = _mm512_xor_si512( + state[i], _mm512_loadu_si512((const __m512i *)ref_block->v + i)); + block_XY[i] = _mm512_xor_si512( + state[i], _mm512_loadu_si512((const __m512i *)next_block->v + i)); + } + } else { + for (i = 0; i < ARGON2_512BIT_WORDS_IN_BLOCK; i++) { + block_XY[i] = state[i] = _mm512_xor_si512( + state[i], _mm512_loadu_si512((const __m512i *)ref_block->v + i)); + } + } + + for (i = 0; i < 2; ++i) { + BLAKE2_ROUND_1( + state[8 * i + 0], state[8 * i + 1], state[8 * i + 2], state[8 * i + 3], + state[8 * i + 4], state[8 * i + 5], state[8 * i + 6], state[8 * i + 7]); + } + + for (i = 0; i < 2; ++i) { + BLAKE2_ROUND_2( + state[2 * 0 + i], state[2 * 1 + i], state[2 * 2 + i], state[2 * 3 + i], + state[2 * 4 + i], state[2 * 5 + i], state[2 * 6 + i], state[2 * 7 + i]); + } + + for (i = 0; i < ARGON2_512BIT_WORDS_IN_BLOCK; i++) { + state[i] = _mm512_xor_si512(state[i], block_XY[i]); + _mm512_storeu_si512((__m512i *)next_block->v + i, state[i]); + } +} +#elif defined(__AVX2__) +static VOID fill_block(__m256i *state, const block *ref_block, + block *next_block, INTN with_xor) { + __m256i block_XY[ARGON2_HWORDS_IN_BLOCK]; + UINTN i; + + if (with_xor) { + for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) { + state[i] = _mm256_xor_si256( + state[i], _mm256_loadu_si256((const __m256i *)ref_block->v + i)); + block_XY[i] = _mm256_xor_si256( + state[i], _mm256_loadu_si256((const __m256i *)next_block->v + i)); + } + } else { + for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) { + block_XY[i] = state[i] = _mm256_xor_si256( + state[i], _mm256_loadu_si256((const __m256i *)ref_block->v + i)); + } + } + + for (i = 0; i < 4; ++i) { + BLAKE2_ROUND_1(state[8 * i + 0], state[8 * i + 4], state[8 * i + 1], state[8 * i + 5], + state[8 * i + 2], state[8 * i + 6], state[8 * i + 3], state[8 * i + 7]); + } + + for (i = 0; i < 4; ++i) { + BLAKE2_ROUND_2(state[ 0 + i], state[ 4 + i], state[ 8 + i], state[12 + i], + state[16 + i], state[20 + i], state[24 + i], state[28 + i]); + } + + for (i = 0; i < ARGON2_HWORDS_IN_BLOCK; i++) { + state[i] = _mm256_xor_si256(state[i], block_XY[i]); + _mm256_storeu_si256((__m256i *)next_block->v + i, state[i]); + } +} +#else +static VOID fill_block(__m128i *state, const block *ref_block, + block *next_block, INTN with_xor) { + __m128i block_XY[ARGON2_OWORDS_IN_BLOCK]; + UINTN i; + + if (with_xor) { + for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) { + state[i] = _mm_xor_si128( + state[i], _mm_loadu_si128((const __m128i *)ref_block->v + i)); + block_XY[i] = _mm_xor_si128( + state[i], _mm_loadu_si128((const __m128i *)next_block->v + i)); + } + } else { + for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) { + block_XY[i] = state[i] = _mm_xor_si128( + state[i], _mm_loadu_si128((const __m128i *)ref_block->v + i)); + } + } + + for (i = 0; i < 8; ++i) { + BLAKE2_ROUND(state[8 * i + 0], state[8 * i + 1], state[8 * i + 2], + state[8 * i + 3], state[8 * i + 4], state[8 * i + 5], + state[8 * i + 6], state[8 * i + 7]); + } + + for (i = 0; i < 8; ++i) { + BLAKE2_ROUND(state[8 * 0 + i], state[8 * 1 + i], state[8 * 2 + i], + state[8 * 3 + i], state[8 * 4 + i], state[8 * 5 + i], + state[8 * 6 + i], state[8 * 7 + i]); + } + + for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) { + state[i] = _mm_xor_si128(state[i], block_XY[i]); + _mm_storeu_si128((__m128i *)next_block->v + i, state[i]); + } +} +#endif + +static VOID next_addresses(block *address_block, block *input_block) { + /*Temporary zero-initialized blocks*/ +#if defined(__AVX512F__) + __m512i zero_block[ARGON2_512BIT_WORDS_IN_BLOCK]; + __m512i zero2_block[ARGON2_512BIT_WORDS_IN_BLOCK]; +#elif defined(__AVX2__) + __m256i zero_block[ARGON2_HWORDS_IN_BLOCK]; + __m256i zero2_block[ARGON2_HWORDS_IN_BLOCK]; +#else + __m128i zero_block[ARGON2_OWORDS_IN_BLOCK]; + __m128i zero2_block[ARGON2_OWORDS_IN_BLOCK]; +#endif + + SetMem(zero_block, sizeof(zero_block), 0); + SetMem(zero2_block, sizeof(zero2_block), 0); + + /*Increasing index counter*/ + input_block->v[6]++; + + /*First iteration of G*/ + fill_block(zero_block, input_block, address_block, 0); + + /*Second iteration of G*/ + fill_block(zero2_block, address_block, address_block, 0); +} + +VOID fill_segment(const argon2_instance_t *instance, + argon2_position_t position) { + block *ref_block = NULL, *curr_block = NULL; + block address_block, input_block; + UINT64 pseudo_rand, ref_index, ref_lane; + UINT32 prev_offset, curr_offset; + UINT32 starting_index, i; +#if defined(__AVX512F__) + __m512i state[ARGON2_512BIT_WORDS_IN_BLOCK]; +#elif defined(__AVX2__) + __m256i state[ARGON2_HWORDS_IN_BLOCK]; +#else + __m128i state[ARGON2_OWORDS_IN_BLOCK]; +#endif + INTN data_independent_addressing; + + if (instance == NULL) { + return; + } + + data_independent_addressing = + (instance->type == Argon2_i) || + (instance->type == Argon2_id && (position.pass == 0) && + (position.slice < ARGON2_SYNC_POINTS / 2)); + + if (data_independent_addressing) { + init_block_value(&input_block, 0); + + input_block.v[0] = position.pass; + input_block.v[1] = position.lane; + input_block.v[2] = position.slice; + input_block.v[3] = instance->memory_blocks; + input_block.v[4] = instance->passes; + input_block.v[5] = instance->type; + } + + starting_index = 0; + + if ((0 == position.pass) && (0 == position.slice)) { + starting_index = 2; /* we have already generated the first two blocks */ + + /* Don't forget to generate the first block of addresses: */ + if (data_independent_addressing) { + next_addresses(&address_block, &input_block); + } + } + + /* Offset of the current block */ + curr_offset = position.lane * instance->lane_length + + position.slice * instance->segment_length + starting_index; + + if (0 == curr_offset % instance->lane_length) { + /* Last block in this lane */ + prev_offset = curr_offset + instance->lane_length - 1; + } else { + /* Previous block */ + prev_offset = curr_offset - 1; + } + + CopyMem(state, ((instance->memory + prev_offset)->v), ARGON2_BLOCK_SIZE); + + for (i = starting_index; i < instance->segment_length; + ++i, ++curr_offset, ++prev_offset) { + /*1.1 Rotating prev_offset if needed */ + if (curr_offset % instance->lane_length == 1) { + prev_offset = curr_offset - 1; + } + + /* 1.2 Computing the index of the reference block */ + /* 1.2.1 Taking pseudo-random value from the previous block */ + if (data_independent_addressing) { + if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) { + next_addresses(&address_block, &input_block); + } + pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK]; + } else { + pseudo_rand = instance->memory[prev_offset].v[0]; + } + + /* 1.2.2 Computing the lane of the reference block */ + ref_lane = ((pseudo_rand >> 32)) % instance->lanes; + + if ((position.pass == 0) && (position.slice == 0)) { + /* Can not reference other lanes yet */ + ref_lane = position.lane; + } + + /* 1.2.3 Computing the number of possible reference block within the + * lane. + */ + position.index = i; + ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF, + ref_lane == position.lane); + + /* 2 Creating a new block */ + ref_block = + instance->memory + instance->lane_length * ref_lane + ref_index; + curr_block = instance->memory + curr_offset; + if (ARGON2_VERSION_10 == instance->version) { + /* version 1.2.1 and earlier: overwrite, not XOR */ + fill_block(state, ref_block, curr_block, 0); + } else { + if(0 == position.pass) { + fill_block(state, ref_block, curr_block, 0); + } else { + fill_block(state, ref_block, curr_block, 1); + } + } + } +} diff --git a/Blastproof/src/libs/argon2/thread.c b/Blastproof/src/libs/argon2/thread.c new file mode 100644 index 0000000..ecb25c5 --- /dev/null +++ b/Blastproof/src/libs/argon2/thread.c @@ -0,0 +1,57 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ +#define ARGON2_NO_THREADS +#if !defined(ARGON2_NO_THREADS) + +#include "thread.h" +#if defined(_WIN32) +#include +#endif + +int argon2_thread_create(argon2_thread_handle_t *handle, + argon2_thread_func_t func, void *args) { + if (NULL == handle || func == NULL) { + return -1; + } +#if defined(_WIN32) + *handle = _beginthreadex(NULL, 0, func, args, 0, NULL); + return *handle != 0 ? 0 : -1; +#else + return pthread_create(handle, NULL, func, args); +#endif +} + +int argon2_thread_join(argon2_thread_handle_t handle) { +#if defined(_WIN32) + if (WaitForSingleObject((HANDLE)handle, INFINITE) == WAIT_OBJECT_0) { + return CloseHandle((HANDLE)handle) != 0 ? 0 : -1; + } + return -1; +#else + return pthread_join(handle, NULL); +#endif +} + +void argon2_thread_exit(void) { +#if defined(_WIN32) + _endthreadex(0); +#else + pthread_exit(NULL); +#endif +} + +#endif /* ARGON2_NO_THREADS */ diff --git a/Blastproof/src/libs/argon2/thread.h b/Blastproof/src/libs/argon2/thread.h new file mode 100644 index 0000000..fb0e8f2 --- /dev/null +++ b/Blastproof/src/libs/argon2/thread.h @@ -0,0 +1,67 @@ +/* + * Argon2 reference source code package - reference C implementations + * + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + * + * You may use this work under the terms of a Creative Commons CC0 1.0 + * License/Waiver or the Apache Public License 2.0, at your option. The terms of + * these licenses can be found at: + * + * - CC0 1.0 Universal : https://creativecommons.org/publicdomain/zero/1.0 + * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0 + * + * You should have received a copy of both of these licenses along with this + * software. If not, they may be obtained at the above URLs. + */ + +#ifndef ARGON2_THREAD_H +#define ARGON2_THREAD_H +#define ARGON2_NO_THREADS +#if !defined(ARGON2_NO_THREADS) + +/* + Here we implement an abstraction layer for the simpĺe requirements + of the Argon2 code. We only require 3 primitives---thread creation, + joining, and termination---so full emulation of the pthreads API + is unwarranted. Currently we wrap pthreads and Win32 threads. + + The API defines 2 types: the function pointer type, + argon2_thread_func_t, + and the type of the thread handle---argon2_thread_handle_t. +*/ +#if defined(_WIN32) +#include +typedef unsigned(__stdcall *argon2_thread_func_t)(void *); +typedef uintptr_t argon2_thread_handle_t; +#else +#include +typedef void *(*argon2_thread_func_t)(void *); +typedef pthread_t argon2_thread_handle_t; +#endif + +/* Creates a thread + * @param handle pointer to a thread handle, which is the output of this + * function. Must not be NULL. + * @param func A function pointer for the thread's entry point. Must not be + * NULL. + * @param args Pointer that is passed as an argument to @func. May be NULL. + * @return 0 if @handle and @func are valid pointers and a thread is successfully + * created. + */ +int argon2_thread_create(argon2_thread_handle_t *handle, + argon2_thread_func_t func, void *args); + +/* Waits for a thread to terminate + * @param handle Handle to a thread created with argon2_thread_create. + * @return 0 if @handle is a valid handle, and joining completed successfully. +*/ +int argon2_thread_join(argon2_thread_handle_t handle); + +/* Terminate the current thread. Must be run inside a thread created by + * argon2_thread_create. +*/ +void argon2_thread_exit(void); + +#endif /* ARGON2_NO_THREADS */ +#endif diff --git a/Blastproof/src/libs/include/conf.h b/Blastproof/src/libs/include/conf.h new file mode 100644 index 0000000..f57eb0e --- /dev/null +++ b/Blastproof/src/libs/include/conf.h @@ -0,0 +1,67 @@ +#ifndef BP_LIB_CONF_H +#define BP_LIB_CONF_H +#include +#include "disk.h" +typedef struct { + CHAR16 *line_content; + UINTN length; +} bp_conf_LINE; +typedef struct { + bp_conf_LINE *lines; + UINTN count; + UINTN capacity; +} bp_conf_LINED_FILE; +typedef struct { + CHAR16 *key; + CHAR16 *value; +} bp_conf_PAIR; +typedef struct { + bp_conf_PAIR *pairs; + UINTN count; + UINTN capacity; +} bp_conf_CONF; +typedef struct { + BOOLEAN serial_port_enabled; + BOOLEAN serial_port_debuging; + BOOLEAN serial_port_erroring; + CHAR16 *font; + BOOLEAN disable_boot_animation; + UINT64 default_horizontal_resolution; + UINT64 default_vertical_resolution; + UINT8 initfs_partition_type_guid[16]; + UINT8 initfs_partition_guid[16]; + UINT8 signsyst_partition_type_guid[16]; + UINT8 signsyst_partition_guid[16]; + UINT8 kernel_log_level; + BOOLEAN kernel_test_benchmark; + UINT64 kernel_bench_iterations; + BOOLEAN kernel_log_disable_serial_port; + BOOLEAN kernel_disable_serial_port; + UINT16 kernel_log_ring_size; +} bp_conf_BOOT_CONF; +STATIC CONST CHAR16 *CONST bp_conf_boot_conf_params[]={ + L"serial_port_enabled", + L"serial_port_debuging", + L"serial_port_erroring", + L"font", + L"disable_boot_animation", + L"default_horizontal_resolution", + L"default_vertical_resolution", + L"initfs_partition_type_guid", + L"initfs_partition_guid", + L"signsyst_partition_type_guid", + L"signsyst_partition_guid", + L"kernel_log_level", + L"kernel_test_benchmark", + L"kernel_bench_iterations", + L"kernel_log_disable_serial_port", + L"kernel_disable_serial_port", + L"kernel_log_ring_size" +}; +#define BP_CONF_BOOT_CONF_PARAMS_SIZE (sizeof(bp_conf_boot_conf_params)/sizeof(bp_conf_boot_conf_params[0])) +EFI_STATUS bp_conf_get_lines_number(bp_disk_FILE *file,UINTN *line_number); +EFI_STATUS bp_conf_parse_file_lines(bp_disk_FILE *file,bp_conf_LINED_FILE **lined_file); +EFI_STATUS bp_conf_parse_conf(bp_conf_LINED_FILE *lined_file,bp_conf_CONF **conf); +EFI_STATUS bp_conf_fill_default_boot_conf(bp_conf_BOOT_CONF **boot_conf); +EFI_STATUS bp_conf_fill_boot_conf(bp_conf_CONF *conf,bp_conf_BOOT_CONF **boot_conf); +#endif diff --git a/Blastproof/src/libs/include/console.h b/Blastproof/src/libs/include/console.h new file mode 100644 index 0000000..f93ba92 --- /dev/null +++ b/Blastproof/src/libs/include/console.h @@ -0,0 +1,13 @@ +#ifndef BP_LIB_CONSOLE_H +#define BP_LIB_CONSOLE_H +#include +#include "Uefi/UefiBaseType.h" +#include "disk.h" +#include "font.h" +typedef struct { + CHAR16* password; + UINTN size; +} bp_console_PASSWORD; +VOID bp_console_print_file(bp_disk_FILE *file); +EFI_STATUS bp_console_read_password(CHAR16 *prompt,bp_console_PASSWORD **pwd,CHAR16 hiding_char); +#endif diff --git a/Blastproof/src/libs/include/cpu.h b/Blastproof/src/libs/include/cpu.h new file mode 100644 index 0000000..b660835 --- /dev/null +++ b/Blastproof/src/libs/include/cpu.h @@ -0,0 +1,18 @@ +#ifndef BP_LIB_CPU_H +#define BP_LIB_CPU_H +#include +typedef struct { + CHAR8 vendor[13]; + UINT32 max_cpuid_level; + UINT32 stepping; + UINT32 model; + UINT32 family; + UINT32 ext_model; + UINT32 ext_family; + UINT32 display_family; + UINT32 display_model; + CHAR16 ecx_bin[33]; + CHAR16 edx_bin[33]; +} bp_cpu_CPU_INFO; +EFI_STATUS bp_cpu_fill_cpu_info(bp_cpu_CPU_INFO* cpu_info); +#endif diff --git a/Blastproof/src/libs/include/crypto.h b/Blastproof/src/libs/include/crypto.h new file mode 100644 index 0000000..077d9d4 --- /dev/null +++ b/Blastproof/src/libs/include/crypto.h @@ -0,0 +1,34 @@ +#ifndef BP_LIB_CRYPTO_H +#define BP_LIB_CRYPTO_H +#include +#include +#include "../sphincsplus/api.h" +#include "console.h" +#include "disk.h" +typedef struct { + UINT8 *digest; + UINTN size_bytes; +} bp_crypto_HASH_DIGEST; +typedef struct { + UINT32 memory_cost; + UINT32 time_cost; + UINT8 *salt; + UINTN salt_size; + bp_console_PASSWORD password; + UINTN hash_size; +} bp_crypto_ARGON2_CONTEXT; +typedef struct { + UINT8 pk[CRYPTO_PUBLICKEYBYTES]; + UINT8 sk[CRYPTO_SECRETKEYBYTES]; + BOOLEAN erase_pk; + BOOLEAN erase_sk; +} bp_crypto_SPHINCSPLUS_KEYPAIR; +EFI_STATUS bp_crypto_secure_erase(UINT8 *ptr,UINTN size); +EFI_STATUS bp_crypto_sha3(UINT8 *input,UINTN size_input,UINTN size_output,bp_crypto_HASH_DIGEST **output); +EFI_STATUS bp_crypto_argon2id_raw(bp_crypto_ARGON2_CONTEXT *context,bp_crypto_HASH_DIGEST **output); +EFI_STATUS bp_crypto_argon2d_raw(bp_crypto_ARGON2_CONTEXT *context,bp_crypto_HASH_DIGEST **output); +EFI_STATUS bp_crypto_argon2i_raw(bp_crypto_ARGON2_CONTEXT *context,bp_crypto_HASH_DIGEST **output); +EFI_STATUS bp_crypto_sphincsplus_keypair_seed(UINT8 *seed,UINTN seed_size,BOOLEAN erase_sk,BOOLEAN erase_pk,bp_crypto_SPHINCSPLUS_KEYPAIR **output); +EFI_STATUS bp_crypto_sphincsplus_verify(UINT8 *signature,UINTN signature_size,UINT8 *message,UINTN message_size,UINT8 *pk); +EFI_STATUS bp_crypto_sphincsplus_verify_file(EFI_FILE_PROTOCOL *root,CHAR16 *file,CHAR16 *sigfile,UINT8 *pk); +#endif diff --git a/Blastproof/src/libs/include/debug.h b/Blastproof/src/libs/include/debug.h new file mode 100644 index 0000000..4b7da5c --- /dev/null +++ b/Blastproof/src/libs/include/debug.h @@ -0,0 +1,7 @@ +#ifndef BP_LIB_DEBUG_H +#define BP_LIB_DEBUG_H +#include +#include +EFI_STATUS bp_debug_write_line(EFI_SERIAL_IO_PROTOCOL *serial_protocol,CHAR8* output,BOOLEAN allow_debugging); +EFI_STATUS bp_debug_write(EFI_SERIAL_IO_PROTOCOL *serial_protocol,CHAR8* output,BOOLEAN allow_debugging); +#endif diff --git a/Blastproof/src/libs/include/default.h b/Blastproof/src/libs/include/default.h new file mode 100644 index 0000000..ab67642 --- /dev/null +++ b/Blastproof/src/libs/include/default.h @@ -0,0 +1,20 @@ +#ifndef BP_LIB_DEFAULT_H +#define BP_LIB_DEFAULT_H +#include +#define BP_DEFAULT_CONF_SERIAL_PORT_ENABLED FALSE +#define BP_DEFAULT_CONF_SERIAL_PORT_DEBUGING FALSE +#define BP_DEFAULT_CONF_SERIAL_PORT_ERRORING FALSE +#define BP_DEFAULT_CONF_FONT L"bitra-ascii-medium.fbm" +#define BP_DEFAULT_CONF_DISABLE_BOOT_ANIMATION TRUE +#define BP_DEFAULT_CONF_DEFAULT_HORIZONTAL_RESOLUTION 0 +#define BP_DEFAULT_CONF_DEFAULT_VERTICAL_RESOLUTION 0 +#define BP_DEFAULT_CONF_INITFS_PARTITION_TYPE_GUID {0x83,0x62,0xb4,0x34,0xd8,0x25,0x11,0xf0,0xa6,0x8f,0x10,0xff,0xe0,0x84,0x23,0xa6} +#define BP_DEFAULT_CONF_SIGNSYST_PARTITION_TYPE_GUID {0xda,0x00,0x48,0xb4,0xd8,0x26,0x11,0xf0,0xb8,0x77,0x10,0xff,0xe0,0x84,0x23,0xa6} +#define BP_DEFAULT_CONF_PARTITION_GUID {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} +#define BP_DEFAULT_CONF_KERNEL_LOG_LEVEL 1 +#define BP_DEFAULT_CONF_KERNEL_TEST_BENCHMARK FALSE +#define BP_DEFAULT_CONF_KERNEL_BENCH_ITERATIONS 10000 +#define BP_DEFAULT_CONF_KERNEL_LOG_DISABLE_SERIAL_PORT TRUE +#define BP_DEFAULT_CONF_KERNEL_DISABLE_SERIAL_PORT FALSE +#define BP_DEFAULT_CONF_KERNEL_LOG_RING_SIZE 2048 +#endif diff --git a/Blastproof/src/libs/include/disk.h b/Blastproof/src/libs/include/disk.h new file mode 100644 index 0000000..0fe4735 --- /dev/null +++ b/Blastproof/src/libs/include/disk.h @@ -0,0 +1,18 @@ +#ifndef BP_LIB_DISK_H +#define BP_LIB_DISK_H +#include +#include +#include +#include +#include +#include +typedef struct { + UINT8 *content; + UINTN file_size_bytes; +} bp_disk_FILE; +EFI_STATUS bp_disk_check_dir_existence(EFI_FILE_PROTOCOL *root,CHAR16 *dir); +EFI_STATUS bp_disk_check_file_existence(EFI_FILE_PROTOCOL *root,CHAR16 *file); +EFI_STATUS bp_disk_open_file(EFI_FILE_PROTOCOL *root,CHAR16 *file,EFI_FILE_PROTOCOL **opened_file); +EFI_STATUS bp_disk_get_file_info(EFI_FILE_PROTOCOL *opened_file,EFI_FILE_INFO **file_info); +EFI_STATUS bp_disk_read_file(EFI_FILE_PROTOCOL *opened_file,bp_disk_FILE **file); +#endif diff --git a/Blastproof/src/libs/include/font.h b/Blastproof/src/libs/include/font.h new file mode 100644 index 0000000..c39e2a9 --- /dev/null +++ b/Blastproof/src/libs/include/font.h @@ -0,0 +1,57 @@ +#ifndef BP_LIB_FONT_H +#define BP_LIB_FONT_H +#include +#include "disk.h" +#include "graphic.h" +typedef struct { + UINT8 encoding; + UINT32 entries_count; + UINT8 height; + UINT8 width; +} bp_font_FBM_HEADER; +typedef struct { + UINT32 codepoint; + UINT8 *data; +} bp_font_FBM_CHAR; +typedef struct { + UINT32 entries_count; + bp_font_FBM_CHAR *chars; +} bp_font_FBM_FONT; +typedef struct { + UINT8 shading; + BOOLEAN enabled; +} bp_font_FBM_PIXEL; +typedef struct { + UINT32 codepoint; + bp_font_FBM_PIXEL *data; +} bp_font_FBM_CHAR_BUFFER; +typedef struct { + UINT32 entries_count; + bp_font_FBM_CHAR_BUFFER *chars; +} bp_font_FBM_FONT_BUFFER; +typedef struct { + UINT32 codepoint; + UINT8 width; + UINT8 height; + UINT32 red_bits; + UINT32 green_bits; + UINT32 blue_bits; + UINT32 *data; +} bp_font_FBM_CHAR_RENDER; +typedef struct { + UINT32 entries_count; + bp_font_FBM_CHAR_RENDER *chars; +} bp_font_FBM_FONT_RENDER; +typedef struct { + bp_graphic_COLOR_RGB background_color; + bp_graphic_COLOR_RGB text_color; + UINT32 red_bits; + UINT32 green_bits; + UINT32 blue_bits; +} bp_font_FBM_RENDERER_SETTING; +EFI_STATUS bp_font_get_header(bp_disk_FILE *fbm_file,bp_font_FBM_HEADER **header); +EFI_STATUS bp_font_load_font(bp_disk_FILE *fbm_file,bp_font_FBM_FONT **loaded_font); +EFI_STATUS bp_font_check_font(bp_font_FBM_FONT *font,bp_font_FBM_HEADER *header); +EFI_STATUS bp_font_parse_font(bp_font_FBM_FONT *font,bp_font_FBM_HEADER *header,bp_font_FBM_FONT_BUFFER **parsed_font); +EFI_STATUS bp_font_render_font(bp_font_FBM_FONT_BUFFER *font_buffer,bp_font_FBM_HEADER *header,bp_font_FBM_RENDERER_SETTING settings,bp_font_FBM_FONT_RENDER **rendered_font); +#endif diff --git a/Blastproof/src/libs/include/graphic.h b/Blastproof/src/libs/include/graphic.h new file mode 100644 index 0000000..99bdc75 --- /dev/null +++ b/Blastproof/src/libs/include/graphic.h @@ -0,0 +1,18 @@ +#ifndef BP_LIB_GRAPHIC_H +#define BP_LIB_GRAPHIC_H +#include +#include +#include +#include +typedef struct { + UINT8 r,g,b,a; +} bp_graphic_COLOR_RGBA; +typedef struct { + UINT8 r,g,b; +} bp_graphic_COLOR_RGB; +EFI_STATUS bp_graphic_get_mode_count(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,UINT32 *output); +EFI_STATUS bp_graphic_get_highest_resolution_index(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,UINT32 *output); +EFI_STATUS bp_graphic_get_index_by_resolution(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,UINT64 hor_res,UINT64 vert_res,UINT32 *output); +UINT32 bp_graphic_get_pixel_by_color(bp_graphic_COLOR_RGB *color,UINT32 red_bits,UINT32 green_bits,UINT32 blue_bits); +EFI_STATUS bp_graphic_copy_frame_part(UINT32 *fb_base,UINT32 *from_base,UINTN height_out,UINTN width_out,UINTN x,UINTN y,UINTN height_from,UINTN width_from); +#endif diff --git a/Blastproof/src/libs/include/initfs.h b/Blastproof/src/libs/include/initfs.h new file mode 100644 index 0000000..ba88b8d --- /dev/null +++ b/Blastproof/src/libs/include/initfs.h @@ -0,0 +1,84 @@ +#ifndef BP_LIB_INITFS_H +#define BP_LIB_INITFS_H +#include +#include +#include "../include/disk.h" +#define BP_INITFS_INITFS_SUPPORTED_VERSION 0x0001 +#pragma pack(1) +typedef struct { + UINT8 sig[8]; + UINT16 bootloader_version; + UINT16 initfs_version; + UINT32 os_version; + UINT8 installation_id[48]; + UINT64 initfs_size; + UINT64 table_size; + UINT64 files_area_size; + UINT64 entries_width; + UINT64 entries_count; + UINT64 files_area_offset; + UINT64 entropy_check1; + UINT64 check1; + UINT8 entries_table_hash[64]; + UINT8 files_area_hash[64]; + UINT8 installation_id_hash_hash[64]; + UINT8 padding[128]; + UINT8 header_hash[64]; +} bp_initfs_INITFS_HEADER; +#pragma pack() +#pragma pack(1) +typedef struct { + UINT64 file_offset; + UINT64 file_size; + UINT8 pk[64]; + UINT8 hash[64]; + UINT8 file_name[112]; +} bp_initfs_FILE_ENTRY; +#pragma pack() +#pragma pack(1) +typedef struct { + bp_initfs_FILE_ENTRY *files_entries; + UINT64 file_count; +} bp_initfs_FILES_TABLE; +#pragma pack() +#pragma pack(1) +typedef struct { + UINT8 sign[8]; + UINT16 bootloader_version; + UINT16 initfs_version; + UINT32 os_version; + UINT8 installation_id[48]; + UINT64 signature_size; + UINT64 signature_count; + UINT64 signsyst_size; + UINT64 signature_block_size; + UINT8 signature_block_hash[64]; + UINT8 padding[288]; + UINT8 header_hash[64]; +} bp_initfs_SIGNSYST_HEADER; +#pragma pack() +#pragma pack(1) +typedef struct { + bp_initfs_INITFS_HEADER *header; + bp_initfs_FILES_TABLE *files_table; + UINT8 *files_area; +} bp_initfs_INITFS; +#pragma pack() +#pragma pack(1) +typedef struct { + bp_initfs_SIGNSYST_HEADER *header; + UINT8 *signature_area; +} bp_initfs_SIGNSYST; +#pragma pack() +EFI_STATUS bp_initfs_parse_initfs_header(UINT8 *header_data,UINTN buffer_size,bp_initfs_INITFS_HEADER **header); +EFI_STATUS bp_initfs_generate_padding(UINTN size,UINT8 byte,UINT8 **result); +EFI_STATUS bp_initfs_check_initfs_header(bp_initfs_INITFS_HEADER *header,EFI_FILE_PROTOCOL *root); +EFI_STATUS bp_initfs_parse_signsyst_header(UINT8 *header_data,UINTN buffer_size,bp_initfs_SIGNSYST_HEADER **header); +EFI_STATUS bp_initfs_check_signsyst_header(bp_initfs_SIGNSYST_HEADER *header,EFI_FILE_PROTOCOL *root); +EFI_STATUS bp_initfs_cross_check_headers(bp_initfs_INITFS_HEADER *initfs,bp_initfs_SIGNSYST_HEADER *signsyst); +EFI_STATUS bp_initfs_create_initfs_structure(bp_initfs_INITFS_HEADER *supposed_header,UINT8 *loaded_initfs,bp_initfs_INITFS **initfs); +EFI_STATUS bp_initfs_create_signsyst_structure(bp_initfs_SIGNSYST_HEADER *supposed_header,UINT8 *loaded_signsyst,bp_initfs_SIGNSYST **signsyst); +EFI_STATUS bp_initfs_check_initfs_integrity(bp_initfs_INITFS *initfs,bp_initfs_SIGNSYST *signsyst); +EFI_STATUS bp_initfs_generate_file_name(CHAR8 *file_name_in,UINT8 **file_name_out); +EFI_STATUS bp_initfs_read_file(bp_initfs_INITFS *initfs,CHAR8 *file_name,bp_disk_FILE **file); +#endif diff --git a/Blastproof/src/libs/include/ui.h b/Blastproof/src/libs/include/ui.h new file mode 100644 index 0000000..6669824 --- /dev/null +++ b/Blastproof/src/libs/include/ui.h @@ -0,0 +1,27 @@ +#ifndef BP_LIB_UI_H +#define BP_LIB_UI_H +#include +#include +#include "font.h" +#include "disk.h" +typedef struct { + UINTN x; + UINTN y; + UINTN width; + UINTN height; +} bp_ui_TEXT_BOX; +typedef struct { + UINT32 *pixels; + UINTN pixels_count; +} bp_ui_BOOT_ANIMATION_FRAME; +typedef struct { + bp_ui_BOOT_ANIMATION_FRAME *frames; + UINTN frames_count; +} bp_ui_BOOT_ANIMATION; +EFI_STATUS bp_ui_draw_text_ascii(UINT32 *fb_base,bp_font_FBM_FONT_RENDER *font,INTN x,INTN y,UINTN width,UINTN heigth,CHAR8 *text,bp_ui_TEXT_BOX **handle); +EFI_STATUS bp_ui_render_boot_animation_4frames(bp_disk_FILE *boot_animation_file,UINTN screen_width,UINTN screen_heigth,UINTN frame_index,bp_ui_BOOT_ANIMATION **rendered_boot_animation); +EFI_STATUS bp_ui_render_boot_animation_last_frame(bp_disk_FILE *boot_animation_file,UINTN screen_width,UINTN screen_height,bp_ui_BOOT_ANIMATION_FRAME **rendered_frame); +EFI_STATUS bp_ui_render_boot_animation_last_frame_color(bp_disk_FILE *boot_animation_file,UINTN screen_width,UINTN screen_height,UINT32 color,bp_ui_BOOT_ANIMATION_FRAME **rendered_frame); +EFI_STATUS bp_ui_show_error_screen(EFI_SERIAL_IO_PROTOCOL *serial,CHAR8 *error_code,UINT32* fb_base,bp_font_FBM_FONT_RENDER *font,UINTN horizontal_resolution,UINTN vertical_resolution,bp_ui_BOOT_ANIMATION_FRAME *background_logo,BOOLEAN allow_erroring); +EFI_STATUS bp_ui_erase_text(UINT32 *fb_base,UINTN screen_width,UINTN screen_heigth,bp_ui_TEXT_BOX *text_box); +#endif diff --git a/Blastproof/src/libs/include/vyx.h b/Blastproof/src/libs/include/vyx.h new file mode 100644 index 0000000..bffe617 --- /dev/null +++ b/Blastproof/src/libs/include/vyx.h @@ -0,0 +1,74 @@ +#ifndef BP_LIB_VYX_H +#define BP_LIB_VYX_H +#include +#include "disk.h" +#define BP_VYX_SUPPORTED_VERSION 0x0001 +#define BP_VYX_SHELTER_BOOT_CONFIG_VA 0x00180000 +#define BP_VYX_SHELTER_MEMORY_MAP_PAGES_COUNT 16 +#define BP_VYX_SHELTER_MEMORY_MAP_VA 0x00190000 +#define BP_VYX_SHELTER_PT_PAGE_COUNT 4096 +#define BP_VYX_SHELTER_LOGGING_RING_BUFFER_VA 0xFFFFFFFFF0000000ULL +#pragma pack(1) +typedef struct { + UINT8 sig[3]; + UINT16 vyx_ver; + UINT64 text_offset; + UINT64 stack_base; + UINT64 text_size_padded; + UINT64 data_size_padded; + UINT64 rodata_size_padded; + UINT64 bss_size_padded; +} bp_vyx_VYX_HEADER; +#pragma pack() +typedef struct { + EFI_PHYSICAL_ADDRESS text_addr_phys; + EFI_PHYSICAL_ADDRESS data_addr_phys; + EFI_PHYSICAL_ADDRESS rodata_addr_phys; + EFI_PHYSICAL_ADDRESS bss_addr_phys; + EFI_PHYSICAL_ADDRESS stack_addr_phys; + EFI_PHYSICAL_ADDRESS keycard_addr_phys; + EFI_PHYSICAL_ADDRESS shelter_boot_config_addr_phys; + UINTN shelter_boot_config_pages_count; + EFI_PHYSICAL_ADDRESS shelter_memory_map_addr_phys; + EFI_PHYSICAL_ADDRESS shelter_page_table_addr_phys; + EFI_PHYSICAL_ADDRESS shelter_logging_ring_buffer_addr_phys; + UINTN shelter_logging_ring_buffer_pages_count; + UINTN keycard_page_count; +} bp_vyx_LOADER_SETTING; +typedef struct __attribute__((aligned(8))) { + UINT8 sig_start[8]; + UINT8 log_level; + UINT16 page_table_allocator_level; + EFI_PHYSICAL_ADDRESS page_table_pool_pa; + EFI_VIRTUAL_ADDRESS page_table_pool_va; + BOOLEAN test_benchmark; + UINT64 bench_iterations; + BOOLEAN log_disable_serial_port; + BOOLEAN disable_serial_port; + UINT16 log_ring_size; + UINT8 sig_end[8]; +} bp_vyx_SHELTER_BOOT_CONFIG; +#pragma pack(1) +typedef struct { + UINT32 type; + UINT64 physical_start; + UINT64 pages_count; + UINT64 attributes; +} bp_vyx_SHELTER_MEMORY_MAP_ENTRY; +#pragma pack() +#pragma pack(1) +typedef struct { + UINT8 sig_start[8]; + UINT64 entry_count; + UINT64 entry_size; + UINT8 mmap_syntax_version; +} bp_vyx_SHELTER_MEMORY_MAP_HEADER; +#pragma pack() +EFI_STATUS bp_vyx_get_header(bp_disk_FILE *file,bp_vyx_VYX_HEADER **header); +EFI_STATUS bp_vyx_create_shelter_boot_conf(UINT8 log_level,BOOLEAN test_benchmark,UINT64 bench_iterations,BOOLEAN log_disable_serial_port,BOOLEAN disable_serial_port,UINT16 log_ring_size,bp_vyx_SHELTER_BOOT_CONFIG *boot_config); +EFI_STATUS bp_vyx_init_pt_pool_allocator(); +UINT64 bp_vyx_get_pt_pool_allocator(); +EFI_PHYSICAL_ADDRESS bp_vyx_pt_pool_alloc_one_page(EFI_PHYSICAL_ADDRESS pt_pool_base); +EFI_STATUS bp_vyx_setup_physical_pages(bp_vyx_VYX_HEADER *header,bp_disk_FILE *vyx_file,bp_disk_FILE *keycard_file,bp_vyx_SHELTER_BOOT_CONFIG *shelter_boot_config,bp_vyx_LOADER_SETTING **loader_setting); +EFI_STATUS bp_vyx_create_page_table(bp_vyx_VYX_HEADER *header,bp_vyx_LOADER_SETTING *settings,EFI_PHYSICAL_ADDRESS *page_table_address,EFI_VIRTUAL_ADDRESS *stack_address,EFI_VIRTUAL_ADDRESS *page_table_pool_va); +#endif diff --git a/Blastproof/src/libs/sha3/sha3.c b/Blastproof/src/libs/sha3/sha3.c new file mode 100644 index 0000000..18fee0b --- /dev/null +++ b/Blastproof/src/libs/sha3/sha3.c @@ -0,0 +1,165 @@ +// sha3.c +// 19-Nov-11 Markku-Juhani O. Saarinen + +// Revised 07-Aug-15 to match with official release of FIPS PUB 202 "SHA3" +// Revised 03-Sep-15 for portability + OpenSSL - style API + +#include "sha3.h" +#include + +// update the state with given number of rounds + +VOID sha3_keccakf(UINT64 st[25]) +{ + // constants + const UINT64 keccakf_rndc[24] = { + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, + 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 + }; + const INTN keccakf_rotc[24] = { + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 + }; + const INTN keccakf_piln[24] = { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 + }; + + // variables + INTN i, j, r; + UINT64 t, bc[5]; + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + UINT8 *v; + + // endianess conversion. this is redundant on little-endian targets + for (i = 0; i < 25; i++) { + v = (UINT8 *) &st[i]; + st[i] = ((UINT64) v[0]) | (((UINT64) v[1]) << 8) | + (((UINT64) v[2]) << 16) | (((UINT64) v[3]) << 24) | + (((UINT64) v[4]) << 32) | (((UINT64) v[5]) << 40) | + (((UINT64) v[6]) << 48) | (((UINT64) v[7]) << 56); + } +#endif + + // actual iteration + for (r = 0; r < KECCAKF_ROUNDS; r++) { + + // Theta + for (i = 0; i < 5; i++) + bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20]; + + for (i = 0; i < 5; i++) { + t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); + for (j = 0; j < 25; j += 5) + st[j + i] ^= t; + } + + // Rho Pi + t = st[1]; + for (i = 0; i < 24; i++) { + j = keccakf_piln[i]; + bc[0] = st[j]; + st[j] = ROTL64(t, keccakf_rotc[i]); + t = bc[0]; + } + + // Chi + for (j = 0; j < 25; j += 5) { + for (i = 0; i < 5; i++) + bc[i] = st[j + i]; + for (i = 0; i < 5; i++) + st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5]; + } + + // Iota + st[0] ^= keccakf_rndc[r]; + } + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ + // endianess conversion. this is redundant on little-endian targets + for (i = 0; i < 25; i++) { + v = (UINT8 *) &st[i]; + t = st[i]; + v[0] = t & 0xFF; + v[1] = (t >> 8) & 0xFF; + v[2] = (t >> 16) & 0xFF; + v[3] = (t >> 24) & 0xFF; + v[4] = (t >> 32) & 0xFF; + v[5] = (t >> 40) & 0xFF; + v[6] = (t >> 48) & 0xFF; + v[7] = (t >> 56) & 0xFF; + } +#endif +} + +// Initialize the context for SHA3 + +INTN sha3_init(SHA3_CTX *c, INTN mdlen) +{ + INTN i; + + for (i = 0; i < 25; i++) + c->st.q[i] = 0; + c->mdlen = mdlen; + c->rsiz = 200 - 2 * mdlen; + c->pt = 0; + + return 1; +} + +// update state with more data + +INTN sha3_update(SHA3_CTX *c, const VOID *data, UINTN len) +{ + INTN i; + INTN j; + + j = c->pt; + for (i = 0; i < len; i++) { + c->st.b[j++] ^= ((const UINT8 *) data)[i]; + if (j >= c->rsiz) { + sha3_keccakf(c->st.q); + j = 0; + } + } + c->pt = j; + + return 1; +} + +// finalize and output a hash + +INTN sha3_final(VOID *md, SHA3_CTX *c) +{ + INTN i; + + c->st.b[c->pt] ^= 0x06; + c->st.b[c->rsiz - 1] ^= 0x80; + sha3_keccakf(c->st.q); + + for (i = 0; i < c->mdlen; i++) { + ((UINT8 *) md)[i] = c->st.b[i]; + } + + return 1; +} + +// compute a SHA-3 hash (md) of given byte length from "in" + +VOID *sha3(const VOID *in, UINTN inlen, VOID *md, UINTN mdlen) +{ + SHA3_CTX sha3; + + sha3_init(&sha3, mdlen); + sha3_update(&sha3, in, inlen); + sha3_final(md, &sha3); + + return md; +} diff --git a/Blastproof/src/libs/sha3/sha3.h b/Blastproof/src/libs/sha3/sha3.h new file mode 100644 index 0000000..5a6a291 --- /dev/null +++ b/Blastproof/src/libs/sha3/sha3.h @@ -0,0 +1,34 @@ +#ifndef SHA3_H +#define SHA3_H + +#include + +#ifndef KECCAKF_ROUNDS +#define KECCAKF_ROUNDS 24 +#endif + +#ifndef ROTL64 +#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) +#endif + +// state context +typedef struct { + union { // state: + UINT8 b[200]; // 8-bit bytes + UINT64 q[25]; // 64-bit words + } st; + INTN pt, rsiz, mdlen; // these don't overflow +} SHA3_CTX; + +// Compression function. +VOID sha3_keccakf(UINT64 st[25]); + +// OpenSSL - like interfece +INTN sha3_init(SHA3_CTX *c, INTN mdlen); // mdlen = hash output in bytes +INTN sha3_update(SHA3_CTX *c, const VOID *data, UINTN len); +INTN sha3_final(VOID *md, SHA3_CTX *c); // digest goes to md + +// compute a sha3 hash (md) of given byte length from "in" +VOID *sha3(const VOID *in, UINTN inlen, VOID *md, UINTN mdlen); + +#endif diff --git a/Blastproof/src/libs/sphincsplus/address.c b/Blastproof/src/libs/sphincsplus/address.c new file mode 100644 index 0000000..7c78d5f --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/address.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +/* + * Specify which level of Merkle tree (the "layer") we're working on + */ +VOID set_layer_addr(UINT32 addr[8], UINT32 layer) +{ + ((UINT8 *)addr)[SPX_OFFSET_LAYER] = (UINT8)layer; +} + +/* + * Specify which Merkle tree within the level (the "tree address") we're working on + */ +VOID set_tree_addr(UINT32 addr[8], UINT64 tree) +{ +#if (SPX_TREE_HEIGHT * (SPX_D - 1)) > 64 + #error Subtree addressing is currently limited to at most 2^64 trees +#endif + ull_to_bytes(&((UINT8 *)addr)[SPX_OFFSET_TREE], 8, tree ); +} + +/* + * Specify the reason we'll use this address structure for, that is, what + * hash will we compute with it. This is used so that unrelated types of + * hashes don't accidentally get the same address structure. The type will be + * one of the SPX_ADDR_TYPE constants + */ +VOID set_type(UINT32 addr[8], UINT32 type) +{ + ((UINT8 *)addr)[SPX_OFFSET_TYPE] = (UINT8)type; +} + +/* + * Copy the layer and tree fields of the address structure. This is used + * when we're doing multiple types of hashes within the same Merkle tree + */ +VOID copy_subtree_addr(UINT32 out[8], const UINT32 in[8]) +{ + CopyMem( out, in, SPX_OFFSET_TREE+8 ); +} + +/* These functions are used for OTS addresses. */ + +/* + * Specify which Merkle leaf we're working on; that is, which OTS keypair + * we're talking about. + */ +VOID set_keypair_addr(UINT32 addr[8], UINT32 keypair) +{ + u32_to_bytes(&((UINT8 *)addr)[SPX_OFFSET_KP_ADDR], keypair); +} + +/* + * Copy the layer, tree and keypair fields of the address structure. This is + * used when we're doing multiple things within the same OTS keypair + */ +VOID copy_keypair_addr(UINT32 out[8], const UINT32 in[8]) +{ + CopyMem( out, in, SPX_OFFSET_TREE+8 ); + CopyMem( (UINT8 *)out + SPX_OFFSET_KP_ADDR, (UINT8 *)in + SPX_OFFSET_KP_ADDR, 4); +} + +/* + * Specify which Merkle chain within the OTS we're working with + * (the chain address) + */ +VOID set_chain_addr(UINT32 addr[8], UINT32 chain) +{ + ((UINT8 *)addr)[SPX_OFFSET_CHAIN_ADDR] = (UINT8)chain; +} + +/* + * Specify where in the Merkle chain we are +* (the hash address) + */ +VOID set_hash_addr(UINT32 addr[8], UINT32 hash) +{ + ((UINT8 *)addr)[SPX_OFFSET_HASH_ADDR] = (UINT8)hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +/* + * Specify the height of the node in the Merkle/FORS tree we are in + * (the tree height) + */ +VOID set_tree_height(UINT32 addr[8], UINT32 tree_height) +{ + ((UINT8 *)addr)[SPX_OFFSET_TREE_HGT] = (UINT8)tree_height; +} + +/* + * Specify the distance from the left edge of the node in the Merkle/FORS tree + * (the tree index) + */ +VOID set_tree_index(UINT32 addr[8], UINT32 tree_index) +{ + u32_to_bytes(&((UINT8 *)addr)[SPX_OFFSET_TREE_INDEX], tree_index ); +} diff --git a/Blastproof/src/libs/sphincsplus/address.h b/Blastproof/src/libs/sphincsplus/address.h new file mode 100644 index 0000000..29298b6 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/address.h @@ -0,0 +1,55 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include +#include +#include +#include +#include +#include "params.h" + +/* The hash types that are passed to set_type */ +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 +#define SPX_ADDR_TYPE_WOTSPRF 5 +#define SPX_ADDR_TYPE_FORSPRF 6 + +#define set_layer_addr SPX_NAMESPACE(set_layer_addr) +VOID set_layer_addr(UINT32 addr[8], UINT32 layer); + +#define set_tree_addr SPX_NAMESPACE(set_tree_addr) +VOID set_tree_addr(UINT32 addr[8], UINT64 tree); + +#define set_type SPX_NAMESPACE(set_type) +VOID set_type(UINT32 addr[8], UINT32 type); + +/* Copies the layer and tree part of one address into the other */ +#define copy_subtree_addr SPX_NAMESPACE(copy_subtree_addr) +VOID copy_subtree_addr(UINT32 out[8], const UINT32 in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +#define set_keypair_addr SPX_NAMESPACE(set_keypair_addr) +VOID set_keypair_addr(UINT32 addr[8], UINT32 keypair); + +#define set_chain_addr SPX_NAMESPACE(set_chain_addr) +VOID set_chain_addr(UINT32 addr[8], UINT32 chain); + +#define set_hash_addr SPX_NAMESPACE(set_hash_addr) +VOID set_hash_addr(UINT32 addr[8], UINT32 hash); + +#define copy_keypair_addr SPX_NAMESPACE(copy_keypair_addr) +VOID copy_keypair_addr(UINT32 out[8], const UINT32 in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +#define set_tree_height SPX_NAMESPACE(set_tree_height) +VOID set_tree_height(UINT32 addr[8], UINT32 tree_height); + +#define set_tree_index SPX_NAMESPACE(set_tree_index) +VOID set_tree_index(UINT32 addr[8], UINT32 tree_index); + +#endif diff --git a/Blastproof/src/libs/sphincsplus/api.h b/Blastproof/src/libs/sphincsplus/api.h new file mode 100644 index 0000000..186ead9 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/api.h @@ -0,0 +1,80 @@ +#ifndef SPX_API_H +#define SPX_API_H + +#include +#include +#include +#include +#include + +#include "params.h" + +#define CRYPTO_ALGNAME "SPHINCS+" + +#define CRYPTO_SECRETKEYBYTES SPX_SK_BYTES +#define CRYPTO_PUBLICKEYBYTES SPX_PK_BYTES +#define CRYPTO_BYTES SPX_BYTES +#define CRYPTO_SEEDBYTES 3*SPX_N + +/* + * Returns the length of a secret key, in bytes + */ +UINT64 crypto_sign_secretkeybytes(VOID); + +/* + * Returns the length of a public key, in bytes + */ +UINT64 crypto_sign_publickeybytes(VOID); + +/* + * Returns the length of a signature, in bytes + */ +UINT64 crypto_sign_bytes(VOID); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +UINT64 crypto_sign_seedbytes(VOID); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +INTN crypto_sign_seed_keypair(UINT8 *pk, UINT8 *sk, + const UINT8 *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +INTN crypto_sign_keypair(UINT8 *pk, UINT8 *sk); + +/** + * Returns an array containing a detached signature. + */ +INTN crypto_sign_signature(UINT8 *sig, UINTN *siglen, + const UINT8 *m, UINTN mlen, const UINT8 *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +INTN crypto_sign_verify(const UINT8 *sig, UINTN siglen, + const UINT8 *m, UINTN mlen, const UINT8 *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +INTN crypto_sign(UINT8 *sm, UINT64 *smlen, + const UINT8 *m, UINT64 mlen, + const UINT8 *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +INTN crypto_sign_open(UINT8 *m, UINT64 *mlen, + const UINT8 *sm, UINT64 smlen, + const UINT8 *pk); + +#endif diff --git a/Blastproof/src/libs/sphincsplus/context.h b/Blastproof/src/libs/sphincsplus/context.h new file mode 100644 index 0000000..5c47bda --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/context.h @@ -0,0 +1,32 @@ +#ifndef SPX_CONTEXT_H +#define SPX_CONTEXT_H + +#include +#include +#include +#include +#include + +#include "params.h" + +typedef struct { + UINT8 pub_seed[SPX_N]; + UINT8 sk_seed[SPX_N]; + +#ifdef SPX_SHA2 + // sha256 state that absorbed pub_seed + UINT8 state_seeded[40]; + +# if SPX_SHA512 + // sha512 state that absorbed pub_seed + UINT8 state_seeded_512[72]; +# endif +#endif + +#ifdef SPX_HARAKA + UINT64 tweaked512_rc64[10][8]; + UINT32 tweaked256_rc32[10][8]; +#endif +} spx_ctx; + +#endif diff --git a/Blastproof/src/libs/sphincsplus/fors.c b/Blastproof/src/libs/sphincsplus/fors.c new file mode 100644 index 0000000..e24976d --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/fors.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include + +#include "fors.h" +#include "utils.h" +#include "utilsx1.h" +#include "hash.h" +#include "thash.h" +#include "address.h" + +static VOID fors_gen_sk(UINT8 *sk, const spx_ctx *ctx, + UINT32 fors_leaf_addr[8]) +{ + prf_addr(sk, ctx, fors_leaf_addr); +} + +static VOID fors_sk_to_leaf(UINT8 *leaf, const UINT8 *sk, + const spx_ctx *ctx, + UINT32 fors_leaf_addr[8]) +{ + thash(leaf, sk, 1, ctx, fors_leaf_addr); +} + +struct fors_gen_leaf_info { + UINT32 leaf_addrx[8]; +}; + +static VOID fors_gen_leafx1(UINT8 *leaf, + const spx_ctx *ctx, + UINT32 addr_idx, VOID *info) +{ + struct fors_gen_leaf_info *fors_info = info; + UINT32 *fors_leaf_addr = fors_info->leaf_addrx; + + /* Only set the parts that the caller doesn't set */ + set_tree_index(fors_leaf_addr, addr_idx); + set_type(fors_leaf_addr, SPX_ADDR_TYPE_FORSPRF); + fors_gen_sk(leaf, ctx, fors_leaf_addr); + + set_type(fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + fors_sk_to_leaf(leaf, leaf, + ctx, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit UINT32egers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static VOID message_to_indices(UINT32 *indices, const UINT8 *m) +{ + UINT32 i, j; + UINT32 offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= ((m[offset >> 3] >> (offset & 0x7)) & 1u) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +VOID fors_sign(UINT8 *sig, UINT8 *pk, + const UINT8 *m, + const spx_ctx *ctx, + const UINT32 fors_addr[8]) +{ + UINT32 indices[SPX_FORS_TREES]; + UINT8 roots[SPX_FORS_TREES * SPX_N]; + UINT32 fors_tree_addr[8] = {0}; + struct fors_gen_leaf_info fors_info = {0}; + UINT32 *fors_leaf_addr = fors_info.leaf_addrx; + UINT32 fors_pk_addr[8] = {0}; + UINT32 idx_offset; + UINT32 i; + + copy_keypair_addr(fors_tree_addr, fors_addr); + copy_keypair_addr(fors_leaf_addr, fors_addr); + + copy_keypair_addr(fors_pk_addr, fors_addr); + set_type(fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + set_tree_height(fors_tree_addr, 0); + set_tree_index(fors_tree_addr, indices[i] + idx_offset); + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSPRF); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, ctx, fors_tree_addr); + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + treehashx1(roots + i*SPX_N, sig, ctx, + indices[i], idx_offset, SPX_FORS_HEIGHT, fors_gen_leafx1, + fors_tree_addr, &fors_info); + + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + thash(pk, roots, SPX_FORS_TREES, ctx, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +VOID fors_pk_from_sig(UINT8 *pk, + const UINT8 *sig, const UINT8 *m, + const spx_ctx* ctx, + const UINT32 fors_addr[8]) +{ + UINT32 indices[SPX_FORS_TREES]; + UINT8 roots[SPX_FORS_TREES * SPX_N]; + UINT8 leaf[SPX_N]; + UINT32 fors_tree_addr[8] = {0}; + UINT32 fors_pk_addr[8] = {0}; + UINT32 idx_offset; + UINT32 i; + + copy_keypair_addr(fors_tree_addr, fors_addr); + copy_keypair_addr(fors_pk_addr, fors_addr); + + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + set_type(fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + set_tree_height(fors_tree_addr, 0); + set_tree_index(fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, ctx, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + compute_root(roots + i*SPX_N, leaf, indices[i], idx_offset, + sig, SPX_FORS_HEIGHT, ctx, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + thash(pk, roots, SPX_FORS_TREES, ctx, fors_pk_addr); +} diff --git a/Blastproof/src/libs/sphincsplus/fors.h b/Blastproof/src/libs/sphincsplus/fors.h new file mode 100644 index 0000000..0ffd4ef --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/fors.h @@ -0,0 +1,36 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include +#include +#include +#include +#include + +#include "params.h" +#include "context.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +#define fors_sign SPX_NAMESPACE(fors_sign) +VOID fors_sign(UINT8 *sig, UINT8 *pk, + const UINT8 *m, + const spx_ctx* ctx, + const UINT32 fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +#define fors_pk_from_sig SPX_NAMESPACE(fors_pk_from_sig) +VOID fors_pk_from_sig(UINT8 *pk, + const UINT8 *sig, const UINT8 *m, + const spx_ctx* ctx, + const UINT32 fors_addr[8]); + +#endif diff --git a/Blastproof/src/libs/sphincsplus/hash.h b/Blastproof/src/libs/sphincsplus/hash.h new file mode 100644 index 0000000..7ededcc --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/hash.h @@ -0,0 +1,32 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include +#include +#include +#include +#include + +#include "context.h" +#include "params.h" + +#define initialize_hash_function SPX_NAMESPACE(initialize_hash_function) +VOID initialize_hash_function(spx_ctx *ctx); + +#define prf_addr SPX_NAMESPACE(prf_addr) +VOID prf_addr(UINT8 *out, const spx_ctx *ctx, + const UINT32 addr[8]); + +#define gen_message_random SPX_NAMESPACE(gen_message_random) +VOID gen_message_random(UINT8 *R, const UINT8 *sk_prf, + const UINT8 *optrand, + const UINT8 *m, UINT64 mlen, + const spx_ctx *ctx); + +#define hash_message SPX_NAMESPACE(hash_message) +VOID hash_message(UINT8 *digest, UINT64 *tree, UINT32 *leaf_idx, + const UINT8 *R, const UINT8 *pk, + const UINT8 *m, UINT64 mlen, + const spx_ctx *ctx); + +#endif diff --git a/Blastproof/src/libs/sphincsplus/hash_sha2.c b/Blastproof/src/libs/sphincsplus/hash_sha2.c new file mode 100644 index 0000000..81226c9 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/hash_sha2.c @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include + +#include "address.h" +#include "utils.h" +#include "params.h" +#include "hash.h" +#include "sha2.h" + +#if SPX_N >= 24 +#define SPX_SHAX_OUTPUT_BYTES SPX_SHA512_OUTPUT_BYTES +#define SPX_SHAX_BLOCK_BYTES SPX_SHA512_BLOCK_BYTES +#define shaX_inc_init sha512_inc_init +#define shaX_inc_blocks sha512_inc_blocks +#define shaX_inc_finalize sha512_inc_finalize +#define shaX sha512 +#define mgf1_X mgf1_512 +#else +#define SPX_SHAX_OUTPUT_BYTES SPX_SHA256_OUTPUT_BYTES +#define SPX_SHAX_BLOCK_BYTES SPX_SHA256_BLOCK_BYTES +#define shaX_inc_init sha256_inc_init +#define shaX_inc_blocks sha256_inc_blocks +#define shaX_inc_finalize sha256_inc_finalize +#define shaX sha256 +#define mgf1_X mgf1_256 +#endif + + +/* For SHA, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +VOID initialize_hash_function(spx_ctx *ctx) +{ + seed_state(ctx); +} + +/* + * Computes PRF(pk_seed, sk_seed, addr). + */ +VOID prf_addr(UINT8 *out, const spx_ctx *ctx, + const UINT32 addr[8]) +{ + UINT8 sha2_state[40]; + UINT8 buf[SPX_SHA256_ADDR_BYTES + SPX_N]; + UINT8 outbuf[SPX_SHA256_OUTPUT_BYTES]; + + /* Retrieve precomputed state containing pub_seed */ + CopyMem(sha2_state, ctx->state_seeded, 40 * sizeof(UINT8)); + + /* Remainder: ADDR^c ‖ SK.seed */ + CopyMem(buf, addr, SPX_SHA256_ADDR_BYTES); + CopyMem(buf + SPX_SHA256_ADDR_BYTES, ctx->sk_seed, SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + SPX_N); + + CopyMem(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHAX_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +VOID gen_message_random(UINT8 *R, const UINT8 *sk_prf, + const UINT8 *optrand, + const UINT8 *m, UINT64 mlen, + const spx_ctx *ctx) +{ + (VOID)ctx; + + UINT8 buf[SPX_SHAX_BLOCK_BYTES + SPX_SHAX_OUTPUT_BYTES]; + UINT8 state[8 + SPX_SHAX_OUTPUT_BYTES]; + INTN i; + +#if SPX_N > SPX_SHAX_BLOCK_BYTES + #error "Currently only supports SPX_N of at most SPX_SHAX_BLOCK_BYTES" +#endif + + /* This implements HMAC-SHA */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + SetMem(buf + SPX_N, SPX_SHAX_BLOCK_BYTES - SPX_N, 0x36); + + shaX_inc_init(state); + shaX_inc_blocks(state, buf, 1); + + CopyMem(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHAX_BLOCK_BYTES) { + CopyMem(buf + SPX_N, m, mlen); + shaX_inc_finalize(buf + SPX_SHAX_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + CopyMem(buf + SPX_N, m, SPX_SHAX_BLOCK_BYTES - SPX_N); + shaX_inc_blocks(state, buf, 1); + + m += SPX_SHAX_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHAX_BLOCK_BYTES - SPX_N; + shaX_inc_finalize(buf + SPX_SHAX_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + SetMem(buf + SPX_N, SPX_SHAX_BLOCK_BYTES - SPX_N, 0x5c); + + shaX(buf, buf, SPX_SHAX_BLOCK_BYTES + SPX_SHAX_OUTPUT_BYTES); + CopyMem(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +VOID hash_message(UINT8 *digest, UINT64 *tree, UINT32 *leaf_idx, + const UINT8 *R, const UINT8 *pk, + const UINT8 *m, UINT64 mlen, + const spx_ctx *ctx) +{ + (VOID)ctx; +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + UINT8 seed[2*SPX_N + SPX_SHAX_OUTPUT_BYTES]; + + /* Round to nearest multiple of SPX_SHAX_BLOCK_BYTES */ +#if (SPX_SHAX_BLOCK_BYTES & (SPX_SHAX_BLOCK_BYTES-1)) != 0 + #error "Assumes that SPX_SHAX_BLOCK_BYTES is a power of 2" +#endif +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHAX_BLOCK_BYTES - 1) & \ + -SPX_SHAX_BLOCK_BYTES) / SPX_SHAX_BLOCK_BYTES) + UINT8 inbuf[SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES]; + + UINT8 buf[SPX_DGST_BYTES]; + UINT8 *bufp = buf; + UINT8 state[8 + SPX_SHAX_OUTPUT_BYTES]; + + shaX_inc_init(state); + + // seed: SHA-X(R ‖ PK.seed ‖ PK.root ‖ M) + CopyMem(inbuf, R, SPX_N); + CopyMem(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES) { + CopyMem(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + shaX_inc_finalize(seed + 2*SPX_N, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + CopyMem(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + shaX_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHAX_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + shaX_inc_finalize(seed + 2*SPX_N, state, m, mlen); + } + + // H_msg: MGF1-SHA-X(R ‖ PK.seed ‖ seed) + CopyMem(seed, R, SPX_N); + CopyMem(seed + SPX_N, pk, SPX_N); + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + mgf1_X(bufp, SPX_DGST_BYTES, seed, 2*SPX_N + SPX_SHAX_OUTPUT_BYTES); + + CopyMem(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + +#if SPX_TREE_BITS > 64 + #error For given height and depth, 64 bits cannot represent all subtrees +#endif + + if (SPX_D == 1) { + *tree = 0; + } else { + *tree = bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(UINT64)0) >> (64 - SPX_TREE_BITS); + } + bufp += SPX_TREE_BYTES; + + *leaf_idx = (UINT32)bytes_to_ull(bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(UINT32)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/Blastproof/src/libs/sphincsplus/merkle.c b/Blastproof/src/libs/sphincsplus/merkle.c new file mode 100644 index 0000000..85a2aa7 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/merkle.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include + +#include "utils.h" +#include "utilsx1.h" +#include "wots.h" +#include "wotsx1.h" +#include "merkle.h" +#include "address.h" +#include "params.h" + +/* + * This generates a Merkle signature (WOTS signature followed by the Merkle + * authentication path). This is in this file because most of the complexity + * is involved with the WOTS signature; the Merkle authentication path logic + * is mostly hidden in treehashx4 + */ +VOID merkle_sign(UINT8 *sig, UINT8 *root, + const spx_ctx *ctx, + UINT32 wots_addr[8], UINT32 tree_addr[8], + UINT32 idx_leaf) +{ + UINT8 *auth_path = sig + SPX_WOTS_BYTES; + struct leaf_info_x1 info = { 0 }; + UINT32 steps[ SPX_WOTS_LEN ]; + + info.wots_sig = sig; + chain_lengths(steps, root); + info.wots_steps = steps; + + set_type(&tree_addr[0], SPX_ADDR_TYPE_HASHTREE); + set_type(&info.pk_addr[0], SPX_ADDR_TYPE_WOTSPK); + copy_subtree_addr(&info.leaf_addr[0], wots_addr); + copy_subtree_addr(&info.pk_addr[0], wots_addr); + + info.wots_sign_leaf = idx_leaf; + + treehashx1(root, auth_path, ctx, + idx_leaf, 0, + SPX_TREE_HEIGHT, + wots_gen_leafx1, + tree_addr, &info); +} + +/* Compute root node of the top-most subtree. */ +VOID merkle_gen_root(UINT8 *root, const spx_ctx *ctx) +{ + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + UINT8 auth_path[SPX_TREE_HEIGHT * SPX_N + SPX_WOTS_BYTES]; + UINT32 top_tree_addr[8] = {0}; + UINT32 wots_addr[8] = {0}; + + set_layer_addr(top_tree_addr, SPX_D - 1); + set_layer_addr(wots_addr, SPX_D - 1); + + merkle_sign(auth_path, root, ctx, + wots_addr, top_tree_addr, + (UINT32)~0 /* ~0 means "don't bother generating an auth path */ ); +} diff --git a/Blastproof/src/libs/sphincsplus/merkle.h b/Blastproof/src/libs/sphincsplus/merkle.h new file mode 100644 index 0000000..3c5fbf6 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/merkle.h @@ -0,0 +1,22 @@ +#if !defined( MERKLE_H_ ) +#define MERKLE_H_ + +#include +#include +#include +#include +#include + +/* Generate a Merkle signature (WOTS signature followed by the Merkle */ +/* authentication path) */ +#define merkle_sign SPX_NAMESPACE(merkle_sign) +VOID merkle_sign(UINT8 *sig, UINT8 *root, + const spx_ctx* ctx, + UINT32 wots_addr[8], UINT32 tree_addr[8], + UINT32 idx_leaf); + +/* Compute the root node of the top-most subtree. */ +#define merkle_gen_root SPX_NAMESPACE(merkle_gen_root) +VOID merkle_gen_root(UINT8 *root, const spx_ctx* ctx); + +#endif /* MERKLE_H_ */ diff --git a/Blastproof/src/libs/sphincsplus/params.h b/Blastproof/src/libs/sphincsplus/params.h new file mode 100644 index 0000000..6dc6974 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/params.h @@ -0,0 +1,3 @@ +#define str(s) #s +#define xstr(s) str(s) +#include "params/params-sphincs-sha2-256f.h" diff --git a/Blastproof/src/libs/sphincsplus/params/params-sphincs-sha2-256f.h b/Blastproof/src/libs/sphincsplus/params/params-sphincs-sha2-256f.h new file mode 100644 index 0000000..53c5bef --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/params/params-sphincs-sha2-256f.h @@ -0,0 +1,85 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +#define SPX_NAMESPACE(s) SPX_##s + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 68 +/* Number of subtree layer. */ +#define SPX_D 17 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 9 +#define SPX_FORS_TREES 35 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* This is a SHA2-based parameter set, hence whether we use SHA-256 + * exclusively or we use both SHA-256 and SHA-512 is controlled by + * the following #define */ +#define SPX_SHA512 1 /* Use SHA-512 for H and T_l, l >= 2 */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#if SPX_WOTS_W == 256 + #define SPX_WOTS_LOGW 8 +#elif SPX_WOTS_W == 16 + #define SPX_WOTS_LOGW 4 +#else + #error SPX_WOTS_W assumed 16 or 256 +#endif + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#if SPX_WOTS_W == 256 + #if SPX_N <= 1 + #define SPX_WOTS_LEN2 1 + #elif SPX_N <= 256 + #define SPX_WOTS_LEN2 2 + #else + #error Did not precompute SPX_WOTS_LEN2 for n outside {2, .., 256} + #endif +#elif SPX_WOTS_W == 16 + #if SPX_N <= 8 + #define SPX_WOTS_LEN2 2 + #elif SPX_N <= 136 + #define SPX_WOTS_LEN2 3 + #elif SPX_N <= 256 + #define SPX_WOTS_LEN2 4 + #else + #error Did not precompute SPX_WOTS_LEN2 for n outside {2, .., 256} + #endif +#endif + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +#if SPX_TREE_HEIGHT * SPX_D != SPX_FULL_HEIGHT + #error SPX_D should always divide SPX_FULL_HEIGHT +#endif + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +#include "../sha2_offsets.h" + +#endif diff --git a/Blastproof/src/libs/sphincsplus/sha2.c b/Blastproof/src/libs/sphincsplus/sha2.c new file mode 100644 index 0000000..2d0bb13 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/sha2.c @@ -0,0 +1,702 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + + #include + #include + #include + #include + #include + +#include "utils.h" +#include "sha2.h" + +static UINT32 load_bigendian_32(const UINT8 *x) { + return (UINT32)(x[3]) | (((UINT32)(x[2])) << 8) | + (((UINT32)(x[1])) << 16) | (((UINT32)(x[0])) << 24); +} + +static UINT64 load_bigendian_64(const UINT8 *x) { + return (UINT64)(x[7]) | (((UINT64)(x[6])) << 8) | + (((UINT64)(x[5])) << 16) | (((UINT64)(x[4])) << 24) | + (((UINT64)(x[3])) << 32) | (((UINT64)(x[2])) << 40) | + (((UINT64)(x[1])) << 48) | (((UINT64)(x[0])) << 56); +} + +static VOID store_bigendian_32(UINT8 *x, UINT64 u) { + x[3] = (UINT8) u; + u >>= 8; + x[2] = (UINT8) u; + u >>= 8; + x[1] = (UINT8) u; + u >>= 8; + x[0] = (UINT8) u; +} + +static VOID store_bigendian_64(UINT8 *x, UINT64 u) { + x[7] = (UINT8) u; + u >>= 8; + x[6] = (UINT8) u; + u >>= 8; + x[5] = (UINT8) u; + u >>= 8; + x[4] = (UINT8) u; + u >>= 8; + x[3] = (UINT8) u; + u >>= 8; + x[2] = (UINT8) u; + u >>= 8; + x[1] = (UINT8) u; + u >>= 8; + x[0] = (UINT8) u; +} + +#define SHR(x, c) ((x) >> (c)) +#define ROTR_32(x, c) (((x) >> (c)) | ((x) << (32 - (c)))) +#define ROTR_64(x,c) (((x) >> (c)) | ((x) << (64 - (c)))) + +#define Ch(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +#define Sigma0_32(x) (ROTR_32(x, 2) ^ ROTR_32(x,13) ^ ROTR_32(x,22)) +#define Sigma1_32(x) (ROTR_32(x, 6) ^ ROTR_32(x,11) ^ ROTR_32(x,25)) +#define sigma0_32(x) (ROTR_32(x, 7) ^ ROTR_32(x,18) ^ SHR(x, 3)) +#define sigma1_32(x) (ROTR_32(x,17) ^ ROTR_32(x,19) ^ SHR(x,10)) + +#define Sigma0_64(x) (ROTR_64(x,28) ^ ROTR_64(x,34) ^ ROTR_64(x,39)) +#define Sigma1_64(x) (ROTR_64(x,14) ^ ROTR_64(x,18) ^ ROTR_64(x,41)) +#define sigma0_64(x) (ROTR_64(x, 1) ^ ROTR_64(x, 8) ^ SHR(x,7)) +#define sigma1_64(x) (ROTR_64(x,19) ^ ROTR_64(x,61) ^ SHR(x,6)) + +#define M_32(w0, w14, w9, w1) w0 = sigma1_32(w14) + (w9) + sigma0_32(w1) + (w0); +#define M_64(w0, w14, w9, w1) w0 = sigma1_64(w14) + (w9) + sigma0_64(w1) + (w0); + +#define EXPAND_32 \ + M_32(w0, w14, w9, w1) \ + M_32(w1, w15, w10, w2) \ + M_32(w2, w0, w11, w3) \ + M_32(w3, w1, w12, w4) \ + M_32(w4, w2, w13, w5) \ + M_32(w5, w3, w14, w6) \ + M_32(w6, w4, w15, w7) \ + M_32(w7, w5, w0, w8) \ + M_32(w8, w6, w1, w9) \ + M_32(w9, w7, w2, w10) \ + M_32(w10, w8, w3, w11) \ + M_32(w11, w9, w4, w12) \ + M_32(w12, w10, w5, w13) \ + M_32(w13, w11, w6, w14) \ + M_32(w14, w12, w7, w15) \ + M_32(w15, w13, w8, w0) + +#define EXPAND_64 \ + M_64(w0 ,w14,w9 ,w1 ) \ + M_64(w1 ,w15,w10,w2 ) \ + M_64(w2 ,w0 ,w11,w3 ) \ + M_64(w3 ,w1 ,w12,w4 ) \ + M_64(w4 ,w2 ,w13,w5 ) \ + M_64(w5 ,w3 ,w14,w6 ) \ + M_64(w6 ,w4 ,w15,w7 ) \ + M_64(w7 ,w5 ,w0 ,w8 ) \ + M_64(w8 ,w6 ,w1 ,w9 ) \ + M_64(w9 ,w7 ,w2 ,w10) \ + M_64(w10,w8 ,w3 ,w11) \ + M_64(w11,w9 ,w4 ,w12) \ + M_64(w12,w10,w5 ,w13) \ + M_64(w13,w11,w6 ,w14) \ + M_64(w14,w12,w7 ,w15) \ + M_64(w15,w13,w8 ,w0 ) + +#define F_32(w, k) \ + T1 = h + Sigma1_32(e) + Ch(e, f, g) + (k) + (w); \ + T2 = Sigma0_32(a) + Maj(a, b, c); \ + h = g; \ + g = f; \ + f = e; \ + e = d + T1; \ + d = c; \ + c = b; \ + b = a; \ + a = T1 + T2; + +#define F_64(w,k) \ + T1 = h + Sigma1_64(e) + Ch(e,f,g) + k + w; \ + T2 = Sigma0_64(a) + Maj(a,b,c); \ + h = g; \ + g = f; \ + f = e; \ + e = d + T1; \ + d = c; \ + c = b; \ + b = a; \ + a = T1 + T2; + +static UINTN crypto_hashblocks_sha256(UINT8 *statebytes, + const UINT8 *in, UINTN inlen) { + UINT32 state[8]; + UINT32 a; + UINT32 b; + UINT32 c; + UINT32 d; + UINT32 e; + UINT32 f; + UINT32 g; + UINT32 h; + UINT32 T1; + UINT32 T2; + + a = load_bigendian_32(statebytes + 0); + state[0] = a; + b = load_bigendian_32(statebytes + 4); + state[1] = b; + c = load_bigendian_32(statebytes + 8); + state[2] = c; + d = load_bigendian_32(statebytes + 12); + state[3] = d; + e = load_bigendian_32(statebytes + 16); + state[4] = e; + f = load_bigendian_32(statebytes + 20); + state[5] = f; + g = load_bigendian_32(statebytes + 24); + state[6] = g; + h = load_bigendian_32(statebytes + 28); + state[7] = h; + + while (inlen >= 64) { + UINT32 w0 = load_bigendian_32(in + 0); + UINT32 w1 = load_bigendian_32(in + 4); + UINT32 w2 = load_bigendian_32(in + 8); + UINT32 w3 = load_bigendian_32(in + 12); + UINT32 w4 = load_bigendian_32(in + 16); + UINT32 w5 = load_bigendian_32(in + 20); + UINT32 w6 = load_bigendian_32(in + 24); + UINT32 w7 = load_bigendian_32(in + 28); + UINT32 w8 = load_bigendian_32(in + 32); + UINT32 w9 = load_bigendian_32(in + 36); + UINT32 w10 = load_bigendian_32(in + 40); + UINT32 w11 = load_bigendian_32(in + 44); + UINT32 w12 = load_bigendian_32(in + 48); + UINT32 w13 = load_bigendian_32(in + 52); + UINT32 w14 = load_bigendian_32(in + 56); + UINT32 w15 = load_bigendian_32(in + 60); + + F_32(w0, 0x428a2f98) + F_32(w1, 0x71374491) + F_32(w2, 0xb5c0fbcf) + F_32(w3, 0xe9b5dba5) + F_32(w4, 0x3956c25b) + F_32(w5, 0x59f111f1) + F_32(w6, 0x923f82a4) + F_32(w7, 0xab1c5ed5) + F_32(w8, 0xd807aa98) + F_32(w9, 0x12835b01) + F_32(w10, 0x243185be) + F_32(w11, 0x550c7dc3) + F_32(w12, 0x72be5d74) + F_32(w13, 0x80deb1fe) + F_32(w14, 0x9bdc06a7) + F_32(w15, 0xc19bf174) + + EXPAND_32 + + F_32(w0, 0xe49b69c1) + F_32(w1, 0xefbe4786) + F_32(w2, 0x0fc19dc6) + F_32(w3, 0x240ca1cc) + F_32(w4, 0x2de92c6f) + F_32(w5, 0x4a7484aa) + F_32(w6, 0x5cb0a9dc) + F_32(w7, 0x76f988da) + F_32(w8, 0x983e5152) + F_32(w9, 0xa831c66d) + F_32(w10, 0xb00327c8) + F_32(w11, 0xbf597fc7) + F_32(w12, 0xc6e00bf3) + F_32(w13, 0xd5a79147) + F_32(w14, 0x06ca6351) + F_32(w15, 0x14292967) + + EXPAND_32 + + F_32(w0, 0x27b70a85) + F_32(w1, 0x2e1b2138) + F_32(w2, 0x4d2c6dfc) + F_32(w3, 0x53380d13) + F_32(w4, 0x650a7354) + F_32(w5, 0x766a0abb) + F_32(w6, 0x81c2c92e) + F_32(w7, 0x92722c85) + F_32(w8, 0xa2bfe8a1) + F_32(w9, 0xa81a664b) + F_32(w10, 0xc24b8b70) + F_32(w11, 0xc76c51a3) + F_32(w12, 0xd192e819) + F_32(w13, 0xd6990624) + F_32(w14, 0xf40e3585) + F_32(w15, 0x106aa070) + + EXPAND_32 + + F_32(w0, 0x19a4c116) + F_32(w1, 0x1e376c08) + F_32(w2, 0x2748774c) + F_32(w3, 0x34b0bcb5) + F_32(w4, 0x391c0cb3) + F_32(w5, 0x4ed8aa4a) + F_32(w6, 0x5b9cca4f) + F_32(w7, 0x682e6ff3) + F_32(w8, 0x748f82ee) + F_32(w9, 0x78a5636f) + F_32(w10, 0x84c87814) + F_32(w11, 0x8cc70208) + F_32(w12, 0x90befffa) + F_32(w13, 0xa4506ceb) + F_32(w14, 0xbef9a3f7) + F_32(w15, 0xc67178f2) + + a += state[0]; + b += state[1]; + c += state[2]; + d += state[3]; + e += state[4]; + f += state[5]; + g += state[6]; + h += state[7]; + + state[0] = a; + state[1] = b; + state[2] = c; + state[3] = d; + state[4] = e; + state[5] = f; + state[6] = g; + state[7] = h; + + in += 64; + inlen -= 64; + } + + store_bigendian_32(statebytes + 0, state[0]); + store_bigendian_32(statebytes + 4, state[1]); + store_bigendian_32(statebytes + 8, state[2]); + store_bigendian_32(statebytes + 12, state[3]); + store_bigendian_32(statebytes + 16, state[4]); + store_bigendian_32(statebytes + 20, state[5]); + store_bigendian_32(statebytes + 24, state[6]); + store_bigendian_32(statebytes + 28, state[7]); + + return inlen; +} + +static INTN crypto_hashblocks_sha512(UINT8 *statebytes,const UINT8 *in,UINT64 inlen) +{ + UINT64 state[8]; + UINT64 a; + UINT64 b; + UINT64 c; + UINT64 d; + UINT64 e; + UINT64 f; + UINT64 g; + UINT64 h; + UINT64 T1; + UINT64 T2; + + a = load_bigendian_64(statebytes + 0); state[0] = a; + b = load_bigendian_64(statebytes + 8); state[1] = b; + c = load_bigendian_64(statebytes + 16); state[2] = c; + d = load_bigendian_64(statebytes + 24); state[3] = d; + e = load_bigendian_64(statebytes + 32); state[4] = e; + f = load_bigendian_64(statebytes + 40); state[5] = f; + g = load_bigendian_64(statebytes + 48); state[6] = g; + h = load_bigendian_64(statebytes + 56); state[7] = h; + + while (inlen >= 128) { + UINT64 w0 = load_bigendian_64(in + 0); + UINT64 w1 = load_bigendian_64(in + 8); + UINT64 w2 = load_bigendian_64(in + 16); + UINT64 w3 = load_bigendian_64(in + 24); + UINT64 w4 = load_bigendian_64(in + 32); + UINT64 w5 = load_bigendian_64(in + 40); + UINT64 w6 = load_bigendian_64(in + 48); + UINT64 w7 = load_bigendian_64(in + 56); + UINT64 w8 = load_bigendian_64(in + 64); + UINT64 w9 = load_bigendian_64(in + 72); + UINT64 w10 = load_bigendian_64(in + 80); + UINT64 w11 = load_bigendian_64(in + 88); + UINT64 w12 = load_bigendian_64(in + 96); + UINT64 w13 = load_bigendian_64(in + 104); + UINT64 w14 = load_bigendian_64(in + 112); + UINT64 w15 = load_bigendian_64(in + 120); + + F_64(w0 ,0x428a2f98d728ae22ULL) + F_64(w1 ,0x7137449123ef65cdULL) + F_64(w2 ,0xb5c0fbcfec4d3b2fULL) + F_64(w3 ,0xe9b5dba58189dbbcULL) + F_64(w4 ,0x3956c25bf348b538ULL) + F_64(w5 ,0x59f111f1b605d019ULL) + F_64(w6 ,0x923f82a4af194f9bULL) + F_64(w7 ,0xab1c5ed5da6d8118ULL) + F_64(w8 ,0xd807aa98a3030242ULL) + F_64(w9 ,0x12835b0145706fbeULL) + F_64(w10,0x243185be4ee4b28cULL) + F_64(w11,0x550c7dc3d5ffb4e2ULL) + F_64(w12,0x72be5d74f27b896fULL) + F_64(w13,0x80deb1fe3b1696b1ULL) + F_64(w14,0x9bdc06a725c71235ULL) + F_64(w15,0xc19bf174cf692694ULL) + + EXPAND_64 + + F_64(w0 ,0xe49b69c19ef14ad2ULL) + F_64(w1 ,0xefbe4786384f25e3ULL) + F_64(w2 ,0x0fc19dc68b8cd5b5ULL) + F_64(w3 ,0x240ca1cc77ac9c65ULL) + F_64(w4 ,0x2de92c6f592b0275ULL) + F_64(w5 ,0x4a7484aa6ea6e483ULL) + F_64(w6 ,0x5cb0a9dcbd41fbd4ULL) + F_64(w7 ,0x76f988da831153b5ULL) + F_64(w8 ,0x983e5152ee66dfabULL) + F_64(w9 ,0xa831c66d2db43210ULL) + F_64(w10,0xb00327c898fb213fULL) + F_64(w11,0xbf597fc7beef0ee4ULL) + F_64(w12,0xc6e00bf33da88fc2ULL) + F_64(w13,0xd5a79147930aa725ULL) + F_64(w14,0x06ca6351e003826fULL) + F_64(w15,0x142929670a0e6e70ULL) + + EXPAND_64 + + F_64(w0 ,0x27b70a8546d22ffcULL) + F_64(w1 ,0x2e1b21385c26c926ULL) + F_64(w2 ,0x4d2c6dfc5ac42aedULL) + F_64(w3 ,0x53380d139d95b3dfULL) + F_64(w4 ,0x650a73548baf63deULL) + F_64(w5 ,0x766a0abb3c77b2a8ULL) + F_64(w6 ,0x81c2c92e47edaee6ULL) + F_64(w7 ,0x92722c851482353bULL) + F_64(w8 ,0xa2bfe8a14cf10364ULL) + F_64(w9 ,0xa81a664bbc423001ULL) + F_64(w10,0xc24b8b70d0f89791ULL) + F_64(w11,0xc76c51a30654be30ULL) + F_64(w12,0xd192e819d6ef5218ULL) + F_64(w13,0xd69906245565a910ULL) + F_64(w14,0xf40e35855771202aULL) + F_64(w15,0x106aa07032bbd1b8ULL) + + EXPAND_64 + + F_64(w0 ,0x19a4c116b8d2d0c8ULL) + F_64(w1 ,0x1e376c085141ab53ULL) + F_64(w2 ,0x2748774cdf8eeb99ULL) + F_64(w3 ,0x34b0bcb5e19b48a8ULL) + F_64(w4 ,0x391c0cb3c5c95a63ULL) + F_64(w5 ,0x4ed8aa4ae3418acbULL) + F_64(w6 ,0x5b9cca4f7763e373ULL) + F_64(w7 ,0x682e6ff3d6b2b8a3ULL) + F_64(w8 ,0x748f82ee5defb2fcULL) + F_64(w9 ,0x78a5636f43172f60ULL) + F_64(w10,0x84c87814a1f0ab72ULL) + F_64(w11,0x8cc702081a6439ecULL) + F_64(w12,0x90befffa23631e28ULL) + F_64(w13,0xa4506cebde82bde9ULL) + F_64(w14,0xbef9a3f7b2c67915ULL) + F_64(w15,0xc67178f2e372532bULL) + + EXPAND_64 + + F_64(w0 ,0xca273eceea26619cULL) + F_64(w1 ,0xd186b8c721c0c207ULL) + F_64(w2 ,0xeada7dd6cde0eb1eULL) + F_64(w3 ,0xf57d4f7fee6ed178ULL) + F_64(w4 ,0x06f067aa72176fbaULL) + F_64(w5 ,0x0a637dc5a2c898a6ULL) + F_64(w6 ,0x113f9804bef90daeULL) + F_64(w7 ,0x1b710b35131c471bULL) + F_64(w8 ,0x28db77f523047d84ULL) + F_64(w9 ,0x32caab7b40c72493ULL) + F_64(w10,0x3c9ebe0a15c9bebcULL) + F_64(w11,0x431d67c49c100d4cULL) + F_64(w12,0x4cc5d4becb3e42b6ULL) + F_64(w13,0x597f299cfc657e2aULL) + F_64(w14,0x5fcb6fab3ad6faecULL) + F_64(w15,0x6c44198c4a475817ULL) + + a += state[0]; + b += state[1]; + c += state[2]; + d += state[3]; + e += state[4]; + f += state[5]; + g += state[6]; + h += state[7]; + + state[0] = a; + state[1] = b; + state[2] = c; + state[3] = d; + state[4] = e; + state[5] = f; + state[6] = g; + state[7] = h; + + in += 128; + inlen -= 128; + } + + store_bigendian_64(statebytes + 0,state[0]); + store_bigendian_64(statebytes + 8,state[1]); + store_bigendian_64(statebytes + 16,state[2]); + store_bigendian_64(statebytes + 24,state[3]); + store_bigendian_64(statebytes + 32,state[4]); + store_bigendian_64(statebytes + 40,state[5]); + store_bigendian_64(statebytes + 48,state[6]); + store_bigendian_64(statebytes + 56,state[7]); + + return inlen; +} + + +static const UINT8 iv_256[32] = { + 0x6a, 0x09, 0xe6, 0x67, 0xbb, 0x67, 0xae, 0x85, + 0x3c, 0x6e, 0xf3, 0x72, 0xa5, 0x4f, 0xf5, 0x3a, + 0x51, 0x0e, 0x52, 0x7f, 0x9b, 0x05, 0x68, 0x8c, + 0x1f, 0x83, 0xd9, 0xab, 0x5b, 0xe0, 0xcd, 0x19 +}; + +static const UINT8 iv_512[64] = { + 0x6a, 0x09, 0xe6, 0x67, 0xf3, 0xbc, 0xc9, 0x08, 0xbb, 0x67, 0xae, + 0x85, 0x84, 0xca, 0xa7, 0x3b, 0x3c, 0x6e, 0xf3, 0x72, 0xfe, 0x94, + 0xf8, 0x2b, 0xa5, 0x4f, 0xf5, 0x3a, 0x5f, 0x1d, 0x36, 0xf1, 0x51, + 0x0e, 0x52, 0x7f, 0xad, 0xe6, 0x82, 0xd1, 0x9b, 0x05, 0x68, 0x8c, + 0x2b, 0x3e, 0x6c, 0x1f, 0x1f, 0x83, 0xd9, 0xab, 0xfb, 0x41, 0xbd, + 0x6b, 0x5b, 0xe0, 0xcd, 0x19, 0x13, 0x7e, 0x21, 0x79 +}; + +VOID sha256_inc_init(UINT8 *state) { + for (UINTN i = 0; i < 32; ++i) { + state[i] = iv_256[i]; + } + for (UINTN i = 32; i < 40; ++i) { + state[i] = 0; + } +} + +VOID sha512_inc_init(UINT8 *state) { + for (UINTN i = 0; i < 64; ++i) { + state[i] = iv_512[i]; + } + for (UINTN i = 64; i < 72; ++i) { + state[i] = 0; + } +} + +VOID sha256_inc_blocks(UINT8 *state, const UINT8 *in, UINTN inblocks) { + UINT64 bytes = load_bigendian_64(state + 32); + + crypto_hashblocks_sha256(state, in, 64 * inblocks); + bytes += 64 * inblocks; + + store_bigendian_64(state + 32, bytes); +} + +VOID sha512_inc_blocks(UINT8 *state, const UINT8 *in, UINTN inblocks) { + UINT64 bytes = load_bigendian_64(state + 64); + + crypto_hashblocks_sha512(state, in, 128 * inblocks); + bytes += 128 * inblocks; + + store_bigendian_64(state + 64, bytes); +} + +VOID sha256_inc_finalize(UINT8 *out, UINT8 *state, const UINT8 *in, UINTN inlen) { + UINT8 padded[128]; + UINT64 bytes = load_bigendian_64(state + 32) + inlen; + + crypto_hashblocks_sha256(state, in, inlen); + in += inlen; + inlen &= 63; + in -= inlen; + + for (UINTN i = 0; i < inlen; ++i) { + padded[i] = in[i]; + } + padded[inlen] = 0x80; + + if (inlen < 56) { + for (UINTN i = inlen + 1; i < 56; ++i) { + padded[i] = 0; + } + padded[56] = (UINT8) (bytes >> 53); + padded[57] = (UINT8) (bytes >> 45); + padded[58] = (UINT8) (bytes >> 37); + padded[59] = (UINT8) (bytes >> 29); + padded[60] = (UINT8) (bytes >> 21); + padded[61] = (UINT8) (bytes >> 13); + padded[62] = (UINT8) (bytes >> 5); + padded[63] = (UINT8) (bytes << 3); + crypto_hashblocks_sha256(state, padded, 64); + } else { + for (UINTN i = inlen + 1; i < 120; ++i) { + padded[i] = 0; + } + padded[120] = (UINT8) (bytes >> 53); + padded[121] = (UINT8) (bytes >> 45); + padded[122] = (UINT8) (bytes >> 37); + padded[123] = (UINT8) (bytes >> 29); + padded[124] = (UINT8) (bytes >> 21); + padded[125] = (UINT8) (bytes >> 13); + padded[126] = (UINT8) (bytes >> 5); + padded[127] = (UINT8) (bytes << 3); + crypto_hashblocks_sha256(state, padded, 128); + } + + for (UINTN i = 0; i < 32; ++i) { + out[i] = state[i]; + } + +} + +VOID sha512_inc_finalize(UINT8 *out, UINT8 *state, const UINT8 *in, UINTN inlen) { + UINT8 padded[256]; + UINT64 bytes = load_bigendian_64(state + 64) + inlen; + + crypto_hashblocks_sha512(state, in, inlen); + in += inlen; + inlen &= 127; + in -= inlen; + + for (UINTN i = 0; i < inlen; ++i) { + padded[i] = in[i]; + } + padded[inlen] = 0x80; + + if (inlen < 112) { + for (UINTN i = inlen + 1; i < 119; ++i) { + padded[i] = 0; + } + padded[119] = (UINT8) (bytes >> 61); + padded[120] = (UINT8) (bytes >> 53); + padded[121] = (UINT8) (bytes >> 45); + padded[122] = (UINT8) (bytes >> 37); + padded[123] = (UINT8) (bytes >> 29); + padded[124] = (UINT8) (bytes >> 21); + padded[125] = (UINT8) (bytes >> 13); + padded[126] = (UINT8) (bytes >> 5); + padded[127] = (UINT8) (bytes << 3); + crypto_hashblocks_sha512(state, padded, 128); + } else { + for (UINTN i = inlen + 1; i < 247; ++i) { + padded[i] = 0; + } + padded[247] = (UINT8) (bytes >> 61); + padded[248] = (UINT8) (bytes >> 53); + padded[249] = (UINT8) (bytes >> 45); + padded[250] = (UINT8) (bytes >> 37); + padded[251] = (UINT8) (bytes >> 29); + padded[252] = (UINT8) (bytes >> 21); + padded[253] = (UINT8) (bytes >> 13); + padded[254] = (UINT8) (bytes >> 5); + padded[255] = (UINT8) (bytes << 3); + crypto_hashblocks_sha512(state, padded, 256); + } + + for (UINTN i = 0; i < 64; ++i) { + out[i] = state[i]; + } +} + +VOID sha256(UINT8 *out, const UINT8 *in, UINTN inlen) { + UINT8 state[40]; + + sha256_inc_init(state); + sha256_inc_finalize(out, state, in, inlen); +} + +VOID sha512(UINT8 *out, const UINT8 *in, UINTN inlen) { + UINT8 state[72]; + + sha512_inc_init(state); + sha512_inc_finalize(out, state, in, inlen); +} + +/** + * mgf1 function based on the SHA-256 hash function + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +VOID mgf1_256(UINT8 *out, UINTN outlen, + const UINT8 *in, UINTN inlen) +{ + SPX_VLA(UINT8, inbuf, inlen+4); + UINT8 outbuf[SPX_SHA256_OUTPUT_BYTES]; + UINTN i; + + CopyMem(inbuf, in, inlen); + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i+1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + u32_to_bytes(inbuf + inlen, i); + sha256(out, inbuf, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i*SPX_SHA256_OUTPUT_BYTES) { + u32_to_bytes(inbuf + inlen, i); + sha256(outbuf, inbuf, inlen + 4); + CopyMem(out, outbuf, outlen - i*SPX_SHA256_OUTPUT_BYTES); + } +} + +/* + * mgf1 function based on the SHA-512 hash function + */ +VOID mgf1_512(UINT8 *out, UINTN outlen, + const UINT8 *in, UINTN inlen) +{ + SPX_VLA(UINT8, inbuf, inlen+4); + UINT8 outbuf[SPX_SHA512_OUTPUT_BYTES]; + UINTN i; + + CopyMem(inbuf, in, inlen); + + /* While we can fit in at least another full block of SHA512 output.. */ + for (i = 0; (i+1)*SPX_SHA512_OUTPUT_BYTES <= outlen; i++) { + u32_to_bytes(inbuf + inlen, i); + sha512(out, inbuf, inlen + 4); + out += SPX_SHA512_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i*SPX_SHA512_OUTPUT_BYTES) { + u32_to_bytes(inbuf + inlen, i); + sha512(outbuf, inbuf, inlen + 4); + CopyMem(out, outbuf, outlen - i*SPX_SHA512_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded and state_seeded_512, which can then be + * reused in thash + **/ +VOID seed_state(spx_ctx *ctx) { + UINT8 block[SPX_SHA512_BLOCK_BYTES]; + UINTN i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = ctx->pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA512_BLOCK_BYTES; ++i) { + block[i] = 0; + } + /* block has been properly initialized for both SHA-256 and SHA-512 */ + + sha256_inc_init(ctx->state_seeded); + sha256_inc_blocks(ctx->state_seeded, block, 1); +#if SPX_SHA512 + sha512_inc_init(ctx->state_seeded_512); + sha512_inc_blocks(ctx->state_seeded_512, block, 1); +#endif +} diff --git a/Blastproof/src/libs/sphincsplus/sha2.h b/Blastproof/src/libs/sphincsplus/sha2.h new file mode 100644 index 0000000..c6a7977 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/sha2.h @@ -0,0 +1,46 @@ +#ifndef SPX_SHA2_H +#define SPX_SHA2_H + +#include "params.h" + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ + +#define SPX_SHA512_BLOCK_BYTES 128 +#define SPX_SHA512_OUTPUT_BYTES 64 + +#if SPX_SHA256_OUTPUT_BYTES < SPX_N + #error Linking against SHA-256 with N larger than 32 bytes is not supported +#endif + +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include +#include +#include +#include + +VOID sha256_inc_init(UINT8 *state); +VOID sha256_inc_blocks(UINT8 *state, const UINT8 *in, UINTN inblocks); +VOID sha256_inc_finalize(UINT8 *out, UINT8 *state, const UINT8 *in, UINTN inlen); +VOID sha256(UINT8 *out, const UINT8 *in, UINTN inlen); + +VOID sha512_inc_init(UINT8 *state); +VOID sha512_inc_blocks(UINT8 *state, const UINT8 *in, UINTN inblocks); +VOID sha512_inc_finalize(UINT8 *out, UINT8 *state, const UINT8 *in, UINTN inlen); +VOID sha512(UINT8 *out, const UINT8 *in, UINTN inlen); + +#define mgf1_256 SPX_NAMESPACE(mgf1_256) +VOID mgf1_256(UINT8 *out, UINTN outlen, + const UINT8 *in, UINTN inlen); + +#define mgf1_512 SPX_NAMESPACE(mgf1_512) +VOID mgf1_512(UINT8 *out, UINTN outlen, + const UINT8 *in, UINTN inlen); + +#define seed_state SPX_NAMESPACE(seed_state) +VOID seed_state(spx_ctx *ctx); + + +#endif diff --git a/Blastproof/src/libs/sphincsplus/sha2_offsets.h b/Blastproof/src/libs/sphincsplus/sha2_offsets.h new file mode 100644 index 0000000..49f7e85 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/sha2_offsets.h @@ -0,0 +1,20 @@ +#ifndef SHA2_OFFSETS_H_ +#define SHA2_OFFSETS_H_ + +/* + * Offsets of various fields in the address structure when we use SHA2 as + * the Sphincs+ hash function + */ + +#define SPX_OFFSET_LAYER 0 /* The byte used to specify the Merkle tree layer */ +#define SPX_OFFSET_TREE 1 /* The start of the 8 byte field used to specify the tree */ +#define SPX_OFFSET_TYPE 9 /* The byte used to specify the hash type (reason) */ +#define SPX_OFFSET_KP_ADDR 10 /* The start of the 4 byte field used to specify the key pair address */ +#define SPX_OFFSET_CHAIN_ADDR 17 /* The byte used to specify the chain address (which Winternitz chain) */ +#define SPX_OFFSET_HASH_ADDR 21 /* The byte used to specify the hash address (where in the Winternitz chain) */ +#define SPX_OFFSET_TREE_HGT 17 /* The byte used to specify the height of this node in the FORS or Merkle tree */ +#define SPX_OFFSET_TREE_INDEX 18 /* The start of the 4 byte field used to specify the node in the FORS or Merkle tree */ + +#define SPX_SHA2 1 + +#endif /* SHA2_OFFSETS_H_ */ diff --git a/Blastproof/src/libs/sphincsplus/sign.c b/Blastproof/src/libs/sphincsplus/sign.c new file mode 100644 index 0000000..0a5feb3 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/sign.c @@ -0,0 +1,289 @@ +#include +#include +#include +#include +#include + +#include "api.h" +#include "params.h" +#include "wots.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "address.h" +#include "utils.h" +#include "merkle.h" + +/* + * Returns the length of a secret key, in bytes + */ +UINT64 crypto_sign_secretkeybytes(VOID) +{ + return CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +UINT64 crypto_sign_publickeybytes(VOID) +{ + return CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +UINT64 crypto_sign_bytes(VOID) +{ + return CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +UINT64 crypto_sign_seedbytes(VOID) +{ + return CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +INTN crypto_sign_seed_keypair(UINT8 *pk, UINT8 *sk, + const UINT8 *seed) +{ + spx_ctx ctx; + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + CopyMem(sk, seed, CRYPTO_SEEDBYTES); + + CopyMem(pk, sk + 2*SPX_N, SPX_N); + + CopyMem(ctx.pub_seed, pk, SPX_N); + CopyMem(ctx.sk_seed, sk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + /* Compute root node of the top-most subtree. */ + merkle_gen_root(sk + 3*SPX_N, &ctx); + + CopyMem(pk + SPX_N, sk + 3*SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +#ifdef BP_CRYPTO_DEFINE_RANDOM_FLAG +INTN crypto_sign_keypair(UINT8 *pk, UINT8 *sk) +{ + UINT8 seed[CRYPTO_SEEDBYTES]; + randombytes(seed, CRYPTO_SEEDBYTES); + crypto_sign_seed_keypair(pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +INTN crypto_sign_signature(UINT8 *sig, UINTN *siglen, + const UINT8 *m, UINTN mlen, const UINT8 *sk) +{ + spx_ctx ctx; + + const UINT8 *sk_prf = sk + SPX_N; + const UINT8 *pk = sk + 2*SPX_N; + + UINT8 optrand[SPX_N]; + UINT8 mhash[SPX_FORS_MSG_BYTES]; + UINT8 root[SPX_N]; + UINT32 i; + UINT64 tree; + UINT32 idx_leaf; + UINT32 wots_addr[8] = {0}; + UINT32 tree_addr[8] = {0}; + + CopyMem(ctx.sk_seed, sk, SPX_N); + CopyMem(ctx.pub_seed, pk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + set_type(wots_addr, SPX_ADDR_TYPE_WOTS); + set_type(tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + gen_message_random(sig, sk_prf, optrand, m, mlen, &ctx); + + /* Derive the message digest and leaf index from R, PK and M. */ + hash_message(mhash, &tree, &idx_leaf, sig, pk, m, mlen, &ctx); + sig += SPX_N; + + set_tree_addr(wots_addr, tree); + set_keypair_addr(wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + fors_sign(sig, root, mhash, &ctx, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + set_layer_addr(tree_addr, i); + set_tree_addr(tree_addr, tree); + + copy_subtree_addr(wots_addr, tree_addr); + set_keypair_addr(wots_addr, idx_leaf); + + merkle_sign(sig, root, &ctx, wots_addr, tree_addr, idx_leaf); + sig += SPX_WOTS_BYTES + SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT)-1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} +#endif +/** + * Verifies a detached signature and message under a given public key. + */ +INTN crypto_sign_verify(const UINT8 *sig, UINTN siglen, + const UINT8 *m, UINTN mlen, const UINT8 *pk) +{ + spx_ctx ctx; + const UINT8 *pub_root = pk + SPX_N; + UINT8 mhash[SPX_FORS_MSG_BYTES]; + UINT8 wots_pk[SPX_WOTS_BYTES]; + UINT8 root[SPX_N]; + UINT8 leaf[SPX_N]; + UINT32 i; + UINT64 tree; + UINT32 idx_leaf; + UINT32 wots_addr[8] = {0}; + UINT32 tree_addr[8] = {0}; + UINT32 wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + CopyMem(ctx.pub_seed, pk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + set_type(wots_addr, SPX_ADDR_TYPE_WOTS); + set_type(tree_addr, SPX_ADDR_TYPE_HASHTREE); + set_type(wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + hash_message(mhash, &tree, &idx_leaf, sig, pk, m, mlen, &ctx); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + set_tree_addr(wots_addr, tree); + set_keypair_addr(wots_addr, idx_leaf); + + fors_pk_from_sig(root, sig, mhash, &ctx, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + set_layer_addr(tree_addr, i); + set_tree_addr(tree_addr, tree); + + copy_subtree_addr(wots_addr, tree_addr); + set_keypair_addr(wots_addr, idx_leaf); + + copy_keypair_addr(wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + wots_pk_from_sig(wots_pk, sig, root, &ctx, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + thash(leaf, wots_pk, SPX_WOTS_LEN, &ctx, wots_pk_addr); + + /* Compute the root node of this subtree. */ + compute_root(root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + &ctx, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT)-1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (CompareMem(root, pub_root, SPX_N)) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +INTN crypto_sign(UINT8 *sm, UINT64 *smlen, + const UINT8 *m, UINT64 mlen, + const UINT8 *sk) +{ + UINTN siglen; + + crypto_sign_signature(sm, &siglen, m, (UINTN)mlen, sk); + + CopyMem(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +INTN crypto_sign_open(UINT8 *m, UINT64 *mlen, + const UINT8 *sm, UINT64 smlen, + const UINT8 *pk) +{ + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + SetMem(m, smlen, 0); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (crypto_sign_verify(sm, SPX_BYTES, sm + SPX_BYTES, (UINTN)*mlen, pk)) { + SetMem(m, smlen, 0); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + CopyMem(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/Blastproof/src/libs/sphincsplus/thash.h b/Blastproof/src/libs/sphincsplus/thash.h new file mode 100644 index 0000000..d6d175a --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/thash.h @@ -0,0 +1,17 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include "context.h" +#include "params.h" + +#include +#include +#include +#include +#include + +#define thash SPX_NAMESPACE(thash) +VOID thash(UINT8 *out, const UINT8 *in, UINT32 inblocks, + const spx_ctx *ctx, UINT32 addr[8]); + +#endif diff --git a/Blastproof/src/libs/sphincsplus/thash_sha2_robust.c b/Blastproof/src/libs/sphincsplus/thash_sha2_robust.c new file mode 100644 index 0000000..0877a97 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/thash_sha2_robust.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +#include "thash.h" +#include "address.h" +#include "params.h" +#include "utils.h" +#include "sha2.h" + +#if SPX_SHA512 +static VOID thash_512(UINT8 *out, const UINT8 *in, UINT32 inblocks, + const spx_ctx *ctx, UINT32 addr[8]); +#endif + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +VOID thash(UINT8 *out, const UINT8 *in, UINT32 inblocks, + const spx_ctx *ctx, UINT32 addr[8]) +{ +#if SPX_SHA512 + if (inblocks > 1) { + thash_512(out, in, inblocks, ctx, addr); + return; + } +#endif + UINT8 outbuf[SPX_SHA256_OUTPUT_BYTES]; + SPX_VLA(UINT8, bitmask, inblocks * SPX_N); + SPX_VLA(UINT8, buf, SPX_N + SPX_SHA256_OUTPUT_BYTES + inblocks*SPX_N); + UINT8 sha2_state[40]; + UINT32 i; + + CopyMem(buf, ctx->pub_seed, SPX_N); + CopyMem(buf + SPX_N, addr, SPX_SHA256_ADDR_BYTES); + mgf1_256(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + CopyMem(sha2_state, ctx->state_seeded, 40 * sizeof(UINT8)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + CopyMem(out, outbuf, SPX_N); +} + +#if SPX_SHA512 +static VOID thash_512(UINT8 *out, const UINT8 *in, UINT32 inblocks, + const spx_ctx *ctx, UINT32 addr[8]) +{ + UINT8 outbuf[SPX_SHA512_OUTPUT_BYTES]; + SPX_VLA(UINT8, bitmask, inblocks * SPX_N); + SPX_VLA(UINT8, buf, SPX_N + SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + UINT8 sha2_state[72]; + UINT32 i; + + CopyMem(buf, ctx->pub_seed, SPX_N); + CopyMem(buf + SPX_N, addr, SPX_SHA256_ADDR_BYTES); + mgf1_512(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + CopyMem(sha2_state, ctx->state_seeded_512, 72 * sizeof(UINT8)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha512_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + CopyMem(out, outbuf, SPX_N); +} +#endif diff --git a/Blastproof/src/libs/sphincsplus/thash_sha2_simple.c b/Blastproof/src/libs/sphincsplus/thash_sha2_simple.c new file mode 100644 index 0000000..b8cad4e --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/thash_sha2_simple.c @@ -0,0 +1,62 @@ + #include + #include + #include + #include + #include + +#include "thash.h" +#include "address.h" +#include "params.h" +#include "utils.h" +#include "sha2.h" + +#if SPX_SHA512 +static VOID thash_512(UINT8 *out, const UINT8 *in, UINT32 inblocks, + const spx_ctx *ctx, UINT32 addr[8]); +#endif + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +VOID thash(UINT8 *out, const UINT8 *in, UINT32 inblocks, + const spx_ctx *ctx, UINT32 addr[8]) +{ +#if SPX_SHA512 + if (inblocks > 1) { + thash_512(out, in, inblocks, ctx, addr); + return; + } +#endif + + UINT8 outbuf[SPX_SHA256_OUTPUT_BYTES]; + UINT8 sha2_state[40]; + SPX_VLA(UINT8, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + + /* Retrieve precomputed state containing pub_seed */ + CopyMem(sha2_state, ctx->state_seeded, 40 * sizeof(UINT8)); + + CopyMem(buf, addr, SPX_SHA256_ADDR_BYTES); + CopyMem(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + CopyMem(out, outbuf, SPX_N); +} + +#if SPX_SHA512 +static VOID thash_512(UINT8 *out, const UINT8 *in, UINT32 inblocks, + const spx_ctx *ctx, UINT32 addr[8]) +{ + UINT8 outbuf[SPX_SHA512_OUTPUT_BYTES]; + UINT8 sha2_state[72]; + SPX_VLA(UINT8, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + + /* Retrieve precomputed state containing pub_seed */ + CopyMem(sha2_state, ctx->state_seeded_512, 72 * sizeof(UINT8)); + + CopyMem(buf, addr, SPX_SHA256_ADDR_BYTES); + CopyMem(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha512_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks*SPX_N); + CopyMem(out, outbuf, SPX_N); +} +#endif diff --git a/Blastproof/src/libs/sphincsplus/utils.c b/Blastproof/src/libs/sphincsplus/utils.c new file mode 100644 index 0000000..1e17111 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/utils.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include + +#include "utils.h" +#include "params.h" +#include "hash.h" +#include "thash.h" +#include "address.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +VOID ull_to_bytes(UINT8 *out, UINT32 outlen, + UINT64 in) +{ + INTN i; + + /* Iterate over out in decreasing order, for big-endianness. */ + for (i = (INT32)outlen - 1; i >= 0; i--) { + out[i] = in & 0xff; + in = in >> 8; + } +} + +VOID u32_to_bytes(UINT8 *out, UINT32 in) +{ + out[0] = (UINT8)(in >> 24); + out[1] = (UINT8)(in >> 16); + out[2] = (UINT8)(in >> 8); + out[3] = (UINT8)in; +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +UINT64 bytes_to_ull(const UINT8 *in, UINT32 inlen) +{ + UINT64 retval = 0; + UINT32 i; + + for (i = 0; i < inlen; i++) { + retval |= ((UINT64)in[i]) << (8*(inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +VOID compute_root(UINT8 *root, const UINT8 *leaf, + UINT32 leaf_idx, UINT32 idx_offset, + const UINT8 *auth_path, UINT32 tree_height, + const spx_ctx *ctx, UINT32 addr[8]) +{ + UINT32 i; + UINT8 buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + CopyMem(buffer + SPX_N, leaf, SPX_N); + CopyMem(buffer, auth_path, SPX_N); + } + else { + CopyMem(buffer, leaf, SPX_N); + CopyMem(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + set_tree_height(addr, i + 1); + set_tree_index(addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + thash(buffer + SPX_N, buffer, 2, ctx, addr); + CopyMem(buffer, auth_path, SPX_N); + } + else { + thash(buffer, buffer, 2, ctx, addr); + CopyMem(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + set_tree_height(addr, tree_height); + set_tree_index(addr, leaf_idx + idx_offset); + thash(root, buffer, 2, ctx, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +VOID treehash(UINT8 *root, UINT8 *auth_path, const spx_ctx* ctx, + UINT32 leaf_idx, UINT32 idx_offset, UINT32 tree_height, + VOID (*gen_leaf)( + UINT8* /* leaf */, + const spx_ctx* /* ctx */, + UINT32 /* addr_idx */, const UINT32[8] /* tree_addr */), + UINT32 tree_addr[8]) +{ + SPX_VLA(UINT8, stack, (tree_height+1)*SPX_N); + SPX_VLA(UINT32, heights, tree_height+1); + UINT32 offset = 0; + UINT32 idx; + UINT32 tree_idx; + + for (idx = 0; idx < (UINT32)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset*SPX_N, ctx, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + CopyMem(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + set_tree_height(tree_addr, heights[offset - 1] + 1); + set_tree_index(tree_addr, + tree_idx + (idx_offset >> (heights[offset-1] + 1))); + /* Hash the top-most nodes from the stack together. */ + thash(stack + (offset - 2)*SPX_N, + stack + (offset - 2)*SPX_N, 2, ctx, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + CopyMem(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + CopyMem(root, stack, SPX_N); +} diff --git a/Blastproof/src/libs/sphincsplus/utils.h b/Blastproof/src/libs/sphincsplus/utils.h new file mode 100644 index 0000000..02f9f15 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/utils.h @@ -0,0 +1,69 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include +#include +#include +#include +#include + +#include "params.h" +#include "context.h" + + +/* To support MSVC use alloca() instead of VLAs. See #20. */ +#ifdef _MSC_VER +/* MSVC defines _alloca in malloc.h */ +# include +/* Note: _malloca(), which is recommended over deprecated _alloca, + requires that you call _freea(). So we stick with _alloca */ +# define SPX_VLA(__t,__x,__s) __t *__x = (__t*)_alloca((__s)*sizeof(__t)) +#else +# define SPX_VLA(__t,__x,__s) __t __x[__s] +#endif + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +#define ull_to_bytes SPX_NAMESPACE(ull_to_bytes) +VOID ull_to_bytes(UINT8 *out, UINT32 outlen, + UINT64 in); +#define u32_to_bytes SPX_NAMESPACE(u32_to_bytes) +VOID u32_to_bytes(UINT8 *out, UINT32 in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +#define bytes_to_ull SPX_NAMESPACE(bytes_to_ull) +UINT64 bytes_to_ull(const UINT8 *in, UINT32 inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +#define compute_root SPX_NAMESPACE(compute_root) +VOID compute_root(UINT8 *root, const UINT8 *leaf, + UINT32 leaf_idx, UINT32 idx_offset, + const UINT8 *auth_path, UINT32 tree_height, + const spx_ctx *ctx, UINT32 addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +#define treehash SPX_NAMESPACE(treehash) +VOID treehash(UINT8 *root, UINT8 *auth_path, + const spx_ctx* ctx, + UINT32 leaf_idx, UINT32 idx_offset, UINT32 tree_height, + VOID (*gen_leaf)( + UINT8* /* leaf */, + const spx_ctx* ctx /* ctx */, + UINT32 /* addr_idx */, const UINT32[8] /* tree_addr */), + UINT32 tree_addr[8]); + + +#endif diff --git a/Blastproof/src/libs/sphincsplus/utilsx1.c b/Blastproof/src/libs/sphincsplus/utilsx1.c new file mode 100644 index 0000000..3f81840 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/utilsx1.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include + +#include "utils.h" +#include "utilsx1.h" +#include "params.h" +#include "thash.h" +#include "address.h" + +/* + * Generate the entire Merkle tree, computing the authentication path for + * leaf_idx, and the resulting root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE) + * + * This expects tree_addr to be initialized to the addr structures for the + * Merkle tree nodes + * + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + * + * This works by using the standard Merkle tree building algorithm, + */ +VOID treehashx1(UINT8 *root, UINT8 *auth_path, + const spx_ctx* ctx, + UINT32 leaf_idx, UINT32 idx_offset, + UINT32 tree_height, + VOID (*gen_leaf)( + UINT8* /* Where to write the leaves */, + const spx_ctx* /* ctx */, + UINT32 idx, VOID *info), + UINT32 tree_addr[8], + VOID *info) +{ + /* This is where we keep the intermediate nodes */ + SPX_VLA(UINT8, stack, tree_height*SPX_N); + + UINT32 idx; + UINT32 max_idx = (UINT32)((1 << tree_height) - 1); + for (idx = 0;; idx++) { + UINT8 current[2*SPX_N]; /* Current logical node is at */ + /* index[SPX_N]. We do this to minimize the number of copies */ + /* needed during a thash */ + gen_leaf( ¤t[SPX_N], ctx, idx + idx_offset, + info ); + + /* Now combine the freshly generated right node with previously */ + /* generated left ones */ + UINT32 internal_idx_offset = idx_offset; + UINT32 internal_idx = idx; + UINT32 internal_leaf = leaf_idx; + UINT32 h; /* The height we are in the Merkle tree */ + for (h=0;; h++, internal_idx >>= 1, internal_leaf >>= 1) { + + /* Check if we hit the top of the tree */ + if (h == tree_height) { + /* We hit the root; return it */ + CopyMem( root, ¤t[SPX_N], SPX_N ); + return; + } + + /* + * Check if the node we have is a part of the + * authentication path; if it is, write it out + */ + if ((internal_idx ^ internal_leaf) == 0x01) { + CopyMem( &auth_path[ h * SPX_N ], + ¤t[SPX_N], + SPX_N ); + } + + /* + * Check if we're at a left child; if so, stop going up the stack + * Exception: if we've reached the end of the tree, keep on going + * (so we combine the last 4 nodes into the one root node in two + * more iterations) + */ + if ((internal_idx & 1) == 0 && idx < max_idx) { + break; + } + + /* Ok, we're at a right node */ + /* Now combine the left and right logical nodes together */ + + /* Set the address of the node we're creating. */ + internal_idx_offset >>= 1; + set_tree_height(tree_addr, h + 1); + set_tree_index(tree_addr, internal_idx/2 + internal_idx_offset ); + + UINT8 *left = &stack[h * SPX_N]; + CopyMem( ¤t[0], left, SPX_N ); + thash( ¤t[1 * SPX_N], + ¤t[0 * SPX_N], + 2, ctx, tree_addr); + } + + /* We've hit a left child; save the current for when we get the */ + /* corresponding right right */ + CopyMem( &stack[h * SPX_N], ¤t[SPX_N], SPX_N); + } +} diff --git a/Blastproof/src/libs/sphincsplus/utilsx1.h b/Blastproof/src/libs/sphincsplus/utilsx1.h new file mode 100644 index 0000000..f66c444 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/utilsx1.h @@ -0,0 +1,30 @@ +#ifndef SPX_UTILSX4_H +#define SPX_UTILSX4_H + +#include +#include +#include +#include +#include +#include "params.h" +#include "context.h" + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +#define treehashx1 SPX_NAMESPACE(treehashx1) +VOID treehashx1(UINT8 *root, UINT8 *auth_path, + const spx_ctx* ctx, + UINT32 leaf_idx, UINT32 idx_offset, UINT32 tree_height, + VOID (*gen_leaf)( + UINT8* /* Where to write the leaf */, + const spx_ctx* /* ctx */, + UINT32 addr_idx, VOID *info), + UINT32 tree_addrx4[8], VOID *info); + +#endif diff --git a/Blastproof/src/libs/sphincsplus/wots.c b/Blastproof/src/libs/sphincsplus/wots.c new file mode 100644 index 0000000..92d8529 --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/wots.c @@ -0,0 +1,115 @@ + #include + #include + #include + #include + #include + +#include "utils.h" +#include "utilsx1.h" +#include "hash.h" +#include "thash.h" +#include "wots.h" +#include "wotsx1.h" +#include "address.h" +#include "params.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static VOID gen_chain(UINT8 *out, const UINT8 *in, + UINT32 start, UINT32 steps, + const spx_ctx *ctx, UINT32 addr[8]) +{ + UINT32 i; + + /* Initialize out with the value at position 'start'. */ + CopyMem(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start+steps) && i < SPX_WOTS_W; i++) { + set_hash_addr(addr, i); + thash(out, out, 1, ctx, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static VOID base_w(UINT32 *output, const INTN out_len, + const UINT8 *input) +{ + INTN in = 0; + INTN out = 0; + UINT8 total; + INTN bits = 0; + INTN consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (total >> bits) & (SPX_WOTS_W - 1); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static VOID wots_checksum(UINT32 *csum_base_w, + const UINT32 *msg_base_w) +{ + UINT32 csum = 0; + UINT8 csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + UINT32 i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << ((8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)) % 8); + ull_to_bytes(csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +VOID chain_lengths(UINT32 *lengths, const UINT8 *msg) +{ + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +VOID wots_pk_from_sig(UINT8 *pk, + const UINT8 *sig, const UINT8 *msg, + const spx_ctx *ctx, UINT32 addr[8]) +{ + UINT32 lengths[SPX_WOTS_LEN]; + UINT32 i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + set_chain_addr(addr, i); + gen_chain(pk + i*SPX_N, sig + i*SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], ctx, addr); + } +} diff --git a/Blastproof/src/libs/sphincsplus/wots.h b/Blastproof/src/libs/sphincsplus/wots.h new file mode 100644 index 0000000..867f4ad --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/wots.h @@ -0,0 +1,29 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include +#include +#include +#include +#include + +#include "params.h" +#include "context.h" + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +#define wots_pk_from_sig SPX_NAMESPACE(wots_pk_from_sig) +VOID wots_pk_from_sig(UINT8 *pk, + const UINT8 *sig, const UINT8 *msg, + const spx_ctx *ctx, UINT32 addr[8]); + +/* + * Compute the chain lengths needed for a given message hash + */ +#define chain_lengths SPX_NAMESPACE(chain_lengths) +VOID chain_lengths(UINT32 *lengths, const UINT8 *msg); + +#endif diff --git a/Blastproof/src/libs/sphincsplus/wotsx1.c b/Blastproof/src/libs/sphincsplus/wotsx1.c new file mode 100644 index 0000000..d593b5f --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/wotsx1.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include + +#include "utils.h" +#include "hash.h" +#include "thash.h" +#include "wots.h" +#include "wotsx1.h" +#include "address.h" +#include "params.h" + +/* + * This generates a WOTS public key + * It also generates the WOTS signature if leaf_info indicates + * that we're signing with this WOTS key + */ +VOID wots_gen_leafx1(UINT8 *dest, + const spx_ctx *ctx, + UINT32 leaf_idx, VOID *v_info) { + struct leaf_info_x1 *info = v_info; + UINT32 *leaf_addr = info->leaf_addr; + UINT32 *pk_addr = info->pk_addr; + UINT32 i, k; + UINT8 pk_buffer[ SPX_WOTS_BYTES ]; + UINT8 *buffer; + UINT32 wots_k_mask; + + if (leaf_idx == info->wots_sign_leaf) { + /* We're traversing the leaf that's signing; generate the WOTS */ + /* signature */ + wots_k_mask = 0; + } else { + /* Nope, we're just generating pk's; turn off the signature logic */ + wots_k_mask = (UINT32)~0; + } + + set_keypair_addr( leaf_addr, leaf_idx ); + set_keypair_addr( pk_addr, leaf_idx ); + + for (i = 0, buffer = pk_buffer; i < SPX_WOTS_LEN; i++, buffer += SPX_N) { + UINT32 wots_k = info->wots_steps[i] | wots_k_mask; /* Set wots_k to */ + /* the step if we're generating a signature, ~0 if we're not */ + + /* Start with the secret seed */ + set_chain_addr(leaf_addr, i); + set_hash_addr(leaf_addr, 0); + set_type(leaf_addr, SPX_ADDR_TYPE_WOTSPRF); + + prf_addr(buffer, ctx, leaf_addr); + + set_type(leaf_addr, SPX_ADDR_TYPE_WOTS); + + /* Iterate down the WOTS chain */ + for (k=0;; k++) { + /* Check if this is the value that needs to be saved as a */ + /* part of the WOTS signature */ + if (k == wots_k) { + CopyMem( info->wots_sig + i * SPX_N, buffer, SPX_N ); + } + + /* Check if we hit the top of the chain */ + if (k == SPX_WOTS_W - 1) break; + + /* Iterate one step on the chain */ + set_hash_addr(leaf_addr, k); + + thash(buffer, buffer, 1, ctx, leaf_addr); + } + } + + /* Do the final thash to generate the public keys */ + thash(dest, pk_buffer, SPX_WOTS_LEN, ctx, pk_addr); +} diff --git a/Blastproof/src/libs/sphincsplus/wotsx1.h b/Blastproof/src/libs/sphincsplus/wotsx1.h new file mode 100644 index 0000000..c2bdb8d --- /dev/null +++ b/Blastproof/src/libs/sphincsplus/wotsx1.h @@ -0,0 +1,40 @@ +#if !defined( WOTSX1_H_ ) +#define WOTSX1_H_ + +#include +#include +#include +#include +#include + +/* + * This is here to provide an interface to the internal wots_gen_leafx1 + * routine. While this routine is not referenced in the package outside of + * wots.c, it is called from the stand-alone benchmark code to characterize + * the performance + */ +struct leaf_info_x1 { + UINT8 *wots_sig; + UINT32 wots_sign_leaf; /* The index of the WOTS we're using to sign */ + UINT32 *wots_steps; + UINT32 leaf_addr[8]; + UINT32 pk_addr[8]; +}; + +/* Macro to set the leaf_info to something 'benign', that is, it would */ +/* run with the same time as it does during the real signing process */ +/* Used only by the benchmark code */ +#define INITIALIZE_LEAF_INFO_X1(info, addr, step_buffer) { \ + info.wots_sig = 0; \ + info.wots_sign_leaf = ~0u; \ + info.wots_steps = step_buffer; \ + CopyMem( &info.leaf_addr[0], addr, 32 ); \ + CopyMem( &info.pk_addr[0], addr, 32 ); \ +} + +#define wots_gen_leafx1 SPX_NAMESPACE(wots_gen_leafx1) +VOID wots_gen_leafx1(UINT8 *dest, + const spx_ctx *ctx, + UINT32 leaf_idx, VOID *v_info); + +#endif /* WOTSX1_H_ */ diff --git a/Blastproof/src/libs/src/conf.c b/Blastproof/src/libs/src/conf.c new file mode 100644 index 0000000..0612548 --- /dev/null +++ b/Blastproof/src/libs/src/conf.c @@ -0,0 +1,432 @@ +#include "../include/conf.h" +#include "../include/default.h" +#include +#include +#include +UINT8 bp_conf_hex_char_to_uint8(CHAR16 c) { + if (c>=L'0' && c<=L'9') return c-L'0'; + if (c>=L'a' && c<=L'f') return c-L'a'+10; + if (c>=L'A' && c<=L'F') return c-L'A'+10; + return 0xFF; +} +EFI_STATUS bp_conf_get_lines_number(bp_disk_FILE *file,UINTN *line_number) { + if (file==NULL || line_number==NULL) { + return EFI_INVALID_PARAMETER; + } + *line_number=0; + for (UINTN i=0;ifile_size_bytes;++i) { + if (file->content[i]=='\n') { + (*line_number)++; + } + } + if (file->file_size_bytes>0 && file->content[file->file_size_bytes-1]!='\n') { + (*line_number)++; + } + return EFI_SUCCESS; +} +EFI_STATUS bp_conf_parse_file_lines(bp_disk_FILE *file,bp_conf_LINED_FILE **lined_file) { + if (file==NULL || lined_file==NULL) { + return EFI_INVALID_PARAMETER; + } + EFI_STATUS status; + UINTN line_count=0; + status=bp_conf_get_lines_number(file,&line_count); + if (EFI_ERROR(status)) { + return status; + } + *lined_file=AllocateZeroPool(sizeof(bp_conf_LINED_FILE)); + if (*lined_file==NULL) { + return EFI_OUT_OF_RESOURCES; + } + (*lined_file)->count=0; + (*lined_file)->capacity=line_count; + (*lined_file)->lines=AllocateZeroPool(sizeof(bp_conf_LINE)*line_count); + if ((*lined_file)->lines==NULL) { + FreePool(*lined_file); + return EFI_OUT_OF_RESOURCES; + } + UINTN start=0; + for (UINTN i=0;ifile_size_bytes;i++) { + if (i==file->file_size_bytes || file->content[i]=='\n') { + UINTN len=i-start; + CHAR16 *line_buf=AllocateZeroPool(sizeof(CHAR16)*(len+1)); + if (line_buf==NULL) { + for (UINTN j=0;j<(*lined_file)->count;j++) { + FreePool((*lined_file)->lines[j].line_content); + } + FreePool((*lined_file)->lines); + FreePool(*lined_file); + return EFI_OUT_OF_RESOURCES; + } + for (UINTN j=0;jcontent[start + j]; + } + (*lined_file)->lines[(*lined_file)->count].line_content=line_buf; + (*lined_file)->lines[(*lined_file)->count].length=len; + (*lined_file)->count++; + start=i+1; + } + } + return EFI_SUCCESS; +} +EFI_STATUS bp_conf_parse_conf(bp_conf_LINED_FILE *lined_file,bp_conf_CONF **conf) { + EFI_STATUS status=EFI_SUCCESS; + *conf=AllocateZeroPool(sizeof(bp_conf_CONF)); + if (*conf==NULL) { + return EFI_OUT_OF_RESOURCES; + } + UINTN count_of_pairs=0; + for (UINTN i=0;icount;++i) { + if (lined_file->lines[i].length>1) { + if (lined_file->lines[i].line_content[0]==L'#') { + continue; + } else { + count_of_pairs=count_of_pairs+1; + } + } else { + continue; + } + } + (*conf)->count=0; + (*conf)->capacity=count_of_pairs; + (*conf)->pairs=AllocateZeroPool(sizeof(bp_conf_PAIR)*count_of_pairs); + if ((*conf)->pairs==NULL) { + FreePool(*conf); + return EFI_OUT_OF_RESOURCES; + } + for (UINTN i=0;icount;++i) { + if (lined_file->lines[i].length>1) { + if (lined_file->lines[i].line_content[0]==L'#') { + continue; + } else { + UINTN egal_index=0; + for (UINTN y=0;ylines[i].length;++y) { + if (lined_file->lines[i].line_content[y]==L'=') { + egal_index=y; + break; + } + } + if (egal_index==lined_file->lines[i].length-1 || egal_index==0) { + return EFI_INVALID_PARAMETER; + } + CHAR16 *key=AllocateZeroPool(sizeof(CHAR16)*(egal_index+1)); + CHAR16 *value=AllocateZeroPool(sizeof(CHAR16)*(lined_file->lines[i].length-egal_index)); + if (key==NULL || value==NULL) { + return EFI_OUT_OF_RESOURCES; + } + for (UINTN k=0;klines[i].line_content[k]; + for (UINTN k=egal_index+1;klines[i].length;k++) value[k-egal_index-1]=lined_file->lines[i].line_content[k]; + key[egal_index]=0; + value[lined_file->lines[i].length-egal_index-1]=0; + (*conf)->pairs[(*conf)->count].key=key; + (*conf)->pairs[(*conf)->count].value=value; + (*conf)->count++; + } + } else { + continue; + } + } + return status; +} +EFI_STATUS bp_conf_fill_default_boot_conf(bp_conf_BOOT_CONF **boot_conf) { + EFI_STATUS status=EFI_SUCCESS; + *boot_conf=AllocateZeroPool(sizeof(bp_conf_BOOT_CONF)); + if (*boot_conf==NULL) { + return EFI_OUT_OF_RESOURCES; + } + (*boot_conf)->serial_port_enabled=BP_DEFAULT_CONF_SERIAL_PORT_ENABLED; + (*boot_conf)->serial_port_debuging=BP_DEFAULT_CONF_SERIAL_PORT_DEBUGING; + (*boot_conf)->serial_port_erroring=BP_DEFAULT_CONF_SERIAL_PORT_ERRORING; + (*boot_conf)->font=BP_DEFAULT_CONF_FONT; + (*boot_conf)->disable_boot_animation=BP_DEFAULT_CONF_DISABLE_BOOT_ANIMATION; + (*boot_conf)->default_horizontal_resolution=BP_DEFAULT_CONF_DEFAULT_HORIZONTAL_RESOLUTION; + (*boot_conf)->default_vertical_resolution=BP_DEFAULT_CONF_DEFAULT_VERTICAL_RESOLUTION; + UINT8 temp1[16]=BP_DEFAULT_CONF_INITFS_PARTITION_TYPE_GUID; + CopyMem((*boot_conf)->initfs_partition_type_guid,temp1,16); + UINT8 temp2[16]=BP_DEFAULT_CONF_SIGNSYST_PARTITION_TYPE_GUID; + CopyMem((*boot_conf)->initfs_partition_type_guid,temp2,16); + UINT8 temp3[16]=BP_DEFAULT_CONF_PARTITION_GUID; + CopyMem((*boot_conf)->initfs_partition_guid,temp3,16); + CopyMem((*boot_conf)->signsyst_partition_guid,temp3,16); + (*boot_conf)->kernel_log_level=BP_DEFAULT_CONF_KERNEL_LOG_LEVEL; + (*boot_conf)->kernel_test_benchmark=BP_DEFAULT_CONF_KERNEL_TEST_BENCHMARK; + (*boot_conf)->kernel_bench_iterations=BP_DEFAULT_CONF_KERNEL_BENCH_ITERATIONS; + (*boot_conf)->kernel_log_disable_serial_port=BP_DEFAULT_CONF_KERNEL_LOG_DISABLE_SERIAL_PORT; + (*boot_conf)->kernel_disable_serial_port=BP_DEFAULT_CONF_KERNEL_DISABLE_SERIAL_PORT; + (*boot_conf)->kernel_log_ring_size=BP_DEFAULT_CONF_KERNEL_LOG_RING_SIZE; + return status; +} +EFI_STATUS bp_conf_fill_boot_conf(bp_conf_CONF *conf,bp_conf_BOOT_CONF **boot_conf) { + EFI_STATUS status=EFI_SUCCESS; + for (UINTN i=0;icount;++i) { + BOOLEAN found_key=FALSE; + for (UINTN y=0;ypairs[i].key,bp_conf_boot_conf_params[y])==0) { + found_key=TRUE; + break; + } + } + if (!found_key) { + Print(L"[BlastConf] Warning: found '%s' which isn't an expected key. Skipping.\n",conf->pairs[i].key); + continue; + } + if (StrCmp(conf->pairs[i].key,L"serial_port_enabled")==0) { + if (StrCmp(conf->pairs[i].value,L"false")==0) { + (*boot_conf)->serial_port_enabled=FALSE; + } else if (StrCmp(conf->pairs[i].value,L"true")==0) { + (*boot_conf)->serial_port_enabled=TRUE; + } else { + Print(L"[BlastConf] Error: value given for serial_port_enabled isn't a boolean. Using default value."); + continue; + } + } else if (StrCmp(conf->pairs[i].key,L"serial_port_debuging")==0) { + if (StrCmp(conf->pairs[i].value,L"false")==0) { + (*boot_conf)->serial_port_debuging=FALSE; + } else if (StrCmp(conf->pairs[i].value,L"true")==0) { + (*boot_conf)->serial_port_debuging=TRUE; + } else { + Print(L"[BlastConf] Error: value given for serial_port_debuging isn't a boolean. Using default value."); + continue; + } + } else if (StrCmp(conf->pairs[i].key,L"serial_port_erroring")==0) { + if (StrCmp(conf->pairs[i].value,L"false")==0) { + (*boot_conf)->serial_port_erroring=FALSE; + } else if (StrCmp(conf->pairs[i].value,L"true")==0) { + (*boot_conf)->serial_port_erroring=TRUE; + } else { + Print(L"[BlastConf] Error: value given for serial_port_erroring isn't a boolean. Using default value."); + continue; + } + } else if (StrCmp(conf->pairs[i].key,L"font")==0) { + (*boot_conf)->font=conf->pairs[i].value; + } else if (StrCmp(conf->pairs[i].key,L"disable_boot_animation")==0) { + if (StrCmp(conf->pairs[i].value,L"false")==0) { + (*boot_conf)->disable_boot_animation=FALSE; + } else if (StrCmp(conf->pairs[i].value,L"true")==0) { + (*boot_conf)->disable_boot_animation=TRUE; + } else { + Print(L"[BlastConf] Error: value given for disable_boot_animation isn't a boolean. Using default value."); + continue; + } + } else if (StrCmp(conf->pairs[i].key,L"default_horizontal_resolution")==0) { + if (StrCmp(conf->pairs[i].value,L"0")==0) { + (*boot_conf)->default_horizontal_resolution=0; + } else { + UINT64 hor=0; + if (conf->pairs[i].value==NULL) { + continue; + } + for (UINTN j=0;conf->pairs[i].value[j]!=0;j++) { + CHAR16 c=conf->pairs[i].value[j]; + if (c>=L'0' && c<=L'9') { + hor=hor*10+(c-L'0'); + } else { + Print(L"[BlastConf] Error for key 'default_horizontal_resolution': %s isn't a valid integer value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + } + (*boot_conf)->default_horizontal_resolution=hor; + } + } else if (StrCmp(conf->pairs[i].key,L"default_vertical_resolution")==0) { + if (StrCmp(conf->pairs[i].value,L"0")==0) { + (*boot_conf)->default_vertical_resolution=0; + } else { + UINT64 vert=0; + if (conf->pairs[i].value==NULL) { + continue; + } + for (UINTN j=0;conf->pairs[i].value[j]!=0;j++) { + CHAR16 c=conf->pairs[i].value[j]; + if (c>=L'0' && c<=L'9') { + vert=vert*10+(c-L'0'); + } else { + Print(L"[BlastConf] Error for key 'default_vertical_resolution': %s isn't a valid integer value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + } + (*boot_conf)->default_vertical_resolution=vert; + } + } else if (StrCmp(conf->pairs[i].key,L"initfs_partition_type_guid")==0) { + UINT8 guid[16]; + if (StrnLenS(conf->pairs[i].value,37)!=36) { + Print(L"[BlastConf] Error for key 'initfs_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + UINTN a,b=0; + for (a=0;a<36;a++) { + if (conf->pairs[i].value[a]==L'-') continue; + UINT8 high=bp_conf_hex_char_to_uint8(conf->pairs[i].value[a]); + UINT8 low=bp_conf_hex_char_to_uint8(conf->pairs[i].value[++a]); + if (high==0xFF || low==0xFF) { + Print(L"[BlastConf] Error for key 'initfs_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + guid[b++]=(high<<4)|low; + } + if (b!=16) { + Print(L"[BlastConf] Error for key 'initfs_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + CopyMem((*boot_conf)->initfs_partition_type_guid,guid,16); + } else if (StrCmp(conf->pairs[i].key,L"signsyst_partition_type_guid")==0) { + UINT8 guid[16]; + if (StrnLenS(conf->pairs[i].value,37)!=36) { + Print(L"[BlastConf] Error for key 'signsyst_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + UINTN a,b=0; + for (a=0;a<36;a++) { + if (conf->pairs[i].value[a]==L'-') continue; + UINT8 high=bp_conf_hex_char_to_uint8(conf->pairs[i].value[a]); + UINT8 low=bp_conf_hex_char_to_uint8(conf->pairs[i].value[++a]); + if (high==0xFF || low==0xFF) { + Print(L"[BlastConf] Error for key 'signsyst_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + guid[b++]=(high<<4)|low; + } + if (b!=16) { + Print(L"[BlastConf] Error for key 'signsyst_partition_type_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + CopyMem((*boot_conf)->signsyst_partition_type_guid,guid,16); + } else if (StrCmp(conf->pairs[i].key,L"initfs_partition_guid")==0) { + UINT8 guid[16]; + if (StrnLenS(conf->pairs[i].value,37)!=36) { + Print(L"[BlastConf] Error for key 'initfs_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + UINTN a,b=0; + for (a=0;a<36;a++) { + if (conf->pairs[i].value[a]==L'-') continue; + UINT8 high=bp_conf_hex_char_to_uint8(conf->pairs[i].value[a]); + UINT8 low=bp_conf_hex_char_to_uint8(conf->pairs[i].value[++a]); + if (high==0xFF || low==0xFF) { + Print(L"[BlastConf] Error for key 'initfs_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + guid[b++]=(high<<4)|low; + } + if (b!=16) { + Print(L"[BlastConf] Error for key 'initfs_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + CopyMem((*boot_conf)->initfs_partition_guid,guid,16); + } else if (StrCmp(conf->pairs[i].key,L"signsyst_partition_guid")==0) { + UINT8 guid[16]; + if (StrnLenS(conf->pairs[i].value,37)!=36) { + Print(L"[BlastConf] Error for key 'signsyst_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + UINTN a,b=0; + for (a=0;a<36;a++) { + if (conf->pairs[i].value[a]==L'-') continue; + UINT8 high=bp_conf_hex_char_to_uint8(conf->pairs[i].value[a]); + UINT8 low=bp_conf_hex_char_to_uint8(conf->pairs[i].value[++a]); + if (high==0xFF || low==0xFF) { + Print(L"[BlastConf] Error for key 'signsyst_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + guid[b++]=(high<<4)|low; + } + if (b!=16) { + Print(L"[BlastConf] Error for key 'signsyst_partition_guid': %s isn't a valid GUID value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + CopyMem((*boot_conf)->signsyst_partition_guid,guid,16); + } else if (StrCmp(conf->pairs[i].key,L"kernel_log_level")==0) { + if (StrCmp(conf->pairs[i].value,L"0")==0) { + (*boot_conf)->kernel_log_level=0; + } else if (StrCmp(conf->pairs[i].value,L"1")==0) { + (*boot_conf)->kernel_log_level=1; + } else if (StrCmp(conf->pairs[i].value,L"2")==0) { + (*boot_conf)->kernel_log_level=2; + } else if (StrCmp(conf->pairs[i].value,L"3")==0) { + (*boot_conf)->kernel_log_level=4; + } else if (StrCmp(conf->pairs[i].value,L"4")==0) { + (*boot_conf)->kernel_log_level=4; + } else if (StrCmp(conf->pairs[i].value,L"5")==0) { + (*boot_conf)->kernel_log_level=5; + } else if (StrCmp(conf->pairs[i].value,L"6")==0) { + (*boot_conf)->kernel_log_level=6; + } else { + Print(L"[BlastConf] Error: value given for kernel_log_level isn't a log level. Using default value."); + continue; + } + } else if (StrCmp(conf->pairs[i].key,L"kernel_test_benchmark")==0) { + if (StrCmp(conf->pairs[i].value,L"false")==0) { + (*boot_conf)->kernel_test_benchmark=FALSE; + } else if (StrCmp(conf->pairs[i].value,L"true")==0) { + (*boot_conf)->kernel_test_benchmark=TRUE; + } else { + Print(L"[BlastConf] Error: value given for kernel_test_benchmark isn't a boolean. Using default value."); + continue; + } + } else if (StrCmp(conf->pairs[i].key,L"kernel_bench_iterations")==0) { + if (StrCmp(conf->pairs[i].value,L"0")==0) { + (*boot_conf)->kernel_bench_iterations=0; + } else { + UINT64 vert=0; + if (conf->pairs[i].value==NULL) { + continue; + } + for (UINTN j=0;conf->pairs[i].value[j]!=0;j++) { + CHAR16 c=conf->pairs[i].value[j]; + if (c>=L'0' && c<=L'9') { + vert=vert*10+(c-L'0'); + } else { + Print(L"[BlastConf] Error for key 'kernel_bench_iterations': %s isn't a valid integer value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + } + if (vert>10000) { + Print(L"[BlastConf] Error for key 'kernel_bench_iterations': %s shouldn't be above 10000.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + (*boot_conf)->kernel_bench_iterations=vert; + } + } else if (StrCmp(conf->pairs[i].key,L"kernel_log_disable_serial_port")==0) { + if (StrCmp(conf->pairs[i].value,L"false")==0) { + (*boot_conf)->kernel_log_disable_serial_port=FALSE; + } else if (StrCmp(conf->pairs[i].value,L"true")==0) { + (*boot_conf)->kernel_log_disable_serial_port=TRUE; + } else { + Print(L"[BlastConf] Error: value given for kernel_log_disable_serial_port isn't a boolean. Using default value."); + continue; + } + } else if (StrCmp(conf->pairs[i].key,L"kernel_disable_serial_port")==0) { + if (StrCmp(conf->pairs[i].value,L"false")==0) { + (*boot_conf)->kernel_disable_serial_port=FALSE; + } else if (StrCmp(conf->pairs[i].value,L"true")==0) { + (*boot_conf)->kernel_disable_serial_port=TRUE; + } else { + Print(L"[BlastConf] Error: value given for kernel_disable_serial_port isn't a boolean. Using default value."); + continue; + } + } else if (StrCmp(conf->pairs[i].key,L"kernel_log_ring_size")==0) { + if (StrCmp(conf->pairs[i].value,L"0")==0) { + (*boot_conf)->kernel_bench_iterations=0; + } else { + UINT64 vert=0; + if (conf->pairs[i].value==NULL) { + continue; + } + for (UINTN j=0;conf->pairs[i].value[j]!=0;j++) { + CHAR16 c=conf->pairs[i].value[j]; + if (c>=L'0' && c<=L'9') { + vert=vert*10+(c-L'0'); + } else { + Print(L"[BlastConf] Error for key 'kernel_log_ring_size': %s isn't a valid integer value.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + } + if (vert>0xFFFF) { + Print(L"[BlastConf] Error for key 'kernel_log_ring_size': %s shouldn't be above 65535.\n",conf->pairs[i].value); + return EFI_INVALID_PARAMETER; + } + (*boot_conf)->kernel_log_ring_size=vert; + } + } + } + return status; +} diff --git a/Blastproof/src/libs/src/console.c b/Blastproof/src/libs/src/console.c new file mode 100644 index 0000000..490c9a0 --- /dev/null +++ b/Blastproof/src/libs/src/console.c @@ -0,0 +1,77 @@ +#include "../include/console.h" +#include +#include +#include +#include +#include +#include +VOID bp_console_print_file(bp_disk_FILE *file) { + for (UINTN i=0;ifile_size_bytes;i++) { + CHAR8 c=file->content[i]; + if (c=='\n') { + Print(L"\r\n"); + } else { + Print(L"%c",(CHAR16)c); + } + } +} +EFI_STATUS bp_console_read_password(CHAR16 *prompt,bp_console_PASSWORD **pwd,CHAR16 hiding_char) { + EFI_STATUS status; + if (prompt==NULL || pwd==NULL) { + return EFI_INVALID_PARAMETER; + } + Print(L"%s",prompt); + EFI_INPUT_KEY key; + UINTN buf_size=16; + UINTN length=0; + CHAR16 *buffer=AllocatePool(buf_size*sizeof(CHAR16)); + if (buffer==NULL) { + return EFI_OUT_OF_RESOURCES; + } + while (TRUE) { + status=gST->ConIn->ReadKeyStroke(gST->ConIn,&key); + if (status==EFI_NOT_READY) { + continue; + } else if (EFI_ERROR(status)) { + FreePool(buffer); + return status; + } + if (key.UnicodeChar==CHAR_CARRIAGE_RETURN) { + break; + } else if (key.UnicodeChar==CHAR_BACKSPACE) { + if (length>0) { + length--; + Print(L"\b \b"); + } + } else if (key.UnicodeChar>=L' ' && key.UnicodeChar<=0x7E) { + if (length+1>=buf_size) { + CHAR16 *new_buf=AllocatePool(buf_size*2*sizeof(CHAR16)); + if (new_buf==NULL) { + FreePool(buffer); + return EFI_OUT_OF_RESOURCES; + } + CopyMem(new_buf,buffer,length*sizeof(CHAR16)); + FreePool(buffer); + buffer=new_buf; + buf_size*=2; + } + buffer[length]=key.UnicodeChar; + length++; + if (hiding_char!=0) { + Print(L"%c",hiding_char); + } else { + Print(L"%c",key.UnicodeChar); + } + } + } + buffer[length]=L'\0'; + *pwd=AllocatePool(sizeof(bp_console_PASSWORD)); + if (*pwd==NULL) { + FreePool(buffer); + return EFI_OUT_OF_RESOURCES; + } + (*pwd)->password=buffer; + (*pwd)->size=length; + Print(L"\n"); + return EFI_SUCCESS; +} diff --git a/Blastproof/src/libs/src/cpu.c b/Blastproof/src/libs/src/cpu.c new file mode 100644 index 0000000..2bcca4f --- /dev/null +++ b/Blastproof/src/libs/src/cpu.c @@ -0,0 +1,29 @@ +#include "../include/cpu.h" +#include +#include +EFI_STATUS bp_cpu_fill_cpu_info(bp_cpu_CPU_INFO* cpu_info) { + UINT32 Eax,Ebx,Ecx,Edx; + AsmCpuid(0,&Eax,&Ebx,&Ecx,&Edx); + *(UINT32 *)&cpu_info->vendor[0]=Ebx; + *(UINT32 *)&cpu_info->vendor[4]=Edx; + *(UINT32 *)&cpu_info->vendor[8]=Ecx; + cpu_info->vendor[12]='\0'; + cpu_info->max_cpuid_level=Eax; + AsmCpuid(1,&Eax,&Ebx,&Ecx,&Edx); + cpu_info->stepping=Eax&0xF; + cpu_info->model=(Eax>>4)&0xF; + cpu_info->family=(Eax>>8)&0xF; + cpu_info->ext_model=(Eax>>16)&0xF; + cpu_info->ext_family=(Eax>>20)&0xFF; + cpu_info->display_family=cpu_info->family+(cpu_info->family==0xF?cpu_info->ext_family:0); + cpu_info->display_model=cpu_info->family+((cpu_info->family==0x6||cpu_info->family==0xF)?(cpu_info->ext_model<<4):0); + for (INTN i=0;i<32;i++) { + cpu_info->ecx_bin[i]=(Ecx&(1<<(31-i)))?L'1':L'0'; + } + cpu_info->ecx_bin[32]=L'\0'; + for (INTN i=0;i<32;i++) { + cpu_info->edx_bin[i]=(Edx&(1<<(31-i)))?L'1':L'0'; + } + cpu_info->edx_bin[32]=L'\0'; + return 0; +} diff --git a/Blastproof/src/libs/src/crypto.c b/Blastproof/src/libs/src/crypto.c new file mode 100644 index 0000000..a795816 --- /dev/null +++ b/Blastproof/src/libs/src/crypto.c @@ -0,0 +1,273 @@ +#include "../include/crypto.h" +#include "../sha3/sha3.h" +#include "../argon2/argon2.h" +#include +#include +#include +#include +EFI_STATUS bp_crypto_secure_erase(UINT8 *ptr,UINTN size) { + if (ptr==NULL || size==0) { + return EFI_INVALID_PARAMETER; + } + ZeroMem(ptr,size); + return EFI_SUCCESS; +} +EFI_STATUS bp_crypto_sha3(UINT8 *input,UINTN size_input,UINTN size_output,bp_crypto_HASH_DIGEST **output) { + EFI_STATUS status=EFI_SUCCESS; + if (size_input<1 || input==NULL) { + return EFI_INVALID_PARAMETER; + } + if (size_output!=32 && size_output!=48 && size_output!=64) { + return EFI_INVALID_PARAMETER; + } + *output=AllocateZeroPool(sizeof(bp_crypto_HASH_DIGEST)); + if (!*output) { + return EFI_OUT_OF_RESOURCES; + } + (*output)->digest=AllocateZeroPool(64); + if (!(*output)->digest) { + FreePool(*output); + *output=NULL; + return EFI_OUT_OF_RESOURCES; + } + (*output)->size_bytes=size_output; + sha3(input,size_input,(*output)->digest,size_output); + return status; +} +EFI_STATUS bp_crypto_argon2id_raw(bp_crypto_ARGON2_CONTEXT *context,bp_crypto_HASH_DIGEST **output) { + if (context==NULL) { + return EFI_INVALID_PARAMETER; + } + if (context->password.password==NULL) { + return EFI_INVALID_PARAMETER; + } + if (context->password.size==0) { + return EFI_INVALID_PARAMETER; + } + if (context->memory_cost==0 || context->time_cost==0) { + return EFI_INVALID_PARAMETER; + } + if (context->hash_size==0) { + return EFI_INVALID_PARAMETER; + } + if (output==NULL) { + return EFI_INVALID_PARAMETER; + } + *output=NULL; + *output=AllocateZeroPool(sizeof(bp_crypto_HASH_DIGEST)); + if (*output==NULL) { + return EFI_OUT_OF_RESOURCES; + } + (*output)->digest=AllocateZeroPool(context->hash_size); + if ((*output)->digest==NULL) { + FreePool(*output); + *output=NULL; + return EFI_OUT_OF_RESOURCES; + } + INTN res=argon2id_hash_raw(context->time_cost,context->memory_cost,1,context->password.password,context->password.size*2,context->salt,context->salt_size,(*output)->digest,context->hash_size); + if (res!=0) { + FreePool((*output)->digest); + FreePool(*output); + *output=NULL; + return EFI_INVALID_PARAMETER; + } + (*output)->size_bytes=context->hash_size; + return EFI_SUCCESS; +} +EFI_STATUS bp_crypto_argon2d_raw(bp_crypto_ARGON2_CONTEXT *context,bp_crypto_HASH_DIGEST **output) { + if (context==NULL) { + return EFI_INVALID_PARAMETER; + } + if (context->password.password==NULL) { + return EFI_INVALID_PARAMETER; + } + if (context->password.size==0) { + return EFI_INVALID_PARAMETER; + } + if (context->memory_cost==0 || context->time_cost==0) { + return EFI_INVALID_PARAMETER; + } + if (context->hash_size==0) { + return EFI_INVALID_PARAMETER; + } + if (output==NULL) { + return EFI_INVALID_PARAMETER; + } + *output=NULL; + *output=AllocateZeroPool(sizeof(bp_crypto_HASH_DIGEST)); + if (*output==NULL) { + return EFI_OUT_OF_RESOURCES; + } + (*output)->digest=AllocateZeroPool(context->hash_size); + if ((*output)->digest==NULL) { + FreePool(*output); + *output=NULL; + return EFI_OUT_OF_RESOURCES; + } + INTN res=argon2d_hash_raw(context->time_cost,context->memory_cost,1,context->password.password,context->password.size*2,context->salt,context->salt_size,(*output)->digest,context->hash_size); + if (res!=0) { + FreePool((*output)->digest); + FreePool(*output); + *output=NULL; + return EFI_INVALID_PARAMETER; + } + (*output)->size_bytes=context->hash_size; + return EFI_SUCCESS; +} +EFI_STATUS bp_crypto_argon2i_raw(bp_crypto_ARGON2_CONTEXT *context,bp_crypto_HASH_DIGEST **output) { + if (context==NULL) { + return EFI_INVALID_PARAMETER; + } + if (context->password.password==NULL) { + return EFI_INVALID_PARAMETER; + } + if (context->password.size==0) { + return EFI_INVALID_PARAMETER; + } + if (context->memory_cost==0 || context->time_cost==0) { + return EFI_INVALID_PARAMETER; + } + if (context->hash_size==0) { + return EFI_INVALID_PARAMETER; + } + if (output==NULL) { + return EFI_INVALID_PARAMETER; + } + *output=NULL; + *output=AllocateZeroPool(sizeof(bp_crypto_HASH_DIGEST)); + if (*output==NULL) { + return EFI_OUT_OF_RESOURCES; + } + (*output)->digest=AllocateZeroPool(context->hash_size); + if ((*output)->digest==NULL) { + FreePool(*output); + *output=NULL; + return EFI_OUT_OF_RESOURCES; + } + INTN res=argon2i_hash_raw(context->time_cost,context->memory_cost,1,context->password.password,context->password.size*2,context->salt,context->salt_size,(*output)->digest,context->hash_size); + if (res!=0) { + FreePool((*output)->digest); + FreePool(*output); + *output=NULL; + return EFI_INVALID_PARAMETER; + } + (*output)->size_bytes=context->hash_size; + return EFI_SUCCESS; +} +EFI_STATUS bp_crypto_sphincsplus_keypair_seed(UINT8 *seed,UINTN seed_size,BOOLEAN erase_sk,BOOLEAN erase_pk,bp_crypto_SPHINCSPLUS_KEYPAIR **output) { + if (seed==NULL || seed_size!=CRYPTO_SEEDBYTES) { + return EFI_INVALID_PARAMETER; + } + if (output==NULL) { + return EFI_INVALID_PARAMETER; + } + *output=NULL; + *output=AllocatePool(sizeof(bp_crypto_SPHINCSPLUS_KEYPAIR)); + if (*output==NULL) { + return EFI_OUT_OF_RESOURCES; + } + (*output)->erase_pk=erase_pk; + (*output)->erase_sk=erase_sk; + INTN res=crypto_sign_seed_keypair((*output)->pk,(*output)->sk,seed); + if (res!=0) { + FreePool(*output); + *output=NULL; + return EFI_INVALID_PARAMETER; + } + if (erase_pk) { + bp_crypto_secure_erase((*output)->pk,CRYPTO_PUBLICKEYBYTES); + } + if (erase_sk) { + bp_crypto_secure_erase((*output)->sk,CRYPTO_SECRETKEYBYTES); + } + return EFI_SUCCESS; +} +EFI_STATUS bp_crypto_sphincsplus_verify(UINT8 *signature,UINTN signature_size,UINT8 *message,UINTN message_size,UINT8 *pk) { + if (signature==NULL || signature_size!=CRYPTO_BYTES) { + return EFI_INVALID_PARAMETER; + } + if (message==NULL || message_size==0) { + return EFI_INVALID_PARAMETER; + } + if (pk==NULL) { + return EFI_INVALID_PARAMETER; + } + INTN res=crypto_sign_verify(signature,signature_size,message,message_size,pk); + if (res!=0) { + return EFI_INVALID_PARAMETER; + } + return EFI_SUCCESS; +} +EFI_STATUS bp_crypto_sphincsplus_verify_file(EFI_FILE_PROTOCOL *root,CHAR16 *file,CHAR16 *sigfile,UINT8 *pk) { + EFI_STATUS status; + if (root==NULL || file==NULL) { + return EFI_INVALID_PARAMETER; + } + EFI_FILE_PROTOCOL *opened_file=NULL; + EFI_FILE_PROTOCOL *opened_sig_file=NULL; + bp_disk_FILE *file_content=NULL; + bp_disk_FILE *sig_file_content=NULL; + if (EFI_ERROR(bp_disk_check_file_existence(root,file))) { + return EFI_NOT_FOUND; + } + if (EFI_ERROR(bp_disk_check_file_existence(root,sigfile))) { + return EFI_NOT_FOUND; + } + status=bp_disk_open_file(root,file,&opened_file); + if (EFI_ERROR(status)) goto cleanup; + EFI_FILE_INFO *file_info; + status=bp_disk_get_file_info(opened_file,&file_info); + if (EFI_ERROR(status)) goto cleanup; + status=bp_disk_open_file(root,sigfile,&opened_sig_file); + if (EFI_ERROR(status)) goto cleanup; + status=bp_disk_read_file(opened_file,&file_content); + if (EFI_ERROR(status)) goto cleanup; + status=bp_disk_read_file(opened_sig_file,&sig_file_content); + if (EFI_ERROR(status)) goto cleanup; + if (sig_file_content->file_size_bytes!=CRYPTO_BYTES) { + status=EFI_SECURITY_VIOLATION; + goto cleanup; + } + bp_crypto_HASH_DIGEST *hash=NULL; + if (file_info->FileSize>1024*1024) { + status=bp_crypto_sha3(file_content->content,file_content->file_size_bytes,64,&hash); + if (EFI_ERROR(status)) goto cleanup; + INTN res=crypto_sign_verify(sig_file_content->content,sig_file_content->file_size_bytes,hash->digest,hash->size_bytes,pk); + if (res!=0) { + status=EFI_SECURITY_VIOLATION; + goto cleanup; + } + status=EFI_SUCCESS; + } else { + INTN res=crypto_sign_verify(sig_file_content->content,sig_file_content->file_size_bytes,file_content->content,file_content->file_size_bytes,pk); + if (res!=0) { + status=EFI_SECURITY_VIOLATION; + goto cleanup; + } + status=EFI_SUCCESS; + } +cleanup: + if (opened_file!=NULL) { + opened_file->Close(opened_file); + } + if (opened_sig_file!=NULL) { + opened_sig_file->Close(opened_sig_file); + } + if (file_content->content) { + bp_crypto_secure_erase(file_content->content,file_content->file_size_bytes); + FreePool(file_content->content); + } + if (sig_file_content->content) { + bp_crypto_secure_erase(sig_file_content->content,sig_file_content->file_size_bytes); + FreePool(sig_file_content->content); + } + if (file_info) { + FreePool(file_info); + } + if (hash) { + bp_crypto_secure_erase(hash->digest,hash->size_bytes); + FreePool(hash->digest); + FreePool(hash); + } + return status; +} diff --git a/Blastproof/src/libs/src/debug.c b/Blastproof/src/libs/src/debug.c new file mode 100644 index 0000000..9523175 --- /dev/null +++ b/Blastproof/src/libs/src/debug.c @@ -0,0 +1,30 @@ +#include "../include/debug.h" +#include +#include +#include +#include +#include +#include +#include +EFI_STATUS bp_debug_write(EFI_SERIAL_IO_PROTOCOL *serial_protocol,CHAR8* output,BOOLEAN allow_debugging) { + if (serial_protocol==NULL || output==NULL) { + return EFI_INVALID_PARAMETER; + } + UINTN strlen=AsciiStrLen(output); + if (allow_debugging) serial_protocol->Write(serial_protocol,&strlen,output); + return EFI_SUCCESS; +} +EFI_STATUS bp_debug_write_line(EFI_SERIAL_IO_PROTOCOL *serial_protocol,CHAR8* output,BOOLEAN allow_debugging) { + if (serial_protocol==NULL || output==NULL) { + return EFI_INVALID_PARAMETER; + } + UINTN strlen=AsciiStrLen(output); + CHAR8 *output_line=AllocatePool(strlen+3); + if (!output_line) { + return EFI_OUT_OF_RESOURCES; + } + UINTN outputlen=AsciiSPrint(output_line,strlen+3,"%a\r\n",output); + if (allow_debugging) serial_protocol->Write(serial_protocol,&outputlen,output_line); + FreePool(output_line); + return EFI_SUCCESS; +} diff --git a/Blastproof/src/libs/src/disk.c b/Blastproof/src/libs/src/disk.c new file mode 100644 index 0000000..2543372 --- /dev/null +++ b/Blastproof/src/libs/src/disk.c @@ -0,0 +1,69 @@ +#include "../include/disk.h" +#include +#include +#include +#include +#include +#include +#include +EFI_STATUS bp_disk_check_dir_existence(EFI_FILE_PROTOCOL *root,CHAR16 *dir) { + EFI_STATUS status; + EFI_FILE_PROTOCOL *object; + status=root->Open(root,&object,dir,EFI_FILE_MODE_READ,EFI_FILE_DIRECTORY); + if (!EFI_ERROR(status)) { + object->Close(object); + } + return status; +} +EFI_STATUS bp_disk_check_file_existence(EFI_FILE_PROTOCOL *root,CHAR16 *file) { + EFI_STATUS status; + EFI_FILE_PROTOCOL *object; + status=root->Open(root,&object,file,EFI_FILE_MODE_READ,0); + if (!EFI_ERROR(status)) { + object->Close(object); + } + return status; +} +EFI_STATUS bp_disk_open_file(EFI_FILE_PROTOCOL *root,CHAR16 *file,EFI_FILE_PROTOCOL **opened_file) { + EFI_STATUS status; + status=root->Open(root,opened_file,file,EFI_FILE_MODE_READ,0); + return status; +} +EFI_STATUS bp_disk_get_file_info(EFI_FILE_PROTOCOL *opened_file,EFI_FILE_INFO **file_info) { + EFI_STATUS status; + UINTN file_info_size=0; + status=opened_file->GetInfo(opened_file,&gEfiFileInfoGuid,&file_info_size,NULL); + if (status!=EFI_BUFFER_TOO_SMALL) { + return status; + } + *file_info=AllocateZeroPool(file_info_size); + if (*file_info==NULL) { + return EFI_OUT_OF_RESOURCES; + } + status=opened_file->GetInfo(opened_file,&gEfiFileInfoGuid,&file_info_size,*file_info); + return status; +} +EFI_STATUS bp_disk_read_file(EFI_FILE_PROTOCOL *opened_file,bp_disk_FILE **file) { + EFI_STATUS status; + if (file==NULL) { + return EFI_INVALID_PARAMETER; + } + *file=AllocateZeroPool(sizeof(bp_disk_FILE)); + if (!*file) { + return EFI_OUT_OF_RESOURCES; + } + EFI_FILE_INFO *file_info; + status=bp_disk_get_file_info(opened_file,&file_info); + if (EFI_ERROR(status)) { + return status; + } + (*file)->file_size_bytes=file_info->FileSize; + FreePool(file_info); + (*file)->content=AllocateZeroPool((*file)->file_size_bytes); + if ((*file)->content==NULL) { + FreePool(*file); + return EFI_OUT_OF_RESOURCES; + } + status=opened_file->Read(opened_file,&((*file)->file_size_bytes),(*file)->content); + return status; +} diff --git a/Blastproof/src/libs/src/font.c b/Blastproof/src/libs/src/font.c new file mode 100644 index 0000000..64cc0e5 --- /dev/null +++ b/Blastproof/src/libs/src/font.c @@ -0,0 +1,234 @@ +#include "../include/font.h" +#include +#include +#include +#include +#include +const UINT8 fbm_sign[3]={'F','B','M'}; +EFI_STATUS bp_font_get_header(bp_disk_FILE *fbm_file,bp_font_FBM_HEADER **header) { + if (fbm_file==NULL) { + return EFI_INVALID_PARAMETER; + } + *header=AllocatePool(sizeof(bp_font_FBM_HEADER)); + if (!*header) { + return EFI_OUT_OF_RESOURCES; + } + if (fbm_file->file_size_bytes<10) { + return EFI_COMPROMISED_DATA; + } + if (CompareMem(fbm_file->content,fbm_sign,3)!=0) { + return EFI_UNSUPPORTED; + } + CopyMem(&(*header)->encoding,fbm_file->content+3,1); + CopyMem(&(*header)->entries_count,fbm_file->content+4,4); + CopyMem(&(*header)->width,fbm_file->content+8,1); + CopyMem(&(*header)->height,fbm_file->content+9,1); + return EFI_SUCCESS; +} +EFI_STATUS bp_font_load_font(bp_disk_FILE *fbm_file,bp_font_FBM_FONT **loaded_font) { + EFI_STATUS status; + if (fbm_file==NULL) { + return EFI_INVALID_PARAMETER; + } + bp_font_FBM_HEADER *header; + status=bp_font_get_header(fbm_file,&header); + if (EFI_ERROR(status)) { + return status; + } + UINTN size_glyph_bytes=header->height*header->width; + if (fbm_file->file_size_bytes!=10+(size_glyph_bytes+4)*header->entries_count) { + FreePool(header); + return EFI_COMPROMISED_DATA; + } + if (size_glyph_bytes==0 || header->entries_count==0) { + FreePool(header); + return EFI_UNSUPPORTED; + } + bp_font_FBM_FONT *font=AllocatePool(sizeof(bp_font_FBM_FONT)); + if (!font) { + FreePool(header); + return EFI_OUT_OF_RESOURCES; + } + font->entries_count=header->entries_count; + font->chars=AllocatePool(sizeof(bp_font_FBM_CHAR)*header->entries_count); + if (!font->chars) { + FreePool(font); + FreePool(header); + return EFI_OUT_OF_RESOURCES; + } + UINT8 *cursor=fbm_file->content+10; + for (UINT32 i=0;ientries_count;++i) { + CopyMem(&(font->chars[i].codepoint),cursor,4); + cursor+=4; + font->chars[i].data=AllocatePool(size_glyph_bytes); + if (!font->chars[i].data) { + for (UINT32 j=0;jchars[j].data) { + FreePool(font->chars[j].data); + } + } + FreePool(font->chars); + FreePool(font); + FreePool(header); + return EFI_OUT_OF_RESOURCES; + } + CopyMem(font->chars[i].data,cursor,size_glyph_bytes); + cursor+=size_glyph_bytes; + } + FreePool(header); + *loaded_font=font; + return EFI_SUCCESS; +} +EFI_STATUS bp_font_check_font(bp_font_FBM_FONT *font,bp_font_FBM_HEADER *header) { + if (font==NULL || header==NULL) { + return EFI_INVALID_PARAMETER; + } + UINTN size_glyph_bytes=header->height*header->width; + if (size_glyph_bytes==0 || header->entries_count==0 || font->entries_count==0 || header->entries_count!=font->entries_count) { + FreePool(header); + return EFI_UNSUPPORTED; + } + for (UINT32 i=0;ientries_count;++i) { + BOOLEAN check1=(BOOLEAN)(((font->chars[i].codepoint>>24)>>1) & 1); + BOOLEAN check2=(BOOLEAN)(((font->chars[i].codepoint>>16)>>3) & 1); + BOOLEAN check3=(BOOLEAN)(((font->chars[i].codepoint>>8)>>5) & 1); + for (UINTN y=0;ychars[i].data[y]>>6)&0x01); + BOOLEAN c2=(BOOLEAN)((font->chars[i].data[y]>>4)&0x01); + BOOLEAN c3=(BOOLEAN)((font->chars[i].data[y]>>2)&0x01); + if (c1!=check1 || c2!=check2 || c3!=check3) { + return EFI_VOLUME_CORRUPTED; + } + } + if (header->encoding==1) { + UINT8 codepoint=(UINT8)((font->chars[i].codepoint>>24) & 0xFF); + if (codepoint>0x7F && codepoint<0xFF) { + return EFI_UNSUPPORTED; + } + } + } + return EFI_SUCCESS; +} +EFI_STATUS bp_font_parse_font(bp_font_FBM_FONT *font,bp_font_FBM_HEADER *header,bp_font_FBM_FONT_BUFFER **parsed_font) { + if (font==NULL || header==NULL) { + return EFI_INVALID_PARAMETER; + } + UINTN size_glyph_bytes=header->height*header->width; + if (size_glyph_bytes==0 || header->entries_count==0 || font->entries_count==0 || header->entries_count!=font->entries_count) { + return EFI_UNSUPPORTED; + } + bp_font_FBM_FONT_BUFFER *font_buffer=AllocatePool(sizeof(bp_font_FBM_FONT_BUFFER)); + if (!font_buffer) { + return EFI_OUT_OF_RESOURCES; + } + font_buffer->entries_count=header->entries_count; + font_buffer->chars=AllocatePool(sizeof(bp_font_FBM_CHAR_BUFFER)*header->entries_count); + if (!font_buffer->chars) { + FreePool(font_buffer); + return EFI_OUT_OF_RESOURCES; + } + for (UINT32 i=0;ientries_count;++i) { + bp_font_FBM_CHAR_BUFFER *char_buf=&font_buffer->chars[i]; + char_buf->data=AllocatePool(sizeof(bp_font_FBM_PIXEL)*header->height*header->width); + if (!char_buf->data) { + for (INTN y=0;ychars[y].data); + } + FreePool(font_buffer->chars); + FreePool(font_buffer); + return EFI_OUT_OF_RESOURCES; + } + char_buf->codepoint=font->chars[i].codepoint; + for (INTN p=0;pheight*header->width;p++) { + bp_font_FBM_PIXEL *pixel=&char_buf->data[p]; + UINT8 r=font->chars[i].data[p]; + pixel->enabled=(BOOLEAN)(r & 0x01); + UINT8 shading=(((r>>7)&1)<<3)|(((r>>5)&1)<<2)|(((r>>3)&1)<<1)|(((r>>1)&1)<<0); + pixel->shading=shading; + } + } + for (UINT32 i=0;ientries_count;++i) { + FreePool(font->chars[i].data); + } + FreePool(font->chars); + FreePool(font); + *parsed_font=font_buffer; + return EFI_SUCCESS; +} +EFI_STATUS bp_font_render_font(bp_font_FBM_FONT_BUFFER *font_buffer,bp_font_FBM_HEADER *header,bp_font_FBM_RENDERER_SETTING settings,bp_font_FBM_FONT_RENDER **rendered_font) { + if (font_buffer==NULL || header==NULL) { + return EFI_INVALID_PARAMETER; + } + if (settings.red_bits==0 || settings.green_bits==0 || settings.blue_bits==0) { + return EFI_INVALID_PARAMETER; + } + bp_graphic_COLOR_RGB gradient[16]; + for (int i=0;i<16;++i) { + gradient[i].r=(UINT8)(settings.background_color.r+i*(settings.text_color.r-settings.background_color.r)/15.0f); + gradient[i].g=(UINT8)(settings.background_color.g+i*(settings.text_color.g-settings.background_color.g)/15.0f); + gradient[i].b=(UINT8)(settings.background_color.b+i*(settings.text_color.b-settings.background_color.b)/15.0f); + } + UINTN size_glyph_bytes=header->height*header->width; + if (size_glyph_bytes==0 || header->entries_count==0 || font_buffer->entries_count==0 || header->entries_count!=font_buffer->entries_count) { + return EFI_UNSUPPORTED; + } + bp_font_FBM_FONT_RENDER *renderer=AllocatePool(sizeof(bp_font_FBM_FONT_RENDER)); + if (!renderer) { + return EFI_OUT_OF_RESOURCES; + } + renderer->entries_count=font_buffer->entries_count; + renderer->chars=AllocatePool(sizeof(bp_font_FBM_CHAR_RENDER)*font_buffer->entries_count); + if (!renderer->chars) { + FreePool(renderer); + return EFI_OUT_OF_RESOURCES; + } + for (UINT32 i=0;ientries_count;++i) { + bp_font_FBM_CHAR_BUFFER *char_buffer=&font_buffer->chars[i]; + bp_font_FBM_CHAR_RENDER *char_render=&renderer->chars[i]; + char_render->data=AllocatePool(sizeof(UINT32)*header->width*header->height); + if (!char_render->data) { + for (INTN y=0;ychars[y].data); + } + FreePool(renderer->chars); + FreePool(renderer); + return EFI_OUT_OF_RESOURCES; + } + char_render->red_bits=settings.red_bits; + char_render->blue_bits=settings.blue_bits; + char_render->green_bits=settings.green_bits; + char_render->codepoint=char_buffer->codepoint; + char_render->height=header->height; + char_render->width=header->width; + for (UINTN p=0;pwidth*header->height;++p) { + bp_font_FBM_PIXEL *pixel=&char_buffer->data[p]; + if (pixel->shading>15) { + for (INTN y=0;ychars[y].data); + } + FreePool(renderer->chars); + FreePool(renderer); + return EFI_COMPROMISED_DATA; + } + bp_graphic_COLOR_RGB color; + if (!pixel->enabled) { + color.r=settings.background_color.r; + color.g=settings.background_color.g; + color.b=settings.background_color.b; + } else { + color.r=gradient[pixel->shading].r; + color.g=gradient[pixel->shading].g; + color.b=gradient[pixel->shading].b; + } + UINT32 pixel_value=bp_graphic_get_pixel_by_color(&color,settings.red_bits,settings.green_bits,settings.blue_bits); + char_render->data[p]=pixel_value; + } + } + for (UINT32 i=0;ientries_count;++i) { + FreePool(font_buffer->chars[i].data); + } + FreePool(font_buffer->chars); + FreePool(font_buffer); + *rendered_font=renderer; + return EFI_SUCCESS; +} diff --git a/Blastproof/src/libs/src/graphic.c b/Blastproof/src/libs/src/graphic.c new file mode 100644 index 0000000..e5a5bd1 --- /dev/null +++ b/Blastproof/src/libs/src/graphic.c @@ -0,0 +1,147 @@ +#include "../include/graphic.h" +#include "ProcessorBind.h" +#include "Uefi/UefiBaseType.h" +#include +#include +#include +#include +#include +#include +EFI_STATUS bp_graphic_get_mode_count(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,UINT32 *output) { + if (gop==NULL || output==NULL) { + return EFI_INVALID_PARAMETER; + } + if (gop->Mode==NULL) { + return EFI_DEVICE_ERROR; + } + *output=gop->Mode->MaxMode; + return EFI_SUCCESS; +} +EFI_STATUS bp_graphic_get_highest_resolution_index(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,UINT32 *output) { + EFI_STATUS status; + if (gop==NULL || output==NULL) { + return EFI_INVALID_PARAMETER; + } + UINT32 protsnum; + status=bp_graphic_get_mode_count(gop,&protsnum); + if (EFI_ERROR(status) || protsnum==0) { + return EFI_DEVICE_ERROR; + } + if (protsnum>1) { + UINT32 vertical_res; + UINT32 horizontal_res; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *prot_info; + UINTN info_size; + status=gop->QueryMode(gop,0,&info_size,&prot_info); + if (EFI_ERROR(status)) { + return EFI_DEVICE_ERROR; + } else { + vertical_res=prot_info->VerticalResolution; + horizontal_res=prot_info->HorizontalResolution; + *output=0; + } + for (INTN i=1;iQueryMode(gop,i,&info_size,&prot_info); + if (EFI_ERROR(status)) { + return EFI_DEVICE_ERROR; + } + UINTN max_pixels=horizontal_res*vertical_res; + UINTN cur_pixels=prot_info->HorizontalResolution*prot_info->VerticalResolution; + if (cur_pixels>max_pixels) { + vertical_res=prot_info->VerticalResolution; + horizontal_res=prot_info->HorizontalResolution; + *output=i; + } + } + } else { + *output=0; + } + return EFI_SUCCESS; +} +EFI_STATUS bp_graphic_get_index_by_resolution(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,UINT64 hor_res,UINT64 vert_res,UINT32 *output) { + if (gop==NULL || output==NULL || (hor_res==0 && vert_res==0)) { + return EFI_INVALID_PARAMETER; + } + UINT32 mode_count; + EFI_STATUS status=bp_graphic_get_mode_count(gop,&mode_count); + if (EFI_ERROR(status)) { + return EFI_DEVICE_ERROR; + } + *output=0xFFFFFFFF; + for (UINT32 i=0;iQueryMode(gop,i,&info_size,&info); + if (EFI_ERROR(status)) { + return EFI_DEVICE_ERROR; + } + if (info->HorizontalResolution==hor_res && info->VerticalResolution==vert_res) { + *output=i; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; +} +STATIC UINT32 bp_graphic_get_shift_from_mask (UINT32 mask) { + UINT32 shift=0; + while (((mask>>shift)&1)==0 && shift<32) { + shift++; + } + return shift; +} +STATIC UINT32 bp_graphic_get_width_from_mask (UINT32 mask) { + UINT32 shift=bp_graphic_get_shift_from_mask(mask); + UINT32 width=0; + mask>>=shift; + while ((mask & 1)==1) { + width++; + mask>>=1; + } + return width; +} +UINT32 bp_graphic_get_pixel_by_color (bp_graphic_COLOR_RGB *color,UINT32 red_bits,UINT32 green_bits,UINT32 blue_bits) { + UINT32 pixel=0; + UINT32 r_shift=bp_graphic_get_shift_from_mask(red_bits); + UINT32 g_shift=bp_graphic_get_shift_from_mask(green_bits); + UINT32 b_shift=bp_graphic_get_shift_from_mask(blue_bits); + UINT32 r_width=bp_graphic_get_width_from_mask(red_bits); + UINT32 g_width=bp_graphic_get_width_from_mask(green_bits); + UINT32 b_width=bp_graphic_get_width_from_mask(blue_bits); + UINT32 r=color->r>>(8-r_width); + UINT32 g=color->g>>(8-g_width); + UINT32 b=color->b>>(8-b_width); + pixel|=(r<=width_out) { + copy_width=0; + } else { + UINTN avail=width_out-x; + copy_width=(width_from<=avail)?width_from:avail; + } + UINTN copy_height; + if (y>=height_out) { + copy_height=0; + } else { + UINTN avail=height_out-y; + copy_height=(height_from<=avail)?height_from:avail; + } + BOOLEAN fully_inside=(copy_width==width_from) && (copy_height==height_from); + for (UINTN yfor=0;yfor +#include +#include +#include +#include +#include +EFI_STATUS bp_initfs_parse_initfs_header(UINT8 *header_data,UINTN buffer_size,bp_initfs_INITFS_HEADER **header) { + if (header==NULL || header_data==NULL) { + return EFI_INVALID_PARAMETER; + } + if (buffer_sizeinitfs_version!=BP_INITFS_INITFS_SUPPORTED_VERSION) { + return EFI_UNSUPPORTED; + } + return EFI_SUCCESS; +} +EFI_STATUS bp_initfs_generate_padding(UINTN size,UINT8 byte,UINT8 **result) { + if (size==0 || result==NULL) { + return EFI_INVALID_PARAMETER; + } + *result=AllocateZeroPool(size); + if (!*result) { + *result=NULL; + return EFI_OUT_OF_RESOURCES; + } + UINTN f0=0; + UINTN f1=1; + while (f0initfs_size+header->table_size+header->files_area_size+header->entries_width+header->entries_count+header->files_area_offset)%MAX_UINT64; + UINT64 check1=(value*0x9E3779B185EBCA87)^header->entropy_check1; + if (check1!=header->check1) { + return EFI_SECURITY_VIOLATION; + } + EFI_FILE_PROTOCOL *opened_file=NULL; + EFI_STATUS status; + status=bp_disk_open_file(root,L"\\EFI\\BlastProofFiles\\initfs-footprint.bin",&opened_file); + if (EFI_ERROR(status)) { + return status; + } + bp_disk_FILE *file=NULL; + status=bp_disk_read_file(opened_file,&file); + if (EFI_ERROR(status)) { + return status; + } + if (file->file_size_bytes!=64) { + return EFI_SECURITY_VIOLATION; + } + bp_crypto_HASH_DIGEST *installation_id_hash=NULL; + status=bp_crypto_sha3(header->installation_id,sizeof(header->installation_id),64,&installation_id_hash); + if (EFI_ERROR(status)) { + return status; + } + if (CompareMem(installation_id_hash->digest,file->content,64)!=0) { + return EFI_SECURITY_VIOLATION; + } + bp_crypto_HASH_DIGEST *installation_id_hash_hash=NULL; + status=bp_crypto_sha3(file->content,file->file_size_bytes,64,&installation_id_hash_hash); + if (EFI_ERROR(status)) { + return status; + } + if (CompareMem(installation_id_hash_hash->digest,header->installation_id_hash_hash,64)!=0) { + return EFI_SECURITY_VIOLATION; + } + UINT8 *padding=NULL; + status=bp_initfs_generate_padding(sizeof(header->padding),header->installation_id[0],&padding); + if (EFI_ERROR(status)) { + return status; + } + if (CompareMem(padding,header->padding,sizeof(header->padding))!=0) { + return EFI_SECURITY_VIOLATION; + } + bp_crypto_HASH_DIGEST *header_hash=NULL; + status=bp_crypto_sha3((UINT8 *)header,sizeof(bp_initfs_INITFS_HEADER)-sizeof(header->header_hash),64,&header_hash); + if (EFI_ERROR(status)) { + return status; + } + if (CompareMem(header_hash->digest,header->header_hash,sizeof(header->header_hash))!=0) { + return EFI_VOLUME_CORRUPTED; + } + if (header->table_size!=header->entries_count*header->entries_width) { + return EFI_VOLUME_CORRUPTED; + } + if (header->initfs_size!=512+header->table_size+header->files_area_size) { + return EFI_VOLUME_CORRUPTED; + } + if (header->files_area_offset!=512+header->table_size) { + return EFI_VOLUME_CORRUPTED; + } + if (header->files_area_size+header->table_size>header->initfs_size) { + return EFI_VOLUME_CORRUPTED; + } + return EFI_SUCCESS; +} +EFI_STATUS bp_initfs_parse_signsyst_header(UINT8 *header_data,UINTN buffer_size,bp_initfs_SIGNSYST_HEADER **header) { + if (header==NULL || header_data==NULL) { + return EFI_INVALID_PARAMETER; + } + if (buffer_sizeinitfs_version!=BP_INITFS_INITFS_SUPPORTED_VERSION) { + return EFI_UNSUPPORTED; + } + return EFI_SUCCESS; +} +EFI_STATUS bp_initfs_check_signsyst_header(bp_initfs_SIGNSYST_HEADER *header,EFI_FILE_PROTOCOL *root) { + if (header==NULL) { + return EFI_INVALID_PARAMETER; + } + EFI_STATUS status; + UINT8 *padding=NULL; + status=bp_initfs_generate_padding(sizeof(header->padding),header->installation_id[sizeof(header->installation_id)-1],&padding); + if (EFI_ERROR(status)) { + return status; + } + if (CompareMem(padding,header->padding,sizeof(header->padding))!=0) { + return EFI_SECURITY_VIOLATION; + } + bp_crypto_HASH_DIGEST *header_hash=NULL; + status=bp_crypto_sha3((UINT8 *)header,sizeof(bp_initfs_SIGNSYST_HEADER)-sizeof(header->header_hash),64,&header_hash); + if (EFI_ERROR(status)) { + return status; + } + if (CompareMem(header_hash->digest,header->header_hash,sizeof(header->header_hash))!=0) { + return EFI_SECURITY_VIOLATION; + } + EFI_FILE_PROTOCOL *opened_file=NULL; + status=bp_disk_open_file(root,L"\\EFI\\BlastProofFiles\\signsyst-hash.bin",&opened_file); + if (EFI_ERROR(status)) { + return status; + } + bp_disk_FILE *file=NULL; + status=bp_disk_read_file(opened_file,&file); + if (EFI_ERROR(status)) { + return status; + } + if (file->file_size_bytes!=64) { + return EFI_SECURITY_VIOLATION; + } + bp_crypto_HASH_DIGEST *signsyst_header_hash=NULL; + status=bp_crypto_sha3((UINT8 *)header,sizeof(bp_initfs_SIGNSYST_HEADER),64,&signsyst_header_hash); + if (EFI_ERROR(status)) { + return status; + } + if (CompareMem(signsyst_header_hash->digest,file->content,64)!=0) { + return EFI_SECURITY_VIOLATION; + } + if (header->signature_block_size!=header->signature_count*header->signature_size) { + return EFI_VOLUME_CORRUPTED; + } + if (header->signsyst_size!=512+header->signature_block_size) { + return EFI_VOLUME_CORRUPTED; + } + if (header->signature_block_size>header->signsyst_size) { + return EFI_VOLUME_CORRUPTED; + } + return EFI_SUCCESS; +} +EFI_STATUS bp_initfs_cross_check_headers(bp_initfs_INITFS_HEADER *initfs,bp_initfs_SIGNSYST_HEADER *signsyst) { + if (initfs==NULL || signsyst==NULL) { + return EFI_INVALID_PARAMETER; + } + if (initfs->bootloader_version!=signsyst->bootloader_version || initfs->initfs_version!=signsyst->initfs_version || initfs->os_version!=signsyst->os_version) { + return EFI_SECURITY_VIOLATION; + } + if (CompareMem(initfs->installation_id,signsyst->installation_id,sizeof(initfs->installation_id))!=0) { + return EFI_SECURITY_VIOLATION; + } + if (initfs->entries_count!=signsyst->signature_count) { + return EFI_SECURITY_VIOLATION; + } + return EFI_SUCCESS; +} +EFI_STATUS bp_initfs_create_initfs_structure(bp_initfs_INITFS_HEADER *supposed_header,UINT8 *loaded_initfs,bp_initfs_INITFS **initfs) { + if (supposed_header==NULL || loaded_initfs==NULL || initfs==NULL) { + return EFI_INVALID_PARAMETER; + } + bp_initfs_INITFS *fs=AllocatePool(sizeof(bp_initfs_INITFS)); + if (!fs) { + return EFI_OUT_OF_RESOURCES; + } + if (CompareMem(supposed_header,loaded_initfs,512)!=0) { + return EFI_VOLUME_CORRUPTED; + } + fs->header=supposed_header; + if (fs->header->entries_count==0) { + return EFI_NOT_FOUND; + } + bp_initfs_FILES_TABLE *files_table=AllocatePool(sizeof(bp_initfs_FILES_TABLE)); + if (!files_table) { + FreePool(fs); + return EFI_OUT_OF_RESOURCES; + } + files_table->files_entries=(bp_initfs_FILE_ENTRY *)(loaded_initfs+512); + files_table->file_count=fs->header->entries_count; + fs->files_table=files_table; + fs->files_area=loaded_initfs+512+(256*fs->header->entries_count); + *initfs=fs; + return EFI_SUCCESS; +} +EFI_STATUS bp_initfs_create_signsyst_structure(bp_initfs_SIGNSYST_HEADER *supposed_header,UINT8 *loaded_signsyst,bp_initfs_SIGNSYST **signsyst) { + if (supposed_header==NULL || loaded_signsyst==NULL || signsyst==NULL) { + return EFI_INVALID_PARAMETER; + } + bp_initfs_SIGNSYST *fs=AllocatePool(sizeof(bp_initfs_SIGNSYST)); + if (!fs) { + return EFI_OUT_OF_RESOURCES; + } + if (CompareMem(supposed_header,loaded_signsyst,512)!=0) { + return EFI_VOLUME_CORRUPTED; + } + fs->header=supposed_header; + if (fs->header->signature_count==0) { + return EFI_NOT_FOUND; + } + fs->signature_area=loaded_signsyst+512; + *signsyst=fs; + return EFI_SUCCESS; +} +EFI_STATUS bp_initfs_check_initfs_integrity(bp_initfs_INITFS *initfs,bp_initfs_SIGNSYST *signsyst) { + if (initfs==NULL || signsyst==NULL) { + return EFI_INVALID_PARAMETER; + } + EFI_STATUS status; + bp_crypto_HASH_DIGEST *table_hash=NULL; + status=bp_crypto_sha3((UINT8 *)initfs->files_table->files_entries,sizeof(bp_initfs_FILE_ENTRY)*initfs->header->entries_count,64,&table_hash); + if (EFI_ERROR(status)) { + return status; + } + for (UINTN i=0;i<48;i++) { + table_hash->digest[i+16]^=initfs->header->installation_id[i]; + } + if (CompareMem(table_hash->digest,initfs->header->entries_table_hash,64)) { + return EFI_VOLUME_CORRUPTED; + } + bp_crypto_HASH_DIGEST *files_area_hash=NULL; + status=bp_crypto_sha3(initfs->files_area,initfs->header->files_area_size,64,&files_area_hash); + if (EFI_ERROR(status)) { + return status; + } + for (UINTN i=0;i<48;i++) { + files_area_hash->digest[i+16]^=initfs->header->installation_id[i]; + } + if (CompareMem(files_area_hash->digest,initfs->header->files_area_hash,64)!=0) { + return EFI_VOLUME_CORRUPTED; + } + bp_crypto_HASH_DIGEST *signature_area_hash=NULL; + status=bp_crypto_sha3(signsyst->signature_area,signsyst->header->signature_block_size,64,&signature_area_hash); + if (EFI_ERROR(status)) { + return status; + } + for (UINTN i=0;i<48;i++) { + signature_area_hash->digest[i+16]^=initfs->header->installation_id[i]; + } + if (CompareMem(signature_area_hash->digest,signsyst->header->signature_block_hash,64)!=0) { + return EFI_VOLUME_CORRUPTED; + } + UINTN cursor=0; + for (UINTN i=0;iheader->entries_count;++i) { + bp_crypto_HASH_DIGEST *file_hash=NULL; + status=bp_crypto_sha3(initfs->files_area+cursor,initfs->files_table->files_entries[i].file_size,64,&file_hash); + if (EFI_ERROR(status)) { + return status; + } + if (CompareMem(file_hash->digest,initfs->files_table->files_entries[i].hash,64)!=0) { + return EFI_SECURITY_VIOLATION; + } + status=bp_crypto_sphincsplus_verify(signsyst->signature_area+i*signsyst->header->signature_size,signsyst->header->signature_size,initfs->files_area+cursor,initfs->files_table->files_entries[i].file_size,initfs->files_table->files_entries[i].pk); + if (EFI_ERROR(status)) { + return EFI_SECURITY_VIOLATION; + } + FreePool(file_hash->digest); + FreePool(file_hash); + if (cursor+initfs->files_table->files_entries[i].file_size>initfs->header->files_area_size) { + return EFI_VOLUME_CORRUPTED; + } + cursor+=initfs->files_table->files_entries[i].file_size; + } + return EFI_SUCCESS; +} +EFI_STATUS bp_initfs_generate_file_name(CHAR8 *file_name_in,UINT8 **file_name_out) { + if (file_name_in==NULL || file_name_out==NULL) { + return EFI_INVALID_PARAMETER; + } + UINTN file_name_len=AsciiStrnLenS(file_name_in,113); + if (file_name_len==113) { + return EFI_INVALID_PARAMETER; + } + *file_name_out=AllocateZeroPool(112); + if (!*file_name_out) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem(*file_name_out,file_name_in,file_name_len); + return EFI_SUCCESS; +} +EFI_STATUS bp_initfs_read_file(bp_initfs_INITFS *initfs,CHAR8 *file_name,bp_disk_FILE **file) { + if (initfs==NULL || file_name==NULL || file==NULL) { + return EFI_INVALID_PARAMETER; + } + UINT8 *file_name_raw=NULL; + EFI_STATUS status=bp_initfs_generate_file_name(file_name,&file_name_raw); + if (EFI_ERROR(status)) { + return status; + } + UINTN index=0; + for (UINTN i=0;iheader->entries_count;++i) { + if (CompareMem(file_name_raw,initfs->files_table->files_entries[i].file_name,112)==0) { + index=i+1; + break; + } + } + if (index==0) { + return EFI_NOT_FOUND; + } + index-=1; + if (initfs->files_table->files_entries[index].file_offset+initfs->files_table->files_entries[index].file_size>initfs->header->files_area_size) { + return EFI_BAD_BUFFER_SIZE; + } + bp_disk_FILE *file_out=AllocatePool(sizeof(bp_disk_FILE)); + if (!file_out) { + return EFI_OUT_OF_RESOURCES; + } + file_out->file_size_bytes=initfs->files_table->files_entries[index].file_size; + file_out->content=AllocateZeroPool(file_out->file_size_bytes); + if (!file_out->content) { + FreePool(file_out); + return EFI_OUT_OF_RESOURCES; + } + CopyMem(file_out->content,initfs->files_area+initfs->files_table->files_entries[index].file_offset,file_out->file_size_bytes); + *file=file_out; + return EFI_SUCCESS; +} diff --git a/Blastproof/src/libs/src/ui.c b/Blastproof/src/libs/src/ui.c new file mode 100644 index 0000000..4274ed1 --- /dev/null +++ b/Blastproof/src/libs/src/ui.c @@ -0,0 +1,311 @@ +#include "../include/ui.h" +#include "../include/graphic.h" +#include "../include/debug.h" +#include +#include +#include +#include +#include +#include +#include +#include +const UINT8 bootanim_header[8]={'B','o','o','t','A','n','i','m'}; +EFI_STATUS bp_ui_draw_text_ascii(UINT32 *fb_base,bp_font_FBM_FONT_RENDER *font,INTN x,INTN y,UINTN width,UINTN heigth,CHAR8 *text,bp_ui_TEXT_BOX **handle) { + if (fb_base==NULL || font==NULL || text==NULL) { + return EFI_INVALID_PARAMETER; + } + if (width==0 || heigth==0) { + *handle=NULL; + return EFI_INVALID_PARAMETER; + } + if (x==-1) { + x=(width/2)-((font->chars[0].width*AsciiStrLen(text))/2); + } + if (y==-1) { + y=(heigth/2)-(font->chars[0].height/2); + } + *handle=AllocatePool(sizeof(bp_ui_TEXT_BOX)); + if (!*handle) { + *handle=NULL; + return EFI_OUT_OF_RESOURCES; + } + (*handle)->x=x; + (*handle)->y=y; + (*handle)->width=font->chars[0].width*AsciiStrLen(text); + (*handle)->height=font->chars[0].height; + UINTN *index_array=AllocatePool(AsciiStrLen(text)*sizeof(UINTN)); + if (!index_array) { + FreePool(*handle); + *handle=NULL; + return EFI_OUT_OF_RESOURCES; + } + UINT32 codepoint=0xFF000000; + UINTN not_found_index=0; + BOOLEAN found=FALSE; + for (UINTN y=0;yentries_count;++y) { + if (font->chars[y].codepoint==codepoint) { + not_found_index=y; + found=TRUE; + break; + } + } + if (!found) { + FreePool(*handle); + FreePool(index_array); + *handle=NULL; + return EFI_NOT_FOUND; + } + for (UINTN i=0;ientries_count;++j) { + if (font->chars[j].codepoint==codepoint) { + index=j; + found=TRUE; + break; + } + } + if (!found) { + index_array[i]=not_found_index; + } else { + index_array[i]=index; + } + } + UINT32 *space_buf=AllocateZeroPool(font->chars[0].width*font->chars[0].height*sizeof(UINT32)); + for (UINTN i=0;ichars[0].width; + if (x_writechars[index_array[i]].data,heigth,width,x_write,y,font->chars[index_array[i]].height,font->chars[index_array[i]].width); + } else { + bp_graphic_copy_frame_part(fb_base,space_buf,heigth,width,x_write,y,font->chars[index_array[i]].height,font->chars[index_array[i]].width); + } + } + } + FreePool(index_array); + return EFI_SUCCESS; +} +EFI_STATUS bp_ui_render_boot_animation_4frames(bp_disk_FILE *boot_animation_file,UINTN screen_width,UINTN screen_height,UINTN frame_index,bp_ui_BOOT_ANIMATION **rendered_boot_animation) { + if (!boot_animation_file || !rendered_boot_animation) { + return EFI_INVALID_PARAMETER; + } + UINT64 w=0,h=0,frames_count=0,pixels_per_frame=0; + CopyMem(&w,boot_animation_file->content+8,8); + CopyMem(&h,boot_animation_file->content+16,8); + CopyMem(&frames_count,boot_animation_file->content+24,8); + CopyMem(&pixels_per_frame,boot_animation_file->content+32,8); + if (frame_index>=frames_count) { + return EFI_INVALID_PARAMETER; + } + if (*rendered_boot_animation==NULL) { + *rendered_boot_animation=AllocatePool(sizeof(bp_ui_BOOT_ANIMATION)); + if (!*rendered_boot_animation) { + return EFI_OUT_OF_RESOURCES; + } + (*rendered_boot_animation)->frames_count=4; + (*rendered_boot_animation)->frames=AllocatePool(sizeof(bp_ui_BOOT_ANIMATION_FRAME)*4); + if (!(*rendered_boot_animation)->frames) { + FreePool(*rendered_boot_animation); + *rendered_boot_animation=NULL; + return EFI_OUT_OF_RESOURCES; + } + } + double scale_x=(double)screen_width/(double)w; + double scale_y=(double)screen_height/(double)h; + UINT16 *frame0=AllocatePool(sizeof(UINT16)*2*pixels_per_frame); + UINT16 *frame1=AllocatePool(sizeof(UINT16)*2*pixels_per_frame); + if (!frame0 || !frame1) { + return EFI_OUT_OF_RESOURCES; + } + UINT8 *cursor=boot_animation_file->content+40+frame_index*sizeof(UINT16)*2*pixels_per_frame; + CopyMem(frame0,cursor,sizeof(UINT16)*2*pixels_per_frame); + cursor+=sizeof(UINT16)*2*pixels_per_frame; + CopyMem(frame1,cursor,sizeof(UINT16)*2*pixels_per_frame); + for (UINTN f=0;f<4;f++) { + bp_ui_BOOT_ANIMATION_FRAME *dst=&(*rendered_boot_animation)->frames[f]; + dst->pixels_count=screen_width*screen_height; + dst->pixels=AllocatePool(sizeof(UINT32)*screen_width*screen_height); + if (!dst->pixels) { + for (UINTN j=0;jframes[j].pixels); + } + FreePool(frame0); + FreePool(frame1); + FreePool((*rendered_boot_animation)->frames); + FreePool(*rendered_boot_animation); + *rendered_boot_animation=NULL; + return EFI_OUT_OF_RESOURCES; + } + SetMem32(dst->pixels,sizeof(UINT32)*screen_width*screen_height,0); + double t; + if (f!=0) { + t=(f+1)/4.0; + } else { + t=0.0; + } + for (UINTN p=0;p=screen_width)x_interp=screen_width-1; + if (y_interp>=screen_height)y_interp=screen_height-1; + dst->pixels[y_interp*screen_width+x_interp]=0xFFFFFFFF; + } + } + FreePool(frame0); + FreePool(frame1); + return EFI_SUCCESS; +} +EFI_STATUS bp_ui_render_boot_animation_last_frame(bp_disk_FILE *boot_animation_file,UINTN screen_width,UINTN screen_height,bp_ui_BOOT_ANIMATION_FRAME **rendered_frame) { + if (!boot_animation_file || !rendered_frame) { + return EFI_INVALID_PARAMETER; + } + UINT64 w=0,h=0,frames_count=0,pixels_per_frame=0; + CopyMem(&w,boot_animation_file->content+8,8); + CopyMem(&h,boot_animation_file->content+16,8); + CopyMem(&frames_count,boot_animation_file->content+24,8); + CopyMem(&pixels_per_frame,boot_animation_file->content+32,8); + if (*rendered_frame==NULL) { + *rendered_frame=AllocateZeroPool(sizeof(bp_ui_BOOT_ANIMATION_FRAME)); + if (!*rendered_frame) { + return EFI_OUT_OF_RESOURCES; + } + (*rendered_frame)->pixels=AllocateZeroPool(screen_height*screen_width*sizeof(UINT32)); + if (!(*rendered_frame)->pixels) { + FreePool(*rendered_frame); + } + } + double scale_x=(double)screen_width/(double)w; + double scale_y=(double)screen_height/(double)h; + UINT16 *frame=AllocateZeroPool(sizeof(UINT16)*2*pixels_per_frame); + if (!frame) { + FreePool(*rendered_frame); + FreePool((*rendered_frame)->pixels); + return EFI_OUT_OF_RESOURCES; + } + UINTN size=+40+frames_count*sizeof(UINT16)*2*pixels_per_frame; + UINT8 *cursor=boot_animation_file->content+size-sizeof(UINT16)*2*pixels_per_frame; + CopyMem(frame,cursor,sizeof(UINT16)*2*pixels_per_frame); + for (INTN i=0;i=screen_width)x=screen_width-1; + if (y>=screen_height)y=screen_height-1; + (*rendered_frame)->pixels[y*screen_width+x]=0xFFFFFFFF; + (*rendered_frame)->pixels[y*screen_width+x+1]=0xFFFFFFFF; + (*rendered_frame)->pixels[(y+1)*screen_width+x]=0xFFFFFFFF; + (*rendered_frame)->pixels[(y+1)*screen_width+x+1]=0xFFFFFFFF; + (*rendered_frame)->pixels[y*screen_width+x-1]=0xFFFFFFFF; + (*rendered_frame)->pixels[(y-1)*screen_width+x]=0xFFFFFFFF; + (*rendered_frame)->pixels[(y-1)*screen_width+x-1]=0xFFFFFFFF; + (*rendered_frame)->pixels[(y-1)*screen_width+x+1]=0xFFFFFFFF; + (*rendered_frame)->pixels[(y+1)*screen_width+x-1]=0xFFFFFFFF; + } + FreePool(frame); + return EFI_SUCCESS; +} +EFI_STATUS bp_ui_render_boot_animation_last_frame_color(bp_disk_FILE *boot_animation_file,UINTN screen_width,UINTN screen_height,UINT32 color,bp_ui_BOOT_ANIMATION_FRAME **rendered_frame) { + if (!boot_animation_file || !rendered_frame) { + return EFI_INVALID_PARAMETER; + } + UINT64 w=0,h=0,frames_count=0,pixels_per_frame=0; + CopyMem(&w,boot_animation_file->content+8,8); + CopyMem(&h,boot_animation_file->content+16,8); + CopyMem(&frames_count,boot_animation_file->content+24,8); + CopyMem(&pixels_per_frame,boot_animation_file->content+32,8); + if (*rendered_frame==NULL) { + *rendered_frame=AllocateZeroPool(sizeof(bp_ui_BOOT_ANIMATION_FRAME)); + if (!*rendered_frame) { + return EFI_OUT_OF_RESOURCES; + } + (*rendered_frame)->pixels=AllocateZeroPool(screen_height*screen_width*sizeof(UINT32)); + if (!(*rendered_frame)->pixels) { + FreePool(*rendered_frame); + } + } + double scale_x=(double)screen_width/(double)w; + double scale_y=(double)screen_height/(double)h; + UINT16 *frame=AllocateZeroPool(sizeof(UINT16)*2*pixels_per_frame); + if (!frame) { + FreePool(*rendered_frame); + FreePool((*rendered_frame)->pixels); + return EFI_OUT_OF_RESOURCES; + } + UINTN size=+40+frames_count*sizeof(UINT16)*2*pixels_per_frame; + UINT8 *cursor=boot_animation_file->content+size-sizeof(UINT16)*2*pixels_per_frame; + CopyMem(frame,cursor,sizeof(UINT16)*2*pixels_per_frame); + for (INTN i=0;i=screen_width)x=screen_width-1; + if (y>=screen_height)y=screen_height-1; + (*rendered_frame)->pixels[y*screen_width+x]=color; + (*rendered_frame)->pixels[y*screen_width+x+1]=color; + (*rendered_frame)->pixels[(y+1)*screen_width+x]=color; + (*rendered_frame)->pixels[(y+1)*screen_width+x+1]=color; + (*rendered_frame)->pixels[y*screen_width+x-1]=color; + (*rendered_frame)->pixels[(y-1)*screen_width+x]=color; + (*rendered_frame)->pixels[(y-1)*screen_width+x-1]=color; + (*rendered_frame)->pixels[(y-1)*screen_width+x+1]=color; + (*rendered_frame)->pixels[(y+1)*screen_width+x-1]=color; + } + FreePool(frame); + (*rendered_frame)->pixels_count=screen_width*screen_height; + return EFI_SUCCESS; +} +EFI_STATUS bp_ui_show_error_screen(EFI_SERIAL_IO_PROTOCOL *serial,CHAR8 *error_code,UINT32* fb_base,bp_font_FBM_FONT_RENDER *font,UINTN horizontal_resolution,UINTN vertical_resoltion,bp_ui_BOOT_ANIMATION_FRAME *background_logo,BOOLEAN allow_erroring) { + EFI_STATUS status; + CHAR8 temp1[256]; + CHAR8 temp2[256]; + if (fb_base==NULL || font==NULL || background_logo==NULL || error_code==NULL) { + AsciiSPrint(temp1,256,"[BlastProof:BlastUi] Error: Couldn't display error screen. Given error code: %s",error_code); + bp_debug_write_line(serial,temp1,allow_erroring); + bp_debug_write_line(serial,"[BlastProof:BlastUi] Hint for why this error happen: bad pointer.",allow_erroring); + return EFI_INVALID_PARAMETER; + } + if (AsciiStrnLenS(error_code,128)==0) { + AsciiSPrint(temp1,256,"[BlastProof:BlastUi]: Couldn't display error screen.",error_code); + bp_debug_write_line(serial,temp1,allow_erroring); + bp_debug_write_line(serial,"[BlastProof:BlastUi] Hint for why this error happen: error code empty.",allow_erroring); + return EFI_INVALID_PARAMETER; + } + SetMem32(fb_base,horizontal_resolution*vertical_resoltion,0x00000000); + for (UINTN i=0;ipixels+i*horizontal_resolution,horizontal_resolution*sizeof(UINT32)); + } + AsciiSPrint(temp1,256,"An error happened. Please reboot your computer."); + bp_ui_TEXT_BOX *handle_1; + status=bp_ui_draw_text_ascii(fb_base,font,-1,vertical_resoltion*0.85,horizontal_resolution,vertical_resoltion,temp1,&handle_1); + if (EFI_ERROR(status)) { + AsciiSPrint(temp1,256,"[BlastProof:BlastUi]: Couldn't display error screen. Given error code: %s",error_code); + bp_debug_write_line(serial,temp1,allow_erroring); + bp_debug_write_line(serial,"[BlastProof:BlastUi] Hint for why this error happen: couldn't display first line.",allow_erroring); + return status; + } + AsciiSPrint(temp2,256,"Error code: %a",error_code); + bp_ui_TEXT_BOX *handle_2; + status=bp_ui_draw_text_ascii(fb_base,font,-1,vertical_resoltion*0.85+16,horizontal_resolution,vertical_resoltion,temp2,&handle_2); + if (EFI_ERROR(status)) { + AsciiSPrint(temp1,256,"[BlastProof:BlastUi]: Couldn't display error screen. Given error code: %s",error_code); + bp_debug_write_line(serial,temp1,allow_erroring); + bp_debug_write_line(serial,"[BlastProof:BlastUi] Hint for why this error happen: couldn't display second line.",allow_erroring); + return status; + } + return EFI_SUCCESS; +} +EFI_STATUS bp_ui_erase_text(UINT32 *fb_base,UINTN screen_width,UINTN screen_heigth,bp_ui_TEXT_BOX *text_box) { + if (fb_base==NULL || text_box==NULL) { + return EFI_INVALID_PARAMETER; + } + if (text_box->x+text_box->width>screen_width || text_box->y+text_box->height>screen_heigth) { + return EFI_INVALID_PARAMETER; + } + for (UINTN y=0;yheight;y++) { + SetMem32(fb_base+(text_box->y+y)*screen_width+text_box->x,text_box->width*sizeof(UINT32),0x00000000); + } + return EFI_SUCCESS; +} diff --git a/Blastproof/src/libs/src/vyx.c b/Blastproof/src/libs/src/vyx.c new file mode 100644 index 0000000..3c7fd36 --- /dev/null +++ b/Blastproof/src/libs/src/vyx.c @@ -0,0 +1,305 @@ +#include "../include/vyx.h" +#include "Base.h" +#include +#include +#include +#define PAGE_PRESENT (1ULL << 0) +#define PAGE_TABLE_FLAGS (1ULL<<1) +#define PAGE_RW (1ULL << 1) +#define PAGE_US (1ULL << 2) +#define PAGE_PWT (1ULL << 3) +#define PAGE_PCD (1ULL << 4) +#define PAGE_ACCESSED (1ULL << 5) +#define PAGE_DIRTY (1ULL << 6) +#define PAGE_PS (1ULL << 7) +#define PAGE_GLOBAL (1ULL << 8) +#define PAGE_NX (1ULL << 63) +static BOOLEAN is_allocator_init=FALSE; +static UINT64 pt_pool_page_offset=0; +UINTN pad_size(UINTN value,UINTN multiple) { + if (multiple==0) return value; + UINTN remainder=value%multiple; + if (remainder==0) return value; + return value+multiple-remainder; +} +EFI_STATUS bp_vyx_get_header(bp_disk_FILE *file,bp_vyx_VYX_HEADER **header) { + if (file==NULL || header==NULL) { + return EFI_INVALID_PARAMETER; + } + if (file->file_size_bytescontent,3)!=0) { + return EFI_COMPROMISED_DATA; + } + *header=(bp_vyx_VYX_HEADER *)file->content; + if ((*header)->vyx_ver!=BP_VYX_SUPPORTED_VERSION) { + return EFI_UNSUPPORTED; + } + return EFI_SUCCESS; +} +EFI_STATUS bp_vyx_create_shelter_boot_conf(UINT8 log_level,BOOLEAN test_benchmark,UINT64 bench_iterations,BOOLEAN log_disable_serial_port,BOOLEAN disable_serial_port,UINT16 log_ring_size,bp_vyx_SHELTER_BOOT_CONFIG *boot_config) { + if (boot_config==NULL) { + return EFI_INVALID_PARAMETER; + } + boot_config->log_level=log_level; + boot_config->test_benchmark=test_benchmark; + boot_config->bench_iterations=bench_iterations; + boot_config->log_disable_serial_port=log_disable_serial_port; + boot_config->disable_serial_port=disable_serial_port; + boot_config->log_ring_size=log_ring_size; + UINT8 sig_start[8]={'S','h','C','f','g','B','e','g'}; + UINT8 sig_end[8]={'S','h','C','f','g','E','n','d'}; + for (UINTN i=0;i<8;i++) {boot_config->sig_start[i]=sig_start[i];boot_config->sig_end[i]=sig_end[i];}; + return EFI_SUCCESS; +} +EFI_STATUS bp_vyx_init_pt_pool_allocator() { + pt_pool_page_offset=0; + is_allocator_init=TRUE; + return EFI_SUCCESS; +} +UINT64 bp_vyx_get_pt_pool_allocator() { + return pt_pool_page_offset; +} +EFI_PHYSICAL_ADDRESS bp_vyx_pt_pool_alloc_one_page(EFI_PHYSICAL_ADDRESS pt_pool_base) { + if (!is_allocator_init) return 0; + if (pt_pool_base==0) { + return (EFI_PHYSICAL_ADDRESS)0; + } + if (pt_pool_page_offset>=BP_VYX_SHELTER_PT_PAGE_COUNT) { + return (EFI_PHYSICAL_ADDRESS)0; + } + EFI_PHYSICAL_ADDRESS out=pt_pool_base+pt_pool_page_offset*4096; + pt_pool_page_offset++; + return out; +} +EFI_STATUS bp_vyx_setup_physical_pages(bp_vyx_VYX_HEADER *header,bp_disk_FILE *vyx_file,bp_disk_FILE *keycard_file,bp_vyx_SHELTER_BOOT_CONFIG *shelter_boot_config,bp_vyx_LOADER_SETTING **loader_setting) { + if (header==NULL || vyx_file==NULL || loader_setting==NULL || keycard_file==NULL || shelter_boot_config==NULL) { + return EFI_INVALID_PARAMETER; + } + UINTN text_size=header->text_size_padded; + if (text_size==0) {return EFI_COMPROMISED_DATA;}; + if (text_size%4096!=0) {text_size=pad_size(text_size,4096);}; + UINTN data_size=header->data_size_padded; + if (data_size!=0 && data_size%4096!=0) {data_size=pad_size(data_size,4096);}; + UINTN rodata_size=header->rodata_size_padded; + if (rodata_size!=0 && rodata_size%4096!=0) {rodata_size=pad_size(rodata_size,4096);}; + UINTN bss_size=header->bss_size_padded; + if (bss_size!=0 && bss_size%4096!=0) {bss_size=pad_size(bss_size,4096);}; + UINTN keycard_size=keycard_file->file_size_bytes; + if (keycard_size==0) {return EFI_COMPROMISED_DATA;}; + if (keycard_size%4096!=0) {keycard_size=pad_size(keycard_size,4096);}; + UINTN keycard_pages=(keycard_size+4095)/4096; + *loader_setting=AllocatePool(sizeof(bp_vyx_LOADER_SETTING)); + UINTN shelter_boot_config_size=sizeof(bp_vyx_SHELTER_BOOT_CONFIG); + if (shelter_boot_config_size%4096!=0) {shelter_boot_config_size=pad_size(shelter_boot_config_size,4096);}; + UINTN shelter_boot_config_pages=(shelter_boot_config_size+4095)/4096; + if (!*loader_setting) { + return EFI_OUT_OF_RESOURCES; + } + EFI_STATUS status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderCode,text_size/4096,&(*loader_setting)->text_addr_phys); + if (EFI_ERROR(status)) { + FreePool(*loader_setting); + return status; + } + if (data_size!=0) { + status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderData,data_size/4096,&(*loader_setting)->data_addr_phys); + if (EFI_ERROR(status)) { + FreePool(*loader_setting); + return status; + } + } else { + (*loader_setting)->data_addr_phys=0; + } + if (rodata_size!=0) { + status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderData,rodata_size/4096,&(*loader_setting)->rodata_addr_phys); + if (EFI_ERROR(status)) { + FreePool(*loader_setting); + return status; + } + } else { + (*loader_setting)->rodata_addr_phys=0; + } + if (bss_size!=0) { + status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderData,bss_size/4096,&(*loader_setting)->bss_addr_phys); + if (EFI_ERROR(status)) { + FreePool(*loader_setting); + return status; + } + } else { + (*loader_setting)->bss_addr_phys=0; + } + status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderCode,64,&(*loader_setting)->stack_addr_phys); + if (EFI_ERROR(status)) { + FreePool(*loader_setting); + return status; + } + status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderCode,keycard_pages,&(*loader_setting)->keycard_addr_phys); + if (EFI_ERROR(status)) { + FreePool(*loader_setting); + return status; + } + status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderCode,shelter_boot_config_pages,&(*loader_setting)->shelter_boot_config_addr_phys); + if (EFI_ERROR(status)) { + FreePool(*loader_setting); + return status; + } + status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderCode,BP_VYX_SHELTER_MEMORY_MAP_PAGES_COUNT,&(*loader_setting)->shelter_memory_map_addr_phys); + if (EFI_ERROR(status)) { + FreePool(*loader_setting); + return status; + } + status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderCode,BP_VYX_SHELTER_PT_PAGE_COUNT,&(*loader_setting)->shelter_page_table_addr_phys); + if (EFI_ERROR(status)) { + FreePool(*loader_setting); + return status; + } + status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderCode,MAX(shelter_boot_config->log_ring_size,4),&(*loader_setting)->shelter_logging_ring_buffer_addr_phys); + if (EFI_ERROR(status)) { + FreePool(*loader_setting); + return status; + } + (*loader_setting)->keycard_page_count=keycard_pages; + (*loader_setting)->shelter_boot_config_pages_count=shelter_boot_config_pages; + if (bss_size!=0) {SetMem((VOID*)(UINTN)(*loader_setting)->bss_addr_phys,bss_size,0);}; + SetMem((VOID*)(UINTN)(*loader_setting)->stack_addr_phys,64*4096,0); + SetMem((VOID*)(UINTN)(*loader_setting)->shelter_memory_map_addr_phys,16*4096,0); + SetMem((VOID*)(UINTN)(*loader_setting)->shelter_logging_ring_buffer_addr_phys,shelter_boot_config->log_ring_size*4096,0); + (*loader_setting)->shelter_logging_ring_buffer_pages_count=MAX(shelter_boot_config->log_ring_size,4); + CopyMem((VOID*)(UINTN)(*loader_setting)->text_addr_phys,vyx_file->content+sizeof(bp_vyx_VYX_HEADER),header->text_size_padded); + if (data_size!=0 && rodata_size!=0) { + CopyMem((VOID*)(UINTN)(*loader_setting)->data_addr_phys,vyx_file->content+sizeof(bp_vyx_VYX_HEADER)+header->text_size_padded,header->data_size_padded); + CopyMem((VOID*)(UINTN)(*loader_setting)->rodata_addr_phys,vyx_file->content+sizeof(bp_vyx_VYX_HEADER)+header->text_size_padded+header->data_size_padded,header->rodata_size_padded); + } else if (data_size==0 && rodata_size!=0) { + CopyMem((VOID*)(UINTN)(*loader_setting)->rodata_addr_phys,vyx_file->content+sizeof(bp_vyx_VYX_HEADER)+header->text_size_padded,header->rodata_size_padded); + } else if (data_size!=0 && rodata_size==0) { + CopyMem((VOID*)(UINTN)(*loader_setting)->data_addr_phys,vyx_file->content+sizeof(bp_vyx_VYX_HEADER)+header->text_size_padded,header->data_size_padded); + } + if (!(keycard_file->file_size_bytes<=keycard_pages*4096)) return EFI_COMPROMISED_DATA; + CopyMem((VOID*)(UINTN)(*loader_setting)->keycard_addr_phys,keycard_file->content,keycard_file->file_size_bytes); + CopyMem((VOID*)(UINTN)(*loader_setting)->shelter_boot_config_addr_phys,(VOID*)shelter_boot_config,sizeof(bp_vyx_SHELTER_BOOT_CONFIG)); + return EFI_SUCCESS; +} +EFI_STATUS map_page(UINT64* pml4,UINT64 va,UINT64 pa,UINT64 flags) { + UINT64 pml4_i=(va>>39) & 0x1FF; + UINT64 pdpt_i=(va>>30) & 0x1FF; + UINT64 pd_i=(va>>21) & 0x1FF; + UINT64 pt_i=(va>>12) & 0x1FF; + UINT64* pdpt; + UINT64* pd; + UINT64* pt; + EFI_PHYSICAL_ADDRESS phys_page; + if (!(pml4[pml4_i] & 1)) { + phys_page=bp_vyx_pt_pool_alloc_one_page((EFI_PHYSICAL_ADDRESS)pml4); + if (phys_page==0) return EFI_OUT_OF_RESOURCES; + pdpt=(UINT64*)(UINTN)phys_page; + SetMem(pdpt,4096,0); + pml4[pml4_i]=phys_page | PAGE_TABLE_FLAGS | PAGE_PRESENT; + } else { + pdpt=(UINT64*)(UINTN)(pml4[pml4_i] & ~0xFFF); + } + if (!(pdpt[pdpt_i] & 1)) { + phys_page=bp_vyx_pt_pool_alloc_one_page((EFI_PHYSICAL_ADDRESS)pml4); + if (phys_page==0) return EFI_OUT_OF_RESOURCES; + pd=(UINT64*)(UINTN)phys_page; + SetMem(pd,4096,0); + pdpt[pdpt_i]=phys_page | PAGE_TABLE_FLAGS | PAGE_PRESENT; + } else { + pd=(UINT64*)(UINTN)(pdpt[pdpt_i] & ~0xFFF); + } + if (!(pd[pd_i] & 1)) { + phys_page=bp_vyx_pt_pool_alloc_one_page((EFI_PHYSICAL_ADDRESS)pml4); + if (phys_page==0) return EFI_OUT_OF_RESOURCES; + pt=(UINT64*)(UINTN)phys_page; + SetMem(pt,4096,0); + pd[pd_i]=phys_page | PAGE_TABLE_FLAGS | PAGE_PRESENT; + } else { + pt=(UINT64*)(UINTN)(pd[pd_i] & ~0xFFF); + } + pt[pt_i]=(pa & ~0xFFF) | flags | PAGE_PRESENT; + return EFI_SUCCESS; +} +EFI_STATUS bp_vyx_create_page_table(bp_vyx_VYX_HEADER *header,bp_vyx_LOADER_SETTING *settings,EFI_PHYSICAL_ADDRESS *page_table_address,EFI_VIRTUAL_ADDRESS *stack_address,EFI_VIRTUAL_ADDRESS *page_table_pool_va) { + if (header==NULL || settings==NULL || page_table_address==NULL) { + return EFI_INVALID_PARAMETER; + } + UINTN text_size=header->text_size_padded; + if (text_size==0) {return EFI_COMPROMISED_DATA;}; + if (text_size%4096!=0) {text_size=pad_size(text_size,4096);}; + UINTN data_size=header->data_size_padded; + if (data_size!=0 && data_size%4096!=0) {data_size=pad_size(data_size,4096);}; + UINTN rodata_size=header->rodata_size_padded; + if (rodata_size!=0 && rodata_size%4096!=0) {rodata_size=pad_size(rodata_size,4096);}; + UINTN bss_size=header->bss_size_padded; + if (bss_size!=0 && bss_size%4096!=0) {bss_size=pad_size(bss_size,4096);}; + UINTN text_pages_count=text_size/4096; + UINTN data_pages_count=data_size/4096; + UINTN rodata_pages_count=rodata_size/4096; + UINTN bss_pages_count=bss_size/4096; + EFI_VIRTUAL_ADDRESS va=header->text_offset; + EFI_VIRTUAL_ADDRESS va_pt_pool=(header->text_offset+header->text_size_padded+text_size+data_size+rodata_size+bss_size+0x200000ULL) & ~0xFFFULL; + if (va_pt_pool+BP_VYX_SHELTER_PT_PAGE_COUNT*4096>header->stack_base) { + return EFI_COMPROMISED_DATA; + } + EFI_STATUS status; + UINT64 *pml4; + pml4=(UINT64 *)(UINTN)settings->shelter_page_table_addr_phys; + SetMem(pml4,4096*BP_VYX_SHELTER_PT_PAGE_COUNT,0); + bp_vyx_init_pt_pool_allocator(); + for (UINTN i=0;ishelter_page_table_addr_phys+i*4096,PAGE_PRESENT | PAGE_NX | PAGE_RW); + if (EFI_ERROR(status)) return status; + } + *page_table_pool_va=va_pt_pool; + for (UINTN i=0;itext_addr_phys+i*4096,PAGE_PRESENT); + if (EFI_ERROR(status)) return status; + va+=4096; + } + if (settings->data_addr_phys!=0) { + for (UINTN i=0;idata_addr_phys+i*4096,PAGE_PRESENT | PAGE_RW | PAGE_NX); + if (EFI_ERROR(status)) return status; + va+=4096; + } + } + if (settings->rodata_addr_phys!=0) { + for (UINTN i=0;irodata_addr_phys+i*4096,PAGE_PRESENT | PAGE_NX); + if (EFI_ERROR(status)) return status; + va+=4096; + } + } + if (settings->bss_addr_phys!=0) { + for (UINTN i=0;ibss_addr_phys+i*4096,PAGE_PRESENT | PAGE_RW | PAGE_NX); + if (EFI_ERROR(status)) return status; + va+=4096; + } + } + EFI_VIRTUAL_ADDRESS stack_base=header->stack_base; + for (UINTN i=1;i<=64;i++) { + status=map_page(pml4,stack_base+i*4096,settings->stack_addr_phys+(i-1)*4096,PAGE_PRESENT | PAGE_RW | PAGE_NX); + if (EFI_ERROR(status)) return status; + } + *stack_address=stack_base; + for (UINTN i=0;ikeycard_page_count;i++) { + status=map_page(pml4,settings->keycard_addr_phys+i*4096,settings->keycard_addr_phys+i*4096,PAGE_PRESENT); + if (EFI_ERROR(status)) return status; + } + for (UINTN i=0;ishelter_boot_config_pages_count;i++) { + status=map_page(pml4,BP_VYX_SHELTER_BOOT_CONFIG_VA+i*4096,settings->shelter_boot_config_addr_phys+i*4096,PAGE_PRESENT | PAGE_NX | PAGE_RW); + if (EFI_ERROR(status)) return status; + } + for (UINTN i=0;ishelter_memory_map_addr_phys+i*4096,PAGE_PRESENT | PAGE_NX); + if (EFI_ERROR(status)) return status; + } + for (UINTN i=0;ishelter_logging_ring_buffer_pages_count;i++) { + status=map_page(pml4,BP_VYX_SHELTER_LOGGING_RING_BUFFER_VA+i*4096,settings->shelter_logging_ring_buffer_addr_phys+i*4096,PAGE_PRESENT | PAGE_NX | PAGE_RW); + if (EFI_ERROR(status)) return status; + } + *page_table_address=settings->shelter_page_table_addr_phys; + return EFI_SUCCESS; +} diff --git a/Blastproof/src/main.c b/Blastproof/src/main.c new file mode 100644 index 0000000..a7d25e4 --- /dev/null +++ b/Blastproof/src/main.c @@ -0,0 +1,908 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libs/include/debug.h" +#include "libs/include/cpu.h" +#include "libs/include/disk.h" +#include "libs/include/console.h" +#include "libs/include/conf.h" +#include "libs/include/crypto.h" +#include "libs/include/font.h" +#include "libs/include/graphic.h" +#include "libs/include/ui.h" +#include "libs/include/initfs.h" +#include "libs/include/vyx.h" +#include "libs/include/key.h" +#define ARGON2_NO_THREADS +const CHAR16 blastproof_files_path[]=L"\\EFI\\BlastProofFiles\\"; +const CHAR16 blastproof_sign_path[]=L"\\EFI\\BlastProofSign\\"; +BOOLEAN allow_debugging=FALSE; +BOOLEAN allow_erroring=FALSE; +EFI_GUID gEfiLoadedImageProtocolGuid=EFI_LOADED_IMAGE_PROTOCOL_GUID; +EFI_GUID gEfiBlockIoProtocolGuid=EFI_BLOCK_IO_PROTOCOL_GUID; +EFI_GUID gEfiFileInfoGuid=EFI_FILE_INFO_ID; +EFI_GUID gEfiSerialIoProtocolGuid=EFI_SERIAL_IO_PROTOCOL_GUID; +EFI_GUID gEfiPartitionInfoProtocolGuid=EFI_PARTITION_INFO_PROTOCOL_GUID; +UINT16 binary_version=0x0001; +EFI_STATUS +EFIAPI +UefiMain (IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable) { + gST->ConOut->ClearScreen(gST->ConOut); + EFI_SERIAL_IO_PROTOCOL *serial; + gBS->LocateProtocol(&gEfiSerialIoProtocolGuid,NULL,(void**)&serial); + Print(L"[BlastBoot] Blastproof v0.1\n"); + Print(L"[BlastBoot] Firmware Vendor: %s\n",gST->FirmwareVendor); + Print(L"[BlastBoot] Firmware Revision: %u (0x%08x)\n",gST->FirmwareRevision,gST->FirmwareRevision); + Print(L"[BlastCPU] --- CPU Info ---\n"); + bp_cpu_CPU_INFO booting_cpu_info; + bp_cpu_fill_cpu_info(&booting_cpu_info); + Print(L"[BlastCPU] CPU Vendor ID: %a\n",booting_cpu_info.vendor); + Print(L"[BlastCPU] Max basic CPUID level: 0x%x\n",booting_cpu_info.max_cpuid_level); + Print(L"[BlastCPU] CPU Family: %u\n[BlastCPU] CPU Model: %u\n[BlastCPU] CPU Stepping: %u\n",booting_cpu_info.display_family,booting_cpu_info.display_model,booting_cpu_info.stepping); + Print(L"[BlastCPU] CPU Feature from Ecx: %s\n",booting_cpu_info.ecx_bin); + Print(L"[BlastCPU] CPU Feature from Edx: %s\n",booting_cpu_info.edx_bin); + EFI_STATUS status; + bp_console_PASSWORD *password; + status=bp_console_read_password(L"[BlastConsole] Please enter boot password: ",&password,L'*'); + if (EFI_ERROR(status)) { + Print(L"[BlastConsole] Error: an error happened during password typing.\n"); + while (TRUE) {}; + } + bp_crypto_HASH_DIGEST *seed; + bp_crypto_ARGON2_CONTEXT context; + context.hash_size=96; + context.salt=bp_key_pwdsalt; + context.salt_size=sizeof(bp_key_pwdsalt); + context.memory_cost=262144; + context.time_cost=3; + context.password=*password; + status=bp_crypto_argon2id_raw(&context,&seed); + if (EFI_ERROR(status)) { + Print(L"[BlastCrypto] Something went wrong when hashing password."); + while (TRUE) {}; + } + bp_crypto_SPHINCSPLUS_KEYPAIR *keypair; + keypair->erase_sk=TRUE; + status=bp_crypto_sphincsplus_keypair_seed(seed->digest,seed->size_bytes,TRUE,FALSE,&keypair); + if (!EFI_ERROR(status)) { + Print(L"[BlastCrypto] Generated keypair successfully.\n"); + } else { + Print(L"[BlastCrypto] An error happened during keypair generation.\n"); + while (TRUE) {}; + } + status=bp_crypto_sphincsplus_verify(bp_key_mainsig,sizeof(bp_key_mainsig),bp_key_pkblob,sizeof(bp_key_pkblob),keypair->pk); + if (!EFI_ERROR(status)) { + Print(L"[BlastCrypto] Verification passed. Password was good.\n"); + } else { + Print(L"[BlastCrypto] Verification failed. Password was wrong.\n"); + Print(L"[BlastCrypto] Please reboot your computer to retry.\n"); + while (TRUE) {}; + } + Print(L"[BlastDisk] Acquiring loaded image.\n"); + EFI_LOADED_IMAGE_PROTOCOL *loaded_image; + status=gBS->HandleProtocol(ImageHandle,&gEfiLoadedImageProtocolGuid,(VOID**)&loaded_image); + if (EFI_ERROR(status)) { + Print(L"[BlastDisk] Error: can't get LoadedImage.\n"); + while (TRUE) {}; + } else { + Print(L"[BlastDisk] LoadedImage acquired.\n"); + } + EFI_BLOCK_IO_PROTOCOL *block_io_loaded_image=NULL; + status=gBS->HandleProtocol(loaded_image->DeviceHandle,&gEfiBlockIoProtocolGuid,(VOID **)&block_io_loaded_image); + if (EFI_ERROR(status)) { + Print(L"[BlastDisk] Error: can't get BlockIo.\n"); + while (TRUE) {}; + } else { + Print(L"[BlastDisk] BlockIo on LoadedImage acquired.\n"); + } + if (!block_io_loaded_image->Media->LogicalPartition) { + Print(L"[BlastDisk] Error: BlockIo on LoadedImage isn't a partition."); + while (TRUE) {}; + } + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs; + EFI_FILE_PROTOCOL *root; + status=gBS->HandleProtocol(loaded_image->DeviceHandle,&gEfiSimpleFileSystemProtocolGuid,(VOID**)&fs); + if (EFI_ERROR(status)) { + Print(L"[BlastDisk] Error: No filesystem on this BlockIo.\n"); + while (TRUE) {}; + } else { + Print(L"[BlastDisk] Filesystem on BlockIo acquired.\n"); + } + status=fs->OpenVolume(fs,&root); + if (EFI_ERROR(status)) { + Print(L"[BlastDisk] Error: can't open root directory.\n"); + while (TRUE) {}; + } else { + Print(L"[BlastDisk] Root directory opened.\n"); + } + CHAR16 file_full_path[250]; + CHAR16 sign_full_path[250]; + UINT8 pk[CRYPTO_PUBLICKEYBYTES]; + for (INTN i=0;icount); + } else { + Print(L"[BlastConf] Error: can't parse configuration.\n",line_number); + while (TRUE) {}; + } + bp_conf_BOOT_CONF *boot_conf=NULL; + status=bp_conf_fill_default_boot_conf(&boot_conf); + if (EFI_ERROR(status)) { + Print(L"[BlastConf] Error: can't set default boot configuration.\n",line_number); + while (TRUE) {}; + } else { + Print(L"[BlastConf] Default boot configuration applied. Overriding with given configuration:\n"); + } + status=bp_conf_fill_boot_conf(conf,&boot_conf); + if (status==EFI_INVALID_PARAMETER) { + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + Print(L"[BlastConf] Error: can't get boot configuration.\n",line_number); + while (TRUE) {}; + } else { + Print(L"[BlastConf] Boot configuration obtained.\n"); + } + allow_debugging=boot_conf->serial_port_enabled && boot_conf->serial_port_debuging; + allow_erroring=boot_conf->serial_port_enabled && boot_conf->serial_port_erroring; + EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; + status=gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid,NULL,(void **)&gop); + if (EFI_ERROR(status)) { + Print(L"[BlastBoot] Error: can't locate Graphics Output Protocol.\n"); + while (TRUE) {}; + } + UINT32 mode_index; + if (boot_conf->default_horizontal_resolution==0 && boot_conf->default_vertical_resolution==0) { + status=bp_graphic_get_highest_resolution_index(gop,&mode_index); + if (EFI_ERROR(status)) { + Print(L"[BlastGraphic] Error: can't identify biggest resolution mode.\n"); + while (TRUE) {}; + } else { + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info_mode; + UINTN info_size; + status=gop->QueryMode(gop,mode_index,&info_size,&info_mode); + if (EFI_ERROR(status)) { + Print(L"[BlastGraphic] Error: can't get information about biggest resolution mode.\n"); + while (TRUE) {}; + } + Print(L"[BlastGraphic] Using mode %d with %dx%d pixels (biggest mode selected by configuration choice).\n",mode_index,info_mode->HorizontalResolution,info_mode->VerticalResolution); + } + } else { + status=bp_graphic_get_index_by_resolution(gop,(*boot_conf).default_horizontal_resolution,(*boot_conf).default_vertical_resolution,&mode_index); + if (status==EFI_SUCCESS) { + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info_mode; + UINTN info_size; + status=gop->QueryMode(gop,mode_index,&info_size,&info_mode); + if (EFI_ERROR(status)) { + Print(L"[BlastGraphic] Error: can't get information about requested resolution mode.\n"); + while (TRUE) {}; + } + Print(L"[BlastGraphic] Using mode %d with %dx%d pixels (mode selected by using resolution from config).\n",mode_index,info_mode->HorizontalResolution,info_mode->VerticalResolution); + } else if (status==EFI_NOT_FOUND) { + Print(L"[BlastGraphic] Warning: requested graphic mode (%dx%d) provided by config isn't available on this platform. Falling back on biggest resolution available.\n",(*boot_conf).default_horizontal_resolution,(*boot_conf).default_vertical_resolution); + status=bp_graphic_get_highest_resolution_index(gop,&mode_index); + if (EFI_ERROR(status)) { + Print(L"[BlastGraphic] Error: can't identify biggest resolution mode.\n"); + while (TRUE) {}; + } else { + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info_mode; + UINTN info_size; + status=gop->QueryMode(gop,mode_index,&info_size,&info_mode); + if (EFI_ERROR(status)) { + Print(L"[BlastGraphic] Error: can't get information about biggest resolution mode.\n"); + while (TRUE) {}; + } + Print(L"[BlastGraphic] Using mode %d with %dx%d pixels (biggest mode selected because resolution provided by configuration isn't available).\n",mode_index,info_mode->HorizontalResolution,info_mode->VerticalResolution); + } + } else { + Print(L"[BlastGraphic] Warning: requested graphic mode (%dx%d) provided by config couldn't be obtained. Trying to fall back on biggest resolution available.\n",(*boot_conf).default_horizontal_resolution,(*boot_conf).default_vertical_resolution); + status=bp_graphic_get_highest_resolution_index(gop,&mode_index); + if (EFI_ERROR(status)) { + Print(L"[BlastGraphic] Error: can't identify biggest resolution mode.\n"); + while (TRUE) {}; + } else { + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info_mode; + UINTN info_size; + status=gop->QueryMode(gop,mode_index,&info_size,&info_mode); + if (EFI_ERROR(status)) { + Print(L"[BlastGraphic] Error: can't get information about biggest resolution mode.\n"); + while (TRUE) {}; + } + Print(L"[BlastGraphic] Using mode %d with %dx%d pixels (biggest mode selected because resolution provided by configuration couldn't be obtained).\n",mode_index,info_mode->HorizontalResolution,info_mode->VerticalResolution); + } + } + } + EFI_FILE_PROTOCOL *fbm_font; + CHAR16 temp_buffer[256]; + Print(L"[BlastFont] Attempting to use font %s as font.\n",boot_conf->font); + UnicodeSPrint(temp_buffer,256,L"\\EFI\\BlastProofFiles\\%s",boot_conf->font); + status=bp_disk_open_file(root,temp_buffer,&fbm_font); + if (EFI_ERROR(status)) { + Print(L"[BlastFont] Error: can't open font.\n"); + while (TRUE) {}; + } else { + Print(L"[BlastFont] Font opened.\n"); + } + bp_disk_FILE *fbm_font_content=NULL; + status=bp_disk_read_file(fbm_font,&fbm_font_content); + if (EFI_ERROR(status)) { + Print(L"[BlastFont] Error: can't read font.\n"); + while (TRUE) {}; + } else { + Print(L"[BlastFont] Readed font.\n"); + } + bp_font_FBM_HEADER *font_header; + status=bp_font_get_header(fbm_font_content,&font_header); + if (EFI_ERROR(font_header)) { + Print(L"[BlastFont] Error: couldn't read font header.\n"); + while (TRUE) {}; + } else { + Print(L"[BlastFont] Font header readed.\n"); + } + if (font_header->encoding!=0x01) { + Print(L"[BlastFont] Error: font isn't encoded in ASCII.\n"); + while (TRUE) {}; + } else { + Print(L"[BlastFont] Font is %d by %d pixels, encoded in ASCII and contain %d glyphs.\n",font_header->width,font_header->height,font_header->entries_count); + } + bp_font_FBM_FONT *loaded_font; + status=bp_font_load_font(fbm_font_content,&loaded_font); + if (EFI_ERROR(status)) { + Print(L"[BlastFont] Error: can't load font.\n",font_header->width,font_header->width,font_header->entries_count); + while (TRUE) {}; + } else { + Print(L"[BlastFont] Successfully loaded font.\n",font_header->width,font_header->width,font_header->entries_count); + } + status=bp_font_check_font(loaded_font,font_header); + if (status==EFI_VOLUME_CORRUPTED) { + Print(L"[BlastFont] Error: font is corrupted.\n",font_header->width,font_header->width,font_header->entries_count); + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + Print(L"[BlastFont] Error: couldn't check font.\n",font_header->width,font_header->width,font_header->entries_count); + while (TRUE) {}; + } else { + Print(L"[BlastFont] Successfully checked font.\n",font_header->width,font_header->width,font_header->entries_count); + } + bp_font_FBM_FONT_BUFFER *font_buffer; + status=bp_font_parse_font(loaded_font,font_header,&font_buffer); + if (EFI_ERROR(status)) { + Print(L"[BlastFont] Error: can't parse font.\n",font_header->width,font_header->width,font_header->entries_count); + while (TRUE) {}; + } else { + Print(L"[BlastFont] Successfully parsed font.\n",font_header->width,font_header->width,font_header->entries_count); + } + bp_font_FBM_FONT_RENDER *font_render; + bp_font_FBM_RENDERER_SETTING settings; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info_mode; + UINTN info_size; + status=gop->QueryMode(gop,mode_index,&info_size,&info_mode); + if (EFI_ERROR(status)) { + Print(L"[BlastFont] Error: can't get information about selected graphic mode for rendering font.\n"); + while (TRUE) {}; + } + if (info_mode->PixelFormat==PixelRedGreenBlueReserved8BitPerColor) { + settings.red_bits=0x00FF0000; + settings.green_bits=0x0000FF00; + settings.blue_bits=0x000000FF; + Print(L"[BlastFont] Using PixelRedGreenBlueReserved8BitPerColor pixel format.\n"); + } else if (info_mode->PixelFormat==PixelBlueGreenRedReserved8BitPerColor) { + settings.blue_bits=0x000000FF; + settings.green_bits=0x0000FF00; + settings.red_bits=0x00FF0000; + Print(L"[BlastFont] Using PixelBlueGreenRedReserved8BitPerColor pixel format.\n"); + } else if (info_mode->PixelFormat==PixelBitMask) { + settings.red_bits=info_mode->PixelInformation.RedMask; + settings.green_bits=info_mode->PixelInformation.GreenMask; + settings.blue_bits=info_mode->PixelInformation.BlueMask; + Print(L"[BlastFont] Using custom pixel format.\n"); + } else { + Print(L"[BlastFont] Error: GoP mode without a physical framebuffer isn't supported for the moment.\n"); + while (TRUE) {}; + } + bp_graphic_COLOR_RGB background_color; + background_color.r=0x00; + background_color.g=0x00; + background_color.b=0x00; + bp_graphic_COLOR_RGB text_color; + text_color.r=0xFF; + text_color.g=0xFF; + text_color.b=0xFF; + settings.background_color=background_color; + settings.text_color=text_color; + status=bp_font_render_font(font_buffer,font_header,settings,&font_render); + if (EFI_ERROR(status)) { + Print(L"[BlastFont] Error: rendering font failed.\n"); + while (TRUE) {}; + } else { + Print(L"[BlastFont] Successfully rendered font.\n"); + } + Print(L"[BlastGraphic] Switching to graphical user interface.\n"); + status=gop->SetMode(gop,mode_index); + if (EFI_ERROR(status)) { + Print(L"[BlastGraphic] Error: an error happened during graphic mode change.\n"); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug] If you can see this message, that mean that you're reading on the serial port or that something has gone horribly wrong in the switch to graphical user interface.",allow_debugging); + EFI_PHYSICAL_ADDRESS framebuf_base=gop->Mode->FrameBufferBase; + UINT32 *fb=(UINT32 *)(UINTN)framebuf_base; + EFI_FILE_PROTOCOL *bootanim_file; + status=bp_disk_open_file(root,L"\\EFI\\BlastProofFiles\\bootanim.bin",&bootanim_file); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastUi] Error: can't open boot animation.",allow_erroring); + while (TRUE) {}; + } else { + bp_debug_write_line(serial,"[BlastDebug:BlastUi] Opened boot animation.",allow_debugging); + } + bp_disk_FILE *bootanim_content=NULL; + status=bp_disk_read_file(bootanim_file,&bootanim_content); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastUi] Error: can't read boot animation.",allow_erroring); + while (TRUE) {}; + } else { + bp_debug_write_line(serial,"[BlastDebug:BlastUi] Readed boot animation.",allow_debugging); + } + CHAR8 temp[256]; + if (!boot_conf->disable_boot_animation) { + UINT64 frames_count=0; + CopyMem(&frames_count,bootanim_content->content+24,8); + for (UINT64 i=0;iHorizontalResolution,info_mode->VerticalResolution,i,&boot_animation); + if (EFI_ERROR(status)) { + AsciiSPrint(temp,256,"[BlastDebug:BlastUi] Error: a frame from the boot animation didn't render properly: frame number %d",i+1); + bp_debug_write_line(serial,temp,allow_erroring); + while (TRUE) {}; + } else { + CopyMem((VOID *)gop->Mode->FrameBufferBase,boot_animation->frames[0].pixels,sizeof(UINT32)*info_mode->HorizontalResolution*info_mode->VerticalResolution); + gBS->Stall(16666); + CopyMem((VOID *)gop->Mode->FrameBufferBase,boot_animation->frames[1].pixels,sizeof(UINT32)*info_mode->HorizontalResolution*info_mode->VerticalResolution); + gBS->Stall(16666); + CopyMem((VOID *)gop->Mode->FrameBufferBase,boot_animation->frames[2].pixels,sizeof(UINT32)*info_mode->HorizontalResolution*info_mode->VerticalResolution); + gBS->Stall(16666); + CopyMem((VOID *)gop->Mode->FrameBufferBase,boot_animation->frames[3].pixels,sizeof(UINT32)*info_mode->HorizontalResolution*info_mode->VerticalResolution); + gBS->Stall(16666); + FreePool(boot_animation->frames[0].pixels); + FreePool(boot_animation->frames[1].pixels); + FreePool(boot_animation->frames[2].pixels); + FreePool(boot_animation->frames[3].pixels); + FreePool(boot_animation->frames); + FreePool(boot_animation); + } + } + } + bp_ui_BOOT_ANIMATION_FRAME *final_frame=NULL; + status=bp_ui_render_boot_animation_last_frame(bootanim_content,info_mode->HorizontalResolution,info_mode->VerticalResolution,&final_frame); + if (EFI_ERROR(status)) { + AsciiSPrint(temp,256,"[BlastDebug:BlastUi] Error: can't render last frame."); + bp_debug_write_line(serial,temp,allow_erroring); + while (TRUE) {}; + } + CopyMem((VOID *)gop->Mode->FrameBufferBase,final_frame->pixels,sizeof(UINT32)*info_mode->HorizontalResolution*info_mode->VerticalResolution); + AsciiSPrint(temp,256,"[BlastDebug:BlastUi] Successfully played boot animation."); + bp_debug_write_line(serial,temp,allow_debugging); + bp_ui_BOOT_ANIMATION_FRAME *error_frame=NULL; + status=bp_ui_render_boot_animation_last_frame_color(bootanim_content,info_mode->HorizontalResolution,info_mode->VerticalResolution,settings.red_bits,&error_frame); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastUi] Error: can't render error frame.",allow_erroring); + while (TRUE) {}; + } + bp_ui_TEXT_BOX *text_box; + status=bp_ui_draw_text_ascii(fb,font_render,-1,(UINTN)info_mode->VerticalResolution*0.9,info_mode->HorizontalResolution,info_mode->VerticalResolution,"Locating Filesystems...",&text_box); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastUi] Error: placing text on framebuffer doesn't work.",allow_erroring); + while (TRUE) {}; + } + UINTN num_handle; + EFI_HANDLE *handles=NULL; + status=gBS->LocateHandleBuffer(ByProtocol,&gEfiBlockIoProtocolGuid,NULL,&num_handle,&handles); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: can't obtain all handles for BlockIO protocol.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_NO_BLOCKIO_PROTOCOL\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + AsciiSPrint(temp,sizeof(temp),"[BlastDebug:BlastDisk] Managed to obtain %d handles.",num_handle); + bp_debug_write_line(serial,temp,allow_debugging); + BOOLEAN found_initfs=FALSE; + BOOLEAN found_signsyst=FALSE; + UINTN initfs_index; + UINTN signsyst_index; + for (UINTN i=0;iHandleProtocol(handles[i],&gEfiBlockIoProtocolGuid,(VOID **)&blockio); + if (EFI_ERROR(status)) continue; + if (!blockio->Media->LogicalPartition) continue; + EFI_PARTITION_INFO_PROTOCOL *part_info; + status=gBS->HandleProtocol(handles[i],&gEfiPartitionInfoProtocolGuid,(VOID **)&part_info); + if (EFI_ERROR(status)) continue; + EFI_GUID efi_type_guid=part_info->Info.Gpt.PartitionTypeGUID; + EFI_GUID efi_unique_guid=part_info->Info.Gpt.UniquePartitionGUID; + UINT8 type_guid[16]; + UINT8 unique_guid[16]; + type_guid[0]=(efi_type_guid.Data1>>24)&0xFF; + type_guid[1]=(efi_type_guid.Data1>>16)&0xFF; + type_guid[2]=(efi_type_guid.Data1>>8)&0xFF; + type_guid[3]=efi_type_guid.Data1&0xFF; + type_guid[4]=(efi_type_guid.Data2>>8)&0xFF; + type_guid[5]=efi_type_guid.Data2&0xFF; + type_guid[6]=(efi_type_guid.Data3>>8)&0xFF; + type_guid[7]=efi_type_guid.Data3&0xFF; + CopyMem(type_guid+8,efi_type_guid.Data4,8); + unique_guid[0]=(efi_unique_guid.Data1>>24)&0xFF; + unique_guid[1]=(efi_unique_guid.Data1>>16)&0xFF; + unique_guid[2]=(efi_unique_guid.Data1>>8)&0xFF; + unique_guid[3]=efi_unique_guid.Data1&0xFF; + unique_guid[4]=(efi_unique_guid.Data2>>8)&0xFF; + unique_guid[5]=efi_unique_guid.Data2&0xFF; + unique_guid[6]=(efi_unique_guid.Data3>>8)&0xFF; + unique_guid[7]=efi_unique_guid.Data3&0xFF; + CopyMem(unique_guid+8,efi_unique_guid.Data4,8); + if (CompareMem(type_guid,boot_conf->initfs_partition_type_guid,16)==0 && CompareMem(unique_guid,boot_conf->initfs_partition_guid,16)==0 && StrCmp(part_info->Info.Gpt.PartitionName,L"InitFS")==0) { + if (part_info->Info.Gpt.EndingLBA-part_info->Info.Gpt.StartingLBA<=0) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: found too small InitFS.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_TOO_SMALL\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + if (handles) FreePool(handles); + while (TRUE) {}; + } + found_initfs=TRUE; + initfs_index=i; + } else if (CompareMem(type_guid,boot_conf->signsyst_partition_type_guid,16)==0 && CompareMem(unique_guid,boot_conf->signsyst_partition_guid,16)==0 && StrCmp(part_info->Info.Gpt.PartitionName,L"SignSyst")==0) { + if (part_info->Info.Gpt.EndingLBA-part_info->Info.Gpt.StartingLBA<=0) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: found too small SignSyst.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_TOO_SMALL\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + if (handles) FreePool(handles); + while (TRUE) {}; + } + found_signsyst=TRUE; + signsyst_index=i; + } + } + if (!found_initfs && !found_signsyst) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: couldn't found InitFS and SignSyst.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_VYSTEM_FS_NOT_FOUND\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + if (handles) FreePool(handles); + while (TRUE) {}; + } else if (!found_initfs) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: couldn't found InitFS.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_NOT_FOUND\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + if (handles) FreePool(handles); + while (TRUE) {}; + } else if (!found_signsyst) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: couldn't found SignSyst.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_NOT_FOUND\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + if (handles) FreePool(handles); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Found InitFS and SignSyst.",allow_debugging); + bp_ui_erase_text(fb,info_mode->HorizontalResolution,info_mode->VerticalResolution,text_box); + status=bp_ui_draw_text_ascii(fb,font_render,-1,(UINTN)info_mode->VerticalResolution*0.9,info_mode->HorizontalResolution,info_mode->VerticalResolution,"Loading Filesystems Headers...",&text_box); + EFI_BLOCK_IO_PROTOCOL *initfs_blockio; + status=gBS->HandleProtocol(handles[initfs_index],&gEfiBlockIoProtocolGuid,(VOID **)&initfs_blockio); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: can't get InitFS BlockIO protocol.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_NO_BLOCK_IO\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + if (handles) FreePool(handles); + while (TRUE) {}; + } + EFI_BLOCK_IO_PROTOCOL *signsyst_blockio; + status=gBS->HandleProtocol(handles[signsyst_index],&gEfiBlockIoProtocolGuid,(VOID **)&signsyst_blockio); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: can't get SignSyst BlockIO protocol.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_NO_BLOCK_IO\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + if (handles) FreePool(handles); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Obtained both BlockIO protocol.",allow_debugging); + UINTN initfs_block_size=initfs_blockio->Media->BlockSize; + UINT8 *initfs_first_lba=AllocatePool(initfs_block_size); + if (!initfs_first_lba) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: allocating memory for InitFS first LBA failed.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_FIRST_LBA_NO_MEMORY\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + if (!initfs_blockio->Media->MediaPresent) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: InitFS mysteriously disappeared.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_NOT_HERE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(initfs_first_lba); + while (TRUE) {}; + } + status=initfs_blockio->ReadBlocks(initfs_blockio,initfs_blockio->Media->MediaId,0,initfs_block_size,initfs_first_lba); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: can't read InitFS first LBA.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_FIRST_LBA_NO_READ\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(initfs_first_lba); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Readed first LBA for InitFS.",allow_debugging); + UINTN signsyst_block_size=signsyst_blockio->Media->BlockSize; + UINT8 *signsyst_first_lba=AllocatePool(signsyst_block_size); + if (!signsyst_first_lba) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: allocating memory for SignSyst first LBA failed.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_FIRST_LBA_NO_MEMORY\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + if (!signsyst_blockio->Media->MediaPresent) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: SignSyst mysteriously disappeared.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_NOT_HERE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(signsyst_first_lba); + while (TRUE) {}; + } + status=signsyst_blockio->ReadBlocks(signsyst_blockio,signsyst_blockio->Media->MediaId,0,signsyst_block_size,signsyst_first_lba); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Error: can't read SignSyst first LBA.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_FIRST_LBA_NO_READ\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(signsyst_first_lba); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastDisk] Readed first LBA for SignSyst.",allow_debugging); + bp_ui_erase_text(fb,info_mode->HorizontalResolution,info_mode->VerticalResolution,text_box); + status=bp_ui_draw_text_ascii(fb,font_render,-1,(UINTN)info_mode->VerticalResolution*0.9,info_mode->HorizontalResolution,info_mode->VerticalResolution,"Parsing Filesystems Headers...",&text_box); + bp_initfs_INITFS_HEADER *initfs_header=NULL; + status=bp_initfs_parse_initfs_header(initfs_first_lba,initfs_block_size,&initfs_header); + if (status==EFI_UNSUPPORTED) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: provided InitFS isn't compatible with InitFS driver.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_WRONG_VERSION\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(initfs_first_lba); + while (TRUE) {}; + } else if (status==EFI_COMPROMISED_DATA) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: provided InitFS doesn't have the InitFS signature.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_WRONG_SIGNATURE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(initfs_first_lba); + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: can't read InitFS header.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_HEADER_READ_ERROR\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(initfs_first_lba); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Parsed InitFS header.",allow_debugging); + AsciiSPrint(temp,sizeof(temp),"[BlastDebug:BlastInitFS] Bootloader version: 0x%04x; InitFS version: 0x%04x; OS version: 0x%08x",initfs_header->bootloader_version,initfs_header->bootloader_version,initfs_header->os_version); + bp_debug_write_line(serial,temp,allow_debugging); + AsciiSPrint(temp,sizeof(temp),"[BlastDebug:BlastInitFS] InitFS Size: %d bytes; Table size: %d bytes; Files area size: %d bytes",initfs_header->initfs_size,initfs_header->table_size,initfs_header->files_area_size); + bp_debug_write_line(serial,temp,allow_debugging); + AsciiSPrint(temp,sizeof(temp),"[BlastDebug:BlastInitFS] Entry width: %d bytes; Entries count: %d; Files area offset: %d bytes",initfs_header->entries_width,initfs_header->entries_count,initfs_header->files_area_offset); + bp_debug_write_line(serial,temp,allow_debugging); + bp_initfs_SIGNSYST_HEADER *signsyst_header=NULL; + status=bp_initfs_parse_signsyst_header(signsyst_first_lba,signsyst_block_size,&signsyst_header); + if (status==EFI_UNSUPPORTED) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: provided SignSyst isn't compatible with InitFS driver.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_WRONG_VERSION\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(signsyst_first_lba); + while (TRUE) {}; + } else if (status==EFI_COMPROMISED_DATA) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: provided SignSyst doesn't have the SignSyst signature.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_WRONG_SIGNATURE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(signsyst_first_lba); + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: can't read SignSyst header.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_HEADER_READ_ERROR\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(signsyst_first_lba); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Parsed SignSyst header.",allow_debugging); + AsciiSPrint(temp,sizeof(temp),"[BlastDebug:BlastInitFS] Bootloader version: 0x%04x; InitFS version: 0x%04x; OS version: 0x%08x",signsyst_header->bootloader_version,signsyst_header->bootloader_version,signsyst_header->os_version); + bp_debug_write_line(serial,temp,allow_debugging); + AsciiSPrint(temp,sizeof(temp),"[BlastDebug:BlastInitFS] Signature size: %d bytes; Signature count: %d",signsyst_header->signature_size,signsyst_header->signature_count); + bp_debug_write_line(serial,temp,allow_debugging); + AsciiSPrint(temp,sizeof(temp),"[BlastDebug:BlastInitFS] SignSyst size: %d bytes; Signatures blocks size: %d",signsyst_header->signsyst_size,signsyst_header->signature_block_size); + bp_debug_write_line(serial,temp,allow_debugging); + bp_ui_erase_text(fb,info_mode->HorizontalResolution,info_mode->VerticalResolution,text_box); + status=bp_ui_draw_text_ascii(fb,font_render,-1,(UINTN)info_mode->VerticalResolution*0.9,info_mode->HorizontalResolution,info_mode->VerticalResolution,"Verifying Filesystems Headers...",&text_box); + status=bp_initfs_check_initfs_header(initfs_header,root); + if (status==EFI_SECURITY_VIOLATION) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: critical security violation in boot process:",allow_erroring); + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] InitFS header isn't valid.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_HEADER_NOT_VALID\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(initfs_first_lba); + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: InitFS header couldn't be verified.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_HEADER_NOT_VERIFIABLE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(initfs_first_lba); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Successfully verified InitFS header.",allow_debugging); + status=bp_initfs_check_signsyst_header(signsyst_header,root); + if (status==EFI_SECURITY_VIOLATION) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: critical security violation in boot process:",allow_erroring); + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] SignSyst header isn't valid.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_HEADER_NOT_VALID\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(signsyst_first_lba); + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: SignSyst header couldn't be verified.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_HEADER_NOT_VERIFIABLE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(signsyst_first_lba); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Successfully verified SignSyst header.",allow_debugging); + status=bp_initfs_cross_check_headers(initfs_header,signsyst_header); + if (status==EFI_SECURITY_VIOLATION) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: critical security violation in boot process:",allow_erroring); + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Provided InitFS and SignSyst doesn't match.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_FS_NO_MATCH\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(initfs_first_lba); + FreePool(signsyst_first_lba); + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: can't verified that provided filesystems match.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_FS_NOT_MATCHABLE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + FreePool(initfs_first_lba); + FreePool(signsyst_first_lba); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Both filesystems match.",allow_debugging); + UINTN initfs_block_count=initfs_blockio->Media->LastBlock+1; + UINTN initfs_size_bytes=initfs_block_count*initfs_block_size; + if (initfs_header->initfs_size>initfs_size_bytes) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: InitFS size provided in header is bigger than partition size.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_SIZE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + UINTN signsyst_block_count=signsyst_blockio->Media->LastBlock+1; + UINTN signsyst_size_bytes=signsyst_block_count*signsyst_block_size; + if (signsyst_header->signsyst_size>signsyst_size_bytes) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: SignSyst size provided in header is bigger than partition size.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_SIZE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_ui_erase_text(fb,info_mode->HorizontalResolution,info_mode->VerticalResolution,text_box); + status=bp_ui_draw_text_ascii(fb,font_render,-1,(UINTN)info_mode->VerticalResolution*0.9,info_mode->HorizontalResolution,info_mode->VerticalResolution,"Loading Filesystems...",&text_box); + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Allocating memory for InitFS...",allow_debugging); + UINTN initfs_page_count=(initfs_size_bytes+EFI_PAGE_SIZE-1)/EFI_PAGE_SIZE; + EFI_PHYSICAL_ADDRESS initfs_ptr; + status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderData,initfs_page_count,&initfs_ptr); + if (status==EFI_OUT_OF_RESOURCES) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: InitFS couldn't be loaded because the system doesn't have enough memory.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_NO_MEMORY\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: InitFS couldn't be loaded because of a page allocation error.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_MEMORY\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Loading InitFS from disk...",allow_debugging); + status=initfs_blockio->ReadBlocks(initfs_blockio,initfs_blockio->Media->MediaId,0,initfs_size_bytes,(VOID *)initfs_ptr); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: InitFS couldn't be loaded.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_LOAD_ERROR\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Successfully loaded InitFS.",allow_debugging); + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Allocating memory for SignSyst...",allow_debugging); + UINTN signsyst_page_count=(signsyst_size_bytes+EFI_PAGE_SIZE-1)/EFI_PAGE_SIZE; + EFI_PHYSICAL_ADDRESS signsyst_ptr; + status=gBS->AllocatePages(AllocateAnyPages,EfiLoaderData,signsyst_page_count,&signsyst_ptr); + if (status==EFI_OUT_OF_RESOURCES) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: SignSyst couldn't be loaded because the system doesn't have enough memory.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_NO_MEMORY\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: SignSyst couldn't be loaded because of a page allocation error.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_MEMORY\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Loading SignSyst from disk...",allow_debugging); + status=initfs_blockio->ReadBlocks(signsyst_blockio,signsyst_blockio->Media->MediaId,0,signsyst_size_bytes,(VOID *)signsyst_ptr); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: SignSyst couldn't be loaded.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_LOAD_ERROR\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Successfully loaded SignSyst.",allow_debugging); + bp_ui_erase_text(fb,info_mode->HorizontalResolution,info_mode->VerticalResolution,text_box); + status=bp_ui_draw_text_ascii(fb,font_render,-1,(UINTN)info_mode->VerticalResolution*0.9,info_mode->HorizontalResolution,info_mode->VerticalResolution,"Parsing Filesystems...",&text_box); + bp_initfs_INITFS *initfs=NULL; + status=bp_initfs_create_initfs_structure(initfs_header,(UINT8 *)initfs_ptr,&initfs); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: can't parse InitFS structure.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_NO_STRUCTURE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Successfully parsed InitFS structure.",allow_debugging); + bp_initfs_SIGNSYST *signsyst=NULL; + status=bp_initfs_create_signsyst_structure(signsyst_header,(UINT8 *)signsyst_ptr,&signsyst); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: can't parse SignSyst structure.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_SIGNSYST_NO_STRUCTURE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_ui_erase_text(fb,info_mode->HorizontalResolution,info_mode->VerticalResolution,text_box); + status=bp_ui_draw_text_ascii(fb,font_render,-1,(UINTN)info_mode->VerticalResolution*0.9,info_mode->HorizontalResolution,info_mode->VerticalResolution,"Verifying InitFS integrity...",&text_box); + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Successfully parsed SignSyst structure.",allow_debugging); + status=bp_initfs_check_initfs_integrity(initfs,signsyst); + if (status==EFI_SECURITY_VIOLATION) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: critical security violation in boot process:",allow_erroring); + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] InitFS integrity is compromised.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_INITFS_COMPROMISED\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Successfully verified InitFS integrity.",allow_debugging); + bp_ui_erase_text(fb,info_mode->HorizontalResolution,info_mode->VerticalResolution,text_box); + status=bp_ui_draw_text_ascii(fb,font_render,-1,(UINTN)info_mode->VerticalResolution*0.9,info_mode->HorizontalResolution,info_mode->VerticalResolution,"Loading kernel...",&text_box); + bp_disk_FILE *kernel_file=NULL; + status=bp_initfs_read_file(initfs,"shelter.vyx",&kernel_file); + if (status==EFI_NOT_FOUND) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: couldn't found kernel.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_KERNEL_NOT_FOUND\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: couldn't read kernel.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_KERNEL_NO_READ\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Successfully readed kernel.",allow_debugging); + bp_disk_FILE *keycard_file=NULL; + status=bp_initfs_read_file(initfs,"keycard.bin",&keycard_file); + if (status==EFI_NOT_FOUND) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: couldn't found keycard.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_KEYCARD_NOT_FOUND\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastInitFS] Error: couldn't read keycard.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_KEYCARD_NO_READ\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Successfully readed keycard.",allow_debugging); + bp_vyx_VYX_HEADER *kernel_header=NULL; + status=bp_vyx_get_header(kernel_file,&kernel_header); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Error: can't obtain kernel header.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_KERNEL_NO_HEADER\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Successfully obtained kernel header.",allow_debugging); + bp_vyx_SHELTER_BOOT_CONFIG *kernel_config=AllocatePool(sizeof(bp_vyx_SHELTER_BOOT_CONFIG)); + status=bp_vyx_create_shelter_boot_conf(boot_conf->kernel_log_level, + boot_conf->kernel_test_benchmark, + boot_conf->kernel_bench_iterations, + boot_conf->kernel_log_disable_serial_port, + boot_conf->kernel_disable_serial_port, + boot_conf->kernel_log_ring_size, + kernel_config); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Error: can't create kernel config.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_KERNEL_NO_CONFIG\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Successfully created kernel config.",allow_debugging); + bp_vyx_LOADER_SETTING *loader_settings=NULL; + status=bp_vyx_setup_physical_pages(kernel_header,kernel_file,keycard_file,kernel_config,&loader_settings); + if (status==EFI_OUT_OF_RESOURCES) { + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Error: can't obtain enough memory to load kernel.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_KERNEL_NOT_ENOUGH_MEMORY\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } else if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Error: can't allocate memory for kernel.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_KERNEL_NO_MEMORY\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Successfully allocated memory for kernel and keycard.",allow_debugging); + EFI_PHYSICAL_ADDRESS page_table_ptr=0; + EFI_VIRTUAL_ADDRESS stack_address=0; + EFI_VIRTUAL_ADDRESS page_table_pool_va=0; + status=bp_vyx_create_page_table(kernel_header,loader_settings,&page_table_ptr,&stack_address,&page_table_pool_va); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Error: couldn't create kernel page table.",allow_erroring); + bp_ui_show_error_screen(serial,"BP_ERROR_KERNEL_NO_PAGE_TABLE\0",fb,font_render,info_mode->HorizontalResolution,info_mode->VerticalResolution,error_frame,allow_erroring); + while (TRUE) {}; + } + kernel_config=(bp_vyx_SHELTER_BOOT_CONFIG*)loader_settings->shelter_boot_config_addr_phys; + kernel_config->page_table_pool_pa=loader_settings->shelter_page_table_addr_phys; + kernel_config->page_table_pool_va=page_table_pool_va; + kernel_config->page_table_allocator_level=bp_vyx_get_pt_pool_allocator(); + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Successfully created kernel and keycard page table.",allow_debugging); + EFI_VIRTUAL_ADDRESS stack_top=(stack_address+65*4096)-8; + UINTN map_key; + UINTN mmap_size=0; + EFI_MEMORY_DESCRIPTOR *mmap=NULL; + UINTN desc_size; + UINT32 desc_version; + status=gBS->GetMemoryMap(&mmap_size,mmap,&map_key,&desc_size,&desc_version); + AsciiSPrint(temp,sizeof(temp),"[BlastDebug:BlastVyx] DescSize=%d, Mmap_size=%d",desc_size,mmap_size); + bp_debug_write_line(serial,temp,allow_debugging); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Received error for first call to GetMemoryMap. Allocating memory for memory map.",allow_debugging); + } + UINTN mmap_size_allocated=mmap_size+desc_size*20; + mmap=AllocatePool(mmap_size_allocated); + status=gBS->GetMemoryMap(&mmap_size_allocated,mmap,&map_key,&desc_size,&desc_version); + if (EFI_ERROR(status)) { + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Error: received error for second call to GetMemoryMap.",allow_debugging); + while (TRUE) {}; + } + UINTN entry_count=mmap_size_allocated/desc_size; + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Obtained EFI memory map.",allow_debugging); + bp_vyx_SHELTER_MEMORY_MAP_HEADER mmap_header={.sig_start={'S','h','e','M','m','a','p','B'},.entry_count=entry_count,.entry_size=sizeof(bp_vyx_SHELTER_MEMORY_MAP_ENTRY),.mmap_syntax_version=1}; + CopyMem((VOID*)(UINTN)loader_settings->shelter_memory_map_addr_phys,&mmap_header,sizeof(bp_vyx_SHELTER_MEMORY_MAP_HEADER)); + for (UINTN i=0;iType,.physical_start=d->PhysicalStart,.pages_count=d->NumberOfPages,.attributes=d->Attribute}; + VOID *target_entry_addr=(VOID*)((UINTN)loader_settings->shelter_memory_map_addr_phys+sizeof(bp_vyx_SHELTER_MEMORY_MAP_HEADER)+i*sizeof(bp_vyx_SHELTER_MEMORY_MAP_ENTRY)); + CopyMem(target_entry_addr,&mmap_entry,sizeof(bp_vyx_SHELTER_MEMORY_MAP_ENTRY)); + } + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Parsed memory map for kernel.",allow_debugging); + bp_debug_write_line(serial,"[BlastDebug:BlastVyx] Jumping to keycard...",allow_debugging); + EFI_STATUS st=gBS->ExitBootServices(ImageHandle,map_key); + if (EFI_ERROR(st)) { + while (TRUE) {}; + } + stack_top&=~0xFULL; + __asm__ __volatile__ ( + "mov %0, %%rax\n" + "mov %1, %%rbx\n" + "mov %2, %%rcx\n" + "jmp *%3\n" + : + : "r"(stack_top), + "r"(page_table_ptr), + "r"(kernel_header->text_offset), + "r"(loader_settings->keycard_addr_phys) + : "rax", "rbx", "rcx" + ); + return EFI_SUCCESS; +} diff --git a/Blastproof/src/main.inf b/Blastproof/src/main.inf new file mode 100644 index 0000000..b61190c --- /dev/null +++ b/Blastproof/src/main.inf @@ -0,0 +1,49 @@ +[Defines] + INF_VERSION = 1.25 + BASE_NAME = BlastMain + FILE_GUID = e7a0b308-aebb-11f0-9ba9-10ffe08423a6 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 0.1 + ENTRY_POINT = UefiMain + DEFINEs = ARGON2_NO_THREADS + +[Sources] + main.c + libs/src/cpu.c + libs/src/disk.c + libs/src/console.c + libs/src/conf.c + libs/src/crypto.c + libs/src/graphic.c + libs/src/font.c + libs/src/debug.c + libs/src/ui.c + libs/src/initfs.c + libs/src/vyx.c + libs/sha3/sha3.c + libs/argon2/argon2.c + libs/argon2/blake2b.c + libs/argon2/core.c + libs/argon2/encoding.c + libs/argon2/opt.c + libs/argon2/thread.c + libs/sphincsplus/address.c + libs/sphincsplus/fors.c + libs/sphincsplus/hash_sha2.c + libs/sphincsplus/merkle.c + libs/sphincsplus/sign.c + libs/sphincsplus/utils.c + libs/sphincsplus/utilsx1.c + libs/sphincsplus/wots.c + libs/sphincsplus/wotsx1.c + libs/sphincsplus/thash_sha2_robust.c + libs/sphincsplus/sha2.c + +[LibraryClasses] + UefiApplicationEntryPoint + UefiLib + UefiBootServicesTableLib + BaseLib + +[Packages] + MdePkg/MdePkg.dec diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..6895f4e --- /dev/null +++ b/build.sh @@ -0,0 +1,353 @@ +# SPDX-License-Identifier: MPL-2.0 +set -e +run() { + local cmd="$1" + local success_msg="$2" + local error_msg="$3" + shift 3 + tmpfile=$(mktemp) + if eval "$cmd" >"$tmpfile" 2>&1; then + echo "${success_msg:-[OK] Command succeeded}" + rm -f "$tmpfile" + return 0 + else + echo -e "${error_msg:-[ERR] Command failed]}" + cat "$tmpfile" + rm -f "$tmpfile" + return 1 + fi +} + +rm -rf initfs_dir +mkdir -p initfs_dir + +echo "[Build] Checking virtual memory layout:" +echo "" +python3 shelter/tools/checker/vmem_layout_check.py shelter/lib/include/memory/vmem_layout.h +echo "" + +echo "[Build] Generating kernel payloads:" +echo -n "[Build] Radix tests..." +run "python shelter/tools/generator/radix_tree_payload_gen.py > shelter/lib/include/kernel/tests/payloads/test_radix_payload.h" " done." "\n[Build] Error:\n" +echo -n "[Build] Pez tests..." +run "python shelter/tools/generator/pez_alloc_free_payload_gen.py > shelter/lib/include/kernel/tests/payloads/test_pez_payload.h" " done." "\n[Build] Error:\n" +echo -n "[Build] Malloc tests..." +run "python shelter/tools/generator/malloc_payload_gen.py > shelter/lib/include/kernel/tests/payloads/test_malloc_payload.h" " done." "\n[Build] Error:\n" +echo "[Build] Successfully generated kernel payloads." + +echo "[Build] Compiling keycard:" +echo -n "[Build] Changing directory..." +run "cd Blastproof/src" " done." "\n[Build] Error:\n" +echo -n "[Build] Cleaning up..." +run "rm -f ./keycard.bin" " done." "\n[Build] Error:\n" +echo -n "[Build] Compiling keycard executable..." +run "nasm -f bin keycard.asm -o keycard.bin" " done." "\n[Build] Error:\n" +echo -n "[Build] Moving keycard to InitFS..." +run "mv keycard.bin ../../initfs_dir/" " done." "\n[Build] Error:\n" +echo -n "[Build] Exiting directory..." +run "cd ../.." " done." "\n[Build] Error:\n" +echo "[Build] Successfully compiled keycard." + +echo "[Build] Compiling vyld and Shelter:" +echo -n "[Build] Changing directory..." +run "cd vyld" " done." "\n[Build] Error:\n" +echo -n "[Build] Cleaning up..." +run "rm -f ./vyld" " done." "\n[Build] Error:\n" +echo -n "[Build] Compiling vyld executable..." +run "g++ vyld.cpp -o vyld" " done." "\n[Build] Error:\n" +echo -n "[Build] Granting permissions to execute vyld..." +run "chmod +x ./vyld" " done." "\n[Build] Error:\n" +echo -n "[Build] Cleaning up..." +run "rm -f *.o *.vyx *.elf *.bin" " done." "\n[Build] Error:\n" +echo -n "[Build] Changing directory..." +run "cd ../shelter" " done." "\n[Build] Error:\n" +echo "[Build] Compiling Shelter:" +echo "" +VYLD_COMPILATION_FLAGS="-fno-stack-protector -O1 -Wall -Wextra -Werror -Wpedantic -Wshadow -Wconversion -Wsign-conversion -Wstrict-prototypes -Wmissing-prototypes -Wundef -fmax-errors=0 -Ilib/include/" \ + ../vyld/vyld main.c \ + lib/src/kernel/log.c \ + lib/src/kernel/conf.c \ + lib/src/kernel/tests/test_utils.c \ + lib/src/kernel/tests/test_slabs.c \ + lib/src/kernel/tests/test_radix.c \ + lib/src/kernel/tests/test_pez.c \ + lib/src/kernel/tests/test_malloc.c \ + lib/src/std/mem.c \ + lib/src/std/malloc.c \ + lib/src/cpu/tsc.c \ + lib/src/cpu/serial.c \ + lib/src/memory/page.c \ + lib/src/memory/ring.c \ + lib/src/memory/pba.c \ + lib/src/memory/heap.c \ + lib/src/memory/slabs/slab_reg_phys.c \ + lib/src/memory/slabs/slab_reg_virt.c \ + lib/src/memory/slabs/slab_radix_node.c \ + lib/src/memory/slabs/slab_generic.c \ + lib/src/memory/pez/pez.c \ + lib/src/memory/pez/pez_debug.c \ + lib/src/memory/pez/radix.c \ + shelter.vyx +echo "" +echo -n "[Build] Copying Shelter executable.." +run "mv shelter.vyx ../initfs_dir/" " done." "\n[Build] Error:\n" +echo -n "[Build] Exiting directory..." +run "cd .." " done." "\n[Build] Error:\n" +echo "[Build] Successfully compiled all files." + +if [ ! -d "edk2" ]; then + echo "[Build] Cloning and setuping edk2:" + echo "[Build] Cloning edk2:" + git clone https://github.com/tianocore/edk2.git + echo -n "[Build] Entering directory..." + run "cd edk2" " done." "\n[Build] Error:\n" + echo "[Build] Cloning submodule:" + git submodule update --init --recursive --progress + echo "[Build] Compiling necessary tools:" + make -C BaseTools/ + tmpfile=$(mktemp) + echo -n "[Build] Setuping environment..." + . ./edksetup.sh >"$tmpfile" 2>&1 + status=$? + if [[ $status -eq 0 ]]; then + echo " done." + else + echo -e "\n[Build] Error:\n$(cat "$tmpfile")" + exit $status + fi + rm -f "$tmpfile" + echo -n "[Build] Building OVMF..." + run "build -p OvmfPkg/OvmfPkgX64.dsc -b RELEASE -a X64 -t GCC" " done." "\n[Build] Error:\n" + echo -n "[Build] Exiting directory..." + run "cd .." " done." "\n[Build] Error:\n" + echo "[Build] Succesfully setuped edk2." +fi + +cd edk2 +tmpfile=$(mktemp) +echo -n "[Build] Setuping environment..." +. ./edksetup.sh >"$tmpfile" 2>&1 +status=$? +if [[ $status -eq 0 ]]; then + echo " done." +else + echo -e "\n[Build] Error:\n$(cat "$tmpfile")" + exit $status +fi +rm -f "$tmpfile" +cd .. + +echo -n "[Build] Copying Blastproof files..." +cp ./Blastproof ./edk2/ -r +echo " done." +echo -n "[Build] Entering directory..." +cd edk2 +echo " done." + +echo "[Build] Building bootanim executable:" +echo -n "[Build] Changing directory..." +run "cd Blastproof/bootanim" " done." "\n[Build] Error:\n" +echo -n "[Build] Cleaning up..." +run "rm bootanim bootanim.bin -f" " done." "\n[Build] Error:\n" +echo -n "[Build] Compiling bootanim..." +run "g++ bootanim.cpp -o bootanim -Ofast -march=native" " done." "\n[Build] Error:\n" +echo -n "[Build] Giving permissions to execute bootanim..." +run "chmod +x bootanim" " done." "\n[Build] Error:\n" +echo "[Build] Bootanim executable built." + +echo "[Build] Building boot animation:" +echo -n "[Build] Generating boot animation... " +run "./bootanim logo.png file 1920 1080" " done." "\n[Build] Error:\n" +echo -n "[Build] Exiting directory... " +run "cd ../.." " done." "\n[Build] Error:\n" +echo "[Build] Boot animation built." + +echo "[Build] Building fontgen executable:" +echo -n "[Build] Changing directory..." +run "cd Blastproof/fontgen" " done." "\n[Build] Error:\n" +echo -n "[Build] Cleaning up..." +run "rm fontgen bitra-ascii-medium.fbm -f" " done." "\n[Build] Error:\n" +echo -n "[Build] Compiling fontgen..." +run "g++ fontgen.cpp -o fontgen" " done." "\n[Build] Error:\n" +echo -n "[Build] Giving permissions to execute fontgen..." +run "chmod +x fontgen" " done." "\n[Build] Error:\n" +echo "[Build] Fontgen executable built." + +echo "[Build] Building Bitra font:" +echo -n "[Build] Running fontgen..." +run "./fontgen \"#FFFFFF\" \"#000000\" ascii ./chars" " done." "\n[Build] Error:\n" +echo -n "[Build] Renaming generated font..." +run "mv font.fbm bitra-ascii-medium.fbm" " done." "\n[Build] Error:\n" +echo -n "[Build] Exiting directory..." +run "cd ../.." " done." "\n[Build] Error:\n" +echo "[Build] Bitra font built." + +echo "[Build] Building initfsgen executable:" +echo -n "[Build] Changing directory..." +run "cd Blastproof/initfsgen" " done." "\n[Build] Error:\n" +echo -n "[Build] Cleaning up..." +run "rm -f initfsgen" " done." "\n[Build] Error:\n" +echo -n "[Build] Compiling initfsgen..." +run "./build.sh" " done." "\n[Build] Error:\n" +echo -n "[Build] Giving permissions to execute initfsgen..." +run "chmod +x ./initfsgen" " done." "\n[Build] Error:\n" +echo -n "[Build] Exiting directory..." +run "cd ../.." " done." "\n[Build] Error:\n" +echo "[Build] Initfsgen executable built." + +echo "[Build] Generating initfs:" +echo -n "[Build] Changing directory..." +run "cd Blastproof/initfsgen" " done." "\n[Build] Error:\n" +echo -n "[Build] Cleaning up..." +run "rm -f initfs.bin initfs-footprint.bin signsyst.bin" " done." "\n[Build] Error:\n" +echo -n "[Build] Generating initfs..." +run "./initfsgen ../../../initfs_dir" " done." "\n[Build] Error:\n" +echo -n "[Build] Exiting directory..." +run "cd ../.." " done." "\n[Build] Error:\n" +echo "[Build] Initfs generated." + +echo "[Build] Setting up GUIDs:" +echo -n "[Build] Removing old configuration..." +run "rm -f Blastproof/config/bp.conf" " done." "\n[Build] Error:\n" +echo -n "[Build] Generating GUIDs..." +INITFS_TYPE_GUID="8362b434-d825-11f0-a68f-10ffe08423a6" +SIGNSYST_TYPE_GUID="da0048b4-d826-11f0-b877-10ffe08423a6" +INITFS_GUID=$(uuidgen) +SIGNSYST_GUID=$(uuidgen) +echo " done." +echo -n "[Build] Copying configuration..." +run "cp Blastproof/config/bp_template.conf Blastproof/config/bp.conf" " done." "\n[Build] Error:\n" +echo -n "[Build] Patching configuration for InitFS configuration..." +run "sed -i -E \"s/=(UUID_GENERATION_NEEDED_INITFS)/=$INITFS_GUID/\" Blastproof/config/bp.conf" " done." "\n[Build] Error:\n" +echo -n "[Build] Patching configuration for SignSyst configuration..." +run "sed -i -E \"s/=(UUID_GENERATION_NEEDED_SIGNSYST)/=$SIGNSYST_GUID/\" Blastproof/config/bp.conf" " done." "\n[Build] Error:\n" +echo "[Build] GUIDs setup completed." + +echo "[Build] Building keygen:" +echo -n "[Build] Changing directory..." +run "cd Blastproof/keygen" " done." "\n[Build] Error:\n" +echo -n "[Build] Cleaning up directories..." +run "rm argon2 -rf" " done." "\n[Build] Error:\n" +echo -n "[Build] Cleaning up files..." +run "rm keygen libargon2.a argon2.h -f" " done." "\n[Build] Error:\n" +echo -n "[Build] Cloning argon2..." +run "wget https://github.com/P-H-C/phc-winner-argon2/archive/refs/heads/master.zip" " done." "\n[Build] Error:\n" +echo -n "[Build] Unziping argon2..." +run "unzip -qo master.zip" " done." "\n[Build] Error:\n" +echo -n "[Build] Renamming argon2 folder..." +run "mv phc-winner-argon2-master argon2" " done." "\n[Build] Error:\n" +echo -n "[Build] Removing downloaded archive..." +run "rm master.zip" " done." "\n[Build] Error:\n" +echo -n "[Build] Changing directory..." +run "cd argon2" " done." "\n[Build] Error:\n" +echo -n "[Build] Compiling argon2..." +run "make" " done." "\n[Build] Error:\n" +echo -n "[Build] Exiting directory..." +run "cd .." " done." "\n[Build] Error:\n" +echo -n "[Build] Moving argon2 library..." +run "mv ./argon2/libargon2.a ./libargon2.a" " done." "\n[Build] Error:\n" +echo -n "[Build] Moving argon2 header..." +run "mv ./argon2/include/argon2.h ./argon2.h" " done." "\n[Build] Error:\n" +echo -n "[Build] Compiling keygen..." +run "./build.sh" " done." "\n[Build] Error:\n" +echo -n "[Build] Giving permissions to execute keygen..." +run "chmod +x keygen" " done." "\n[Build] Error:\n" +echo -n "[Build] Exiting folder..." +run "cd .." " done." "\n[Build] Error:\n" +echo "[Build] Keygen executable built." + +echo "[Build] Generating key for important files:" +echo "[Build] Obtaining permissions:" +sudo echo -n "" +echo -e "[Build] Running keygen:\n" +sudo ./keygen/keygen ./config/bp.conf ./fontgen/bitra-ascii-medium.fbm ./initfsgen/initfs-footprint.bin ./initfsgen/signsyst-hash.bin +echo -n "[Build] Moving generated header file..." +run "sudo mv -f key.h ./src/libs/include/key.h" " done." "\n[Build] Error:\n" +echo "[Build] Keys generated." + +echo "[Build] Building Blastproof:" +echo -n "[Build] Changing directory..." +run "cd .." " done." "\n[Build] Error:\n" +echo -n "[Build] Building Blastproof..." +run "build --platform=Blastproof/blastproof.dsc --module=Blastproof/src/main.inf --arch=X64 --buildtarget=RELEASE --tagname=GCC" " done." "\n[Build] Error:\n" +echo "[Build] Blastproof built." + +echo "[Build] Building disk image:" +echo -n "[Build] Removing old disk image..." +run "rm -rf disk.img" " done." "\n[Build] Error:\n" +echo -n "[Build] Creating a new disk image..." +run "fallocate -l 1024M disk.img" " done." "\n[Build] Error:\n" +echo -n "[Build] Mounting disk image..." +run "LPDEV=$(sudo losetup --find --show disk.img)" " done." "\n[Build] Error:\n" +echo -n "[Build] Creating GPT table..." +run "sudo parted "$LPDEV" --script mklabel gpt" " done." "\n[Build] Error:\n" +echo -n "[Build] Creating EFI partition..." +run "sudo parted "$LPDEV" --script mkpart ESP fat32 1MiB 100Mib" " done." "\n[Build] Error:\n" +echo -n "[Build] Making EFI partition bootable..." +run "sudo parted "$LPDEV" --script set 1 esp on" " done." "\n[Build] Error:\n" +echo -n "[Build] Creating FAT32 filesystem on EFI partition..." +run "sudo mkfs.fat -F32 "${LPDEV}p1"" " done." "\n[Build] Error:\n" +echo -n "[Build] Creating mount point..." +run "sudo mkdir -p ./efi-part" " done." "\n[Build] Error:\n" +echo -n "[Build] Mounting EFI partition..." +run "sudo mount "${LPDEV}p1" ./efi-part" " done." "\n[Build] Error:\n" +echo -n "[Build] Creating folder (1/3)..." +run "sudo mkdir -p ./efi-part/EFI/BOOT" " done." "\n[Build] Error:\n" +echo -n "[Build] Creating folder (2/3)..." +run "sudo mkdir -p ./efi-part/EFI/BlastProofFiles" " done." "\n[Build] Error:\n" +echo -n "[Build] Copying file (1/5)..." +run "sudo cp Blastproof/bootanim/bootanim.bin ./efi-part/EFI/BlastProofFiles/bootanim.bin" " done." "\n[Build] Error:\n" +echo -n "[Build] Copying file (2/5)..." +run "sudo cp Blastproof/config/bp.conf ./efi-part/EFI/BlastProofFiles/bp.conf" " done." "\n[Build] Error:\n" +echo -n "[Build] Copying file (3/5)..." +run "sudo cp Blastproof/fontgen/bitra-ascii-medium.fbm ./efi-part/EFI/BlastProofFiles/bitra-ascii-medium.fbm" " done." "\n[Build] Error:\n" +echo -n "[Build] Copying file (4/5)..." +run "sudo cp Blastproof/initfsgen/initfs-footprint.bin ./efi-part/EFI/BlastProofFiles/initfs-footprint.bin" " done." "\n[Build] Error:\n" +echo -n "[Build] Copying file (5/5)..." +run "sudo cp Blastproof/initfsgen/signsyst-hash.bin ./efi-part/EFI/BlastProofFiles/signsyst-hash.bin" " done." "\n[Build] Error:\n" +echo -n "[Build] Creating folder (3/3)..." +run "sudo mkdir -p ./efi-part/EFI/BlastProofSign" " done." "\n[Build] Error:\n" +echo -n "[Build] Copying all signatures files..." +run "sudo cp Blastproof/sign/* ./efi-part/EFI/BlastProofSign/" " done." "\n[Build] Error:\n" +echo -n "[Build] Copying bootloader..." +run "sudo cp Build/BlastproofEnv/RELEASE_GCC/X64/BlastMain.efi ./efi-part/EFI/BOOT/BOOTX64.EFI" " done." "\n[Build] Error:\n" +echo -n "[Build] Unmounting EFI partition..." +run "sudo umount ./efi-part" " done." "\n[Build] Error:\n" +echo -n "[Build] Creating InitFS partition..." +INITFS_BIN="Blastproof/initfsgen/initfs.bin" +INITFS_SIZE_BYTES=$(stat -c '%s' "$INITFS_BIN") +INITFS_SIZE_ALIGNED=$((INITFS_SIZE_BYTES/512+1)) +SIZE="+${INITFS_SIZE_ALIGNED}" +run "sudo sgdisk \ + --new=2:0:${SIZE} \ + --typecode=2:${INITFS_TYPE_GUID} \ + --partition-guid=2:${INITFS_GUID} \ + --change-name=2:\"InitFS\" \ + \"${LPDEV}\"" " done." "\n[Build] Error:\n" + echo -n "[Build] Creating SignSyst partition..." +SIGNSYST_BIN="Blastproof/initfsgen/signsyst.bin" +SIGNSYST_SIZE_BYTES=$(stat -c '%s' "$SIGNSYST_BIN") +SIGNSYST_SIZE_ALIGNED=$((SIGNSYST_SIZE_BYTES/512+1)) +SIZE="+${SIGNSYST_SIZE_ALIGNED}" +run "sudo sgdisk \ + --new=3:0:${SIZE} \ + --typecode=3:${SIGNSYST_TYPE_GUID} \ + --partition-guid=3:${SIGNSYST_GUID} \ + --change-name=3:\"SignSyst\" \ + \"${LPDEV}\"" " done." "\n[Build] Error:\n" +echo -n "[Build] Syncing partition table..." +run "sudo partprobe \"$LPDEV\"" " done." "\n[Build] Error:\n" +echo -n "[Build] Flashing InitFS..." +run "sudo dd if=Blastproof/initfsgen/initfs.bin of="${LPDEV}p2" bs=4M conv=fsync" " done." "\n[Build] Error:\n" +echo -n "[Build] Flashing SignSyst..." +run "sudo dd if=Blastproof/initfsgen/signsyst.bin of="${LPDEV}p3" bs=4M conv=fsync" " done." "\n[Build] Error:\n" +echo -n "[Build] Unmounting disk image..." +run "sudo losetup -d "$LPDEV"" " done." "\n[Build] Error:\n" +echo "[Build] Launching QEMU with disk image" +qemu-system-x86_64 \ + -drive if=pflash,format=raw,unit=0,readonly=on,file=Build/OvmfX64/RELEASE_GCC/FV/OVMF_CODE.fd \ + -drive file=disk.img,format=raw,if=virtio,index=0 \ + -serial stdio \ + -m 4096M \ + -cpu host \ + -enable-kvm diff --git a/docs/blastproof/bootconfig.md b/docs/blastproof/bootconfig.md new file mode 100644 index 0000000..7375ed5 --- /dev/null +++ b/docs/blastproof/bootconfig.md @@ -0,0 +1,111 @@ +# Blastproof boot config + +## Introduction + +Blastproof store his configuration inside a text file named `bp.conf` and stored inside `\EFI\BlastProofFiles` inside the EFI partition. Due to being protected by SBFIE, the config is immutable after compilation and any change to it will cause the boot to fail, even if provided password was good. A example configuration is provided as `Blastproof/config/bp_template.conf` + +## Syntax and behaviour + +Blastproof configuration format is pretty simple: +- Any line starting by `#` will be considered a comment and ignored +- Any empty line will be ignored +- All the other lines will be considered as a key-value pair, the first `=` will be considered as a separator. +- All spaces from both sides of the separator will be considered a part of the key or value, including starting and trailing spaces +- All lines are separated by `\n` + +Any non-ascii character will cause the boot to fail. All the keys that are listed below are automatically replaced by their default value if non-present. Somes keys are mandatory for the boot to succeed. Any invalid value will cause the boot process to fail. + +## List of all keys + +### Serial port related keys + +**serial_port_enabled:** +- Description: Authorize the output on serial port once the bootloader enable the selected graphic output mode. Override `serial_port_erroring` and `serial_port_debuging` +- Possible value: `true` or `false` +- Default value: `false` + +**serial_port_debuging:** +- Description: Authorize the output on serial port for anything else than errors +- Possible value: `true` or `false` +- Default value: `false` + +**serial_port_erroring:** +- Description: Authorize the output on serial port for anything related to errors +- Possible value: `true` or `false` +- Default value: `false` + +### Graphics related keys + +**font:** +- Description: Give the name of the file containing the font the bootloader will atempt to use. +- Possible value: any value representing a file name stored inside `EFI\BlastProofFiles` +- Default value: `bitra-ascii-medium.fbm` + +**disable_boot_animation:** +- Description: Disable the loading and playing of the boot animation +- Possible value: `true` or `false` +- Default value: `true` + +**default_horizontal_resolution:** +- Description: define the target horizontal resolution for graphic output mode selection. Set this and `default_vertical_resolution` to `0` to select highest resolution available +- Possible value: any null or positive integer +- Default value: `0` + +**default_vertical_resolution:** +- Description: define the target vertical resolution for graphic output mode selection. Set this and `default_horizontal_resolution` to `0` to select highest resolution available +- Possible value: any null or positive integer +- Default value: `0` + +### InitFS and SignSyst related keys + +**initfs_partition_type_guid:** +- Description: define the type GUID of an InitFS partition. Warning: this GUID is common to all installation and shouldn't be modified in any way +- Possible value: any GUID under the form `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` where `x` is any hexadecimal character +- Default value: `8362b434-d825-11f0-a68f-10ffe08423a6` + +**initfs_partition_guid:** +- Description: define the unique GUID of the InitFS partition. Warning: this GUID is changed at every compilation and auto-completed by build script +- Possible value: any GUID under the form `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` where `x` is any hexadecimal character +- Default value: there is no default value for this pair + +**signsyst_partition_type_guid:** +- Description: define the type GUID of an SignSyst partition. Warning: this GUID is common to all installation and shouldn't be modified in any way +- Possible value: any GUID under the form `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` where `x` is any hexadecimal character +- Default value: `da0048b4-d826-11f0-b877-10ffe08423a6` + +**signsyst_partition_guid:** +- Description: define the unique GUID of the SignSyst partition. Warning: this GUID is changed at every compilation and auto-completed by build script +- Possible value: any GUID under the form `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` where `x` is any hexadecimal character +- Default value: there is no default value for this pair + +### Shelter related keys + +**kernel_log_level:** +- Description: define the log level of the kernel +- Possible value: any integer ranging from 0 to 6 +- Default value: `1` + +**kernel_test_benchmark:** +- Description: define if the kernel should test and benchmark his subsystems +- Possible value: `true` or `false` +- Default value: `false` + +**kernel_bench_iterations:** +- Description: define the amount of iterations used for tests and benchmarks +- Possible value: any integer ranging from 0 to 10000 +- Default value: `10000` + +**kernel_log_disable_serial_port:** +- Description: disable the outputting of logs on the serial port +- Possible value: `true` or `false` +- Default value: `true` + +**kernel_disable_serial_port:** +- Description: disable any outputting on the serial port +- Possible value: `true` or `false` +- Default value: `false` + +**kernel_log_ring_size:** +- Description: define the amount of pages used for logging ring buffer. Setting this pair to 0 will disable the logging ring. +- Possible value: any integer ranging from 0 to 65535 +- Default value: `2048` diff --git a/docs/blastproof/bootprocess.md b/docs/blastproof/bootprocess.md new file mode 100644 index 0000000..38e358d --- /dev/null +++ b/docs/blastproof/bootprocess.md @@ -0,0 +1,35 @@ +# Blastproof boot process + +Blastproof boot process follow a very rigid sequences of steps that are detailled below: + +- Printing informations: + - Bootloader version + - Firmware informations + - CPU informations +- Reading boot password from EFI console +- Verifying integrity of integrated keys (see SBFIE) +- Acquiring access to ESP partition + - Acquiring loaded image handle + - Acquiring BlockIO protocol for ESP partition + - Acquiring filesystem protocol (Blastproof doesn't have an integrated FAT32 driver) + - Opening root folder +- Performing cryptographic checks with internal keys on his own files (see SBFIE) +- Readind, parsing and applying boot configuration +- Locating Simple Graphic Protocol (GOP) handle +- Identify a suitable graphic output format, based on configuration, or highest resolution available in case of fallback +- Loading and rendering bitmap font according to selected graphic output format +- Loading, rendering and playing boot animation if configuration allow it +- Loading InitFS and SignSyst + - Listing all availables BlockIO protocol + - Locating handles for InitFS and SignSyst + - Allocating memory for them + - Loading them into RAM + - Parsing their headers + - Performing cryptographic checks to validate their headers and contents +- Loading the kernel + - Loading Shelter and Keycard binary + - Generating Shelter boot configuration + - Allocating pages for kernel sections + - Creating page table + - Parsing EFI Memory Map and transforming it into a understandable format for Vystem + - Jumping to kernel diff --git a/docs/blastproof/bplib.md b/docs/blastproof/bplib.md new file mode 100644 index 0000000..4b14ffc --- /dev/null +++ b/docs/blastproof/bplib.md @@ -0,0 +1,44 @@ +# Blastproof library + +## Introduction + +While Blastproof is based on EDK II C library, it uses a wrapper around what EDK II can provide (mainly BlockIO protocol, filesystem protocol, graphic output protocol and custom Vystem formats support) as well custom-integrated cryptographic libraries. This wrapper is called the Blastproof library. + +## External libraries + +The source code for external libraries are stored in different folders depending on the library. Their source code has been adapted to run in an EDK II environment. Here is a list of all external libraries inside the Blastproof library, that doesn't include libraries used in boot utilities: + +Folder | Original source code | Author | License +--- | --- | --- | --- +Blastproof/src/libs/sha3 | [mjosaarinen/tiny_sha3](https://github.com/mjosaarinen/tiny_sha3) | mjosaarinen | MIT +Blastproof/src/libs/sphincsplus | [sphincs/sphincsplus](https://github.com/sphincs/sphincsplus) | SPHINCS+ team | MIT-0 +Blastproof/src/libs/argon2 | [P-H-C/phc-winner-argon2](https://github.com/P-H-C/phc-winner-argon2) | Argon2 team | CC0-1.0 + +Note: some of the projects cited above let the user choose the license at their convenience. The most permissive option has been selected when applicable + +## EDK II + +The Blastproof bootloader is based on the EDK II framework: + +- Source: https://github.com/tianocore/ +- License: BSD-2-Clause-Patent +- Copyright: + Copyright (c) Intel Corporation and other contributors. + +## Blastproof library components + +Blastproof library is divided into several components, some relying on the external libraries cited above, and all relying on the EDK II framework. Here is the list of all components with their headers: +- `conf.h`: manage configuration loading and parsing +- `console.h`: provide a simple way to enter a password inside the EFI shell +- `cpu.h`: provide a simple way to print CPU ID informations +- `crypto.h`: provide the cryptographic abstraction layer to use algorithms such as Argon2, SHA3 or Sphincs+ +- `debug.h`: provide outputting primitives to the serial port +- `default.h`: provide macros for default configuration values +- `disk.h`: provide functions to read files from ESP partition +- `font.h`: provide functions to load, parse and render FBM fonts +- `graphic.h`: provide basic primitives to interact with GOP framebuffers +- `initfs.h`: InitFS and SignSyst drivers +- `ui.h`: provide primitives to render text and boot animation in the GOP framebuffer +- `vyx.h`: provide a very basic Vyx-based kernel loader + +Additional headers can be generated at compilation time and aren't included in this list, such as `key.h`. diff --git a/docs/blastproof/fbm.md b/docs/blastproof/fbm.md new file mode 100644 index 0000000..76a88ca --- /dev/null +++ b/docs/blastproof/fbm.md @@ -0,0 +1,53 @@ +# Font Bitmap + +## Introduction + +Font Bitmap (FBM) is the custom font format used by the Blastproof bootloader. It has been designed to be simple and support a selected set of features useful and simple to render from a bare metal environnement. This font format is generated by the `fontgen` utility. In this file, we describe how they are structured, not how they are generated, rendered or used. Please take note that this format has been designed for the x86-64 architecture and for little endian usage. More precisely, all multibytes integer are little endian, except codepoint encoding (see below). + +## Header + +Each FBM font start with the following 10 bytes header: +- Signature: 3 bytes that are set to `FBM` in ASCII +- Encoding: 1 byte. Reserved encoding are 0x01 for ASCII, 0x08 for UTF-8 and 0x10 for UTF-16 +- Entries count: 4 bytes, indicate the number of glyph inside the file +- Width: 1 byte, the width in pixel of a glyph +- Height: 1 byte, the heigth in pixel of a glyph + +Please note that the provided font in Vystem are only tested for ASCII encoding. + +## Glyphs table + +Each glyph occupies `4+width*height` bytes. + +### Codepoint + +The 4 first byte are for the codepoint. The codepoint is encoded as detailled below, **no matter the endianness**: +- ASCII: the first byte of the codepoint is the ASCII code, the next 3 are set to 0 +- UTF-8: if `N` is the amount of byte the UTF-8 code take, then the first `N` are to be set to the UTF-8 code bytes, and the next `4-N` bytes are set to 0 +- UTF-16: if 2 bytes are used, the two first bytes of the UTF-16 code are set into the first two bytes of the codepoint. If 4 bytes are used, use all codepoint bytes + +In all cases, the useful bytes are always left-aligned and the unused bytes are set to 0. The codepoint `0xFFFFFFFF` is reserved for the replacement character in case of missing character in the font. + +### Pixel encoding + +Each pixel is encoded with two values in mind: +- a boolean indicating if the pixel is on or off +- a 4 bit value indicating the shading of the pixel, ranging from 0 (background color) to 15 (text full color) + +Each pixel is encoded on one byte with the following scheme: +- Bit 0 (MSB): shading bit 0 (MSB of the shading value) +- Bit 1: second bit of the first byte of the codepoint +- Bit 2: shading bit 1 +- Bit 3: fourth bit of the second byte of the codepoint +- Bit 4: shading bit 2 +- Bit 5: sixth bit of the third byte of the codepoint +- Bit 6: shading bit 3 (LSB of the shading value) +- Bit 7: on/off bit + +Note: above, when we provide a bit index, it starting from left to rigth. It apply to pixel byte, shading value and bit index for corruption detection. + +When rendering the font, the renderer will take two values: background color and text color. It will create a scale ranging from 0 to 15 using linear interpolation and apply the expected color to each pixel using this scale and the shading value. The bit 1, 3 and 5 of each pixel is set to bits found in the codepoint to detect corruption. The pixels are stored in a grid, line by line, from top to bottom. + +## Provided font + +Vystem provide a basic font named `bitra`, with 12 pixels as width and 20 as height, only supporting ASCII characters. This font is stored in the file `bitra-medium-ascii.fbm`. diff --git a/docs/blastproof/fs.md b/docs/blastproof/fs.md new file mode 100644 index 0000000..df694dd --- /dev/null +++ b/docs/blastproof/fs.md @@ -0,0 +1,91 @@ +# Custom filesystems + +## Introduction + +Vystem define two new filesystems: InitFS and SignSyst. The purpose of these filesystems are to store any sensitive systems files. Both of these filesystems are designed to be integrated together. For each InitFS, there is always a SignSyst and vice versa. These filesystems are generated by the `initfsgen` utility. In this file, we describe how they are structured, not how they are generated, verified or used. Please take note that these filesystems has been designed for the x86-64 architecture and for little endian usage. + +## InitFS + +InitFS is new, very simple filesystem. It doesn't support folders and is completely immutable after generation. It has been designed to support strong files integrity check and be loader as one big chunk into memory. + +### Header + +The header of any InitFS filesystem is as follow: +- Signature: 8 bytes that are set to `InitFiSy` in ASCII +- Bootloader version: an uint16_t indicating the bootloader version, currently set to `0x0001` +- InitFS version: an uint16_t indicating the InitFS version, currently set to `0x0001` +- OS version: an uint32_t indicating the OS version, currently set to `0x00000001` +- Installation ID: an array of 48 completely random bytes. His SHA3-512 hash is stored inside the `initfs-footprint.bin` file, protected by SPFIE +- InitFS Size: the amount of bytes in the total filesystem, not forcefully rounded to the disk block size. Stored as an uint64_t +- Files table size: the amount of bytes used for the filesystem files table. Stored as an uint64_t +- Files content size: the amount of bytes for the filesystem files content area. Stored as an uint64_t +- Entry width: the width of a file table entry. In the current version of InitFS, must always be set to 256. Stored as an uint64_t +- Entries count: the count of entries inside files table, stored as an uint64_t +- Files content area offset: the amount to bytes to add to the filesystem base to reach the files content area. Stored as an uint64_t +- Entropy: 8 bytes of random data, stored as an uint64_t +- Entropy check: 8 bytes of a value generated with this formula : `header.entropy_check=(header.entropy*0x9E3779B185EBCA87)^header.entropy`. Stored as an uint64_t +- Files table hash: an array of 64 bytes containing the SHA3-512 hash of the files table +- Files content area hash: an array of 64 bytes containig the SHA3-512 hash of the files content area +- Installation ID double hash: an array of 64 bytes containing the SHA3-512 hash of the content of the `initfs-footprint.bin` file. +- Padding: 128 bytes of padding generated with a specific pattern (see below) +- Header hash: an array of 64 bytes containing the SHA3-512 of the InitFS header, not including the last 64 bytes + +This header is exactly 512 bytes. The mecanism decribed above with the installation ID and the `initfs-footprint.bin` is to ensure that an installation of Blastproof find his associated InitFS. + +### Files table and files entries + +The files table is filled with files entries structured as follow, each being 256 bytes: +- File offset in the files content area, starting at the beginning of the files content area,stored as an uint64_t +- File size, stored in bytes as an uint64_t +- Sphincs+ public key of the file, stored as an array of 64 bytes +- SHA3-512 hash of the file, stored as an array of 64 bytes +- File name, stored as an array of 112 bytes + +Files names can only use ASCII and have to be finished by `\0`, unless the file name is exactly 112 characters. + +### Files content area + +The files content area come just after the files table and contain the files content. + +## SignSyst + +SignSyst is a special filesystem: it doesn't contain files but signatures. Signatures have all the same fixed size and are stored in the same order as the files entries inside the associated InitFS. + +### Header + +The header of any SignSyst is as follow: +- Signature: 8 bytes that are set to `SignSyst` in ASCII +- Bootloader version: an uint16_t indicating the bootloader version, need to be set as same as the associated InitFS +- InitFS version: an uint16_t indicating the InitFS version, need to be set as same as the associated InitFS +- OS version: an uint32_t indicating the OS version, need to be set as same as the associated InitFS +- Installation ID: an array of 48 bytes containing the installation ID, need to be set as same as the associated InitFS +- Signature size: the size of a signature, stored as an uint64_t +- Signature count: the count of signatures, stored as an uint64_t +- SignSyst size: the amount of bytes in the total filesystem, not forcefully rounded to the disk block size. Stored as an uint64_t +- Signature area size: the amount of bytes used to actually store the signatures, stored as an uint64_t +- Signature area hash: an array of 64 bytes containing the SHA3-512 hash of the signatures area +- Padding: 288 bytes of padding generated with the same pattern as InitFS +- Header hash: an array of 64 bytes containing the SHA3-512 of the SignSyst header, not including the last 64 bytes + +The header is exactly 512 bytes. The file `signsyst-hash.bin`, protected by SPFIE, contain the full hash of the SignSyst header. + +### Signatues area + +The signatures area come just after the SignSyst header and contain all Sphincs+ signatures. + +## Padding generation + +InitFS and SignSyst padding use a very specific generation method: +The padding is first initialized with zeroed bytes, after which a marker value `p` is written at positions determined by the sequence defined by (f0,f1)=(0,1) and iteratively updated as (f0←f1,f1←f0+f1). At each step, the byte at index `f0` is set to `p`, resulting in writes at offsets following the Fibonacci sequence until the index exceeds the padding size. +In InitFS, the marquer value `p` is the first byte of the installation ID. +In SignSyst, the marquer value `p` is the last byte of the installation ID. + +## Hash generation + +The files table hash, files content area hash and signatures area hash are altered in a certain way: their 48 last bytes are xored with the installation ID. The header hash for both filesystems are generated at the final end of the generation process. + +## Stored files + +To this day, the InitFS contain the following files +- `shelter.vyx` +- `keycard.bin` diff --git a/docs/blastproof/index.md b/docs/blastproof/index.md new file mode 100644 index 0000000..72207b2 --- /dev/null +++ b/docs/blastproof/index.md @@ -0,0 +1,21 @@ +# Blastproof Docs + +## Introduction + +Blastproof is Vystem's bootloader. It is a UEFI x86-64 only bootloader that rely on EDK II for all hardware interactions. It is able to confirm the integrity of his files, locate the kernel and load it into memory, using the specific boot protocol designed for Shelter. + +## Blastproof architectural overview + +The Blastproof bootloader rely mainly on the EDK II framework for printing to the EFI console, interacting with disks/partitions and FAT32 filesystem, graphic output protocol, and memory allocation. However, it uses Vystem standard formats for fonts, filesystems, security and integrity checks, graphic renderer, and kernel loading. + +## Ressources + +1) [Bootprocess](bootprocess.md) +2) [Boot configuration](bootconfig.md) +3) [Security and integrity model at boot level](security.md) +4) [Secure Boot Files Integrity Enforcement (SBFIE)](sbfie.md) +5) [Custom filesystems](fs.md) +6) [Font Bitmap](fbm.md) +7) [Blastproof library](bplib.md) + +For detailled informations about kernel loading, please see Vyld docs. diff --git a/docs/blastproof/sbfie.md b/docs/blastproof/sbfie.md new file mode 100644 index 0000000..cbd50d7 --- /dev/null +++ b/docs/blastproof/sbfie.md @@ -0,0 +1,53 @@ +# SBFIE + +## Introduction + +Secure Boot Files Integrity Enforcement (SBFIE) is a compoment of Blastproof in charge of verifying the integrity of Blastproof's files. This allow for cryptographic immutability of boot files. + +## Limitations + +SBFIE has been conceived with the following limitations in mind: +- it doesn't check integrity of the entire bootloader itself +- it doesn't replace UEFI Secure Boot but make it impossible to use without customised certificates generated at each compilation (not supported for the moment) +- due to the will of it being compatible with a large variety of devices, it doesn't integrate TPM utilization (but support in an eventual future is planned) +- any user of a non-QWERTY keyboard will have a hard time typing a password in an UEFI console +- it use the EFI console as input (support of self-made input methods and protocol will be implemented in a near future) +- any missing file that is supposed to be protected by SPFIE will cause the boot process to fail + +## How it work + +For details on how to setup SBFIE, see the doc on the `keygen` utility. + +### Initial parameters and variables + +The following explenations assume that the bootloader has been compiled with a specially generated header `key.h`, generated by the `keygen` utility, for protecting `N` number of files. + +The `key.h` header contain the following datas: +- `bp_key_mainsig`: an array of bytes containing the main signature generated with Sphincs+ +- `bp_key_pkblob`: an array of `64*N` bytes containing `N` Sphincs+ public keys, one for each file +- `bp_key_pwdsalt`: an array of 32 bytes containing the salt to use for hashing the boot password +- `bp_key_files`: an array of `N` strings indicating the order in which boot files should be verified for each public key from `sh_key_pkblob` to correspond with each file + +We assume that all files are in the `\EFI\BlastProofFiles` folder with their corresponding names as specified in `bp_key_files` and that their Sphincs+ signatures are stored as `.sig` inside of the `\EFI\BlastProofSign` folder. + +### Detailled steps + +The steps in which SPFIE check the files is as follow: + +1. The user enter the boot password +2. The boot password is hashed using Argon2 using 262144 of memory cost and 3 of time cost. The salt is stored inside `bp_key_pwdsalt`The output is a 96 digest. +3. The digest is used as seed for generating a Sphincs+ keypair, immediately erasing the private key and keeping only the public key. +4. The generated public key is used to verify the signature inside `bp_key_mainsig` with the message being `bp_key_pkblob` +5. If the verification is successfull, the boot process continue. If not, the boot process is halted. +6. Each file indicated inside `bp_key_files` is opened in order, their signature file is also opened and their public key is extracted from `bp_key_pkblob`. Each file is verified. +7. If any verification fail, the boot process is halted. + +**Note:** all the signatures verifications mecanisms use Sphincs+ and Argon2 for password hashing. The boot password hash is never stored on disk. Their sources come directly from their official implementations in C, only adapted to use types and functions provided by EDK II. All non-deterministic/randomness generation happen at compile time. + +## Concerned files + +To this date, the following files generated by build script are protected by SPFIE: +- `bp.conf` +- `bitra-ascii-medium.fbm` +- `initfs-footprint.bin` +- `signsyst-hash.bin` diff --git a/docs/blastproof/security.md b/docs/blastproof/security.md new file mode 100644 index 0000000..6fda35f --- /dev/null +++ b/docs/blastproof/security.md @@ -0,0 +1,22 @@ +# Security and integrity model at boot level + +## Introduction + +The Vystem project has been designed for the ground up for security and integrity, and that start directly at the boot level, by focusing on boot file integrity. + +## Overview + +In order to achieve a strong amount of security without sacrificing compatibility with existing devices, we have chosen to integrate the following elements into the boot sequence: +- Secure Boot Files Integrity Enforcement (SBFIE): protect cores bootloader files assuming bootloader integrity with a user-defined password, without storing it directly but integrating it very deeply in the integrity check chain. See [SPFIE docs](spfie.md) for more informations +- Robust post-quantum cryptography integrated directly into the bootloader: while we rely on EDK II for files and disk IO as well as password input, all the cryptography stack is integrated into the bootloader, not relying on firmware cryptography at any time. We use post-quantum signing algorithm like SPHINCS+ and strong hashing primitives like Argon2 and SHA3, with their sources codes directly taken from their official implementation, only modified to integrate with EDK II types and memory allocation system +- Signing of every system files: every sensitive system file is integrated into the InitFS, signed into a trio of check: each Blastproof binary (and so SPFIE keys), InitFS and SignSyst is uniquely associated (mainly by installation ID), meaning that the whole system can only work if all of them are properly setup on the same machine device. InitFS integrity check are also cryptographically linked to SPFIE verification. + +## Limitations + +The current Vystem security model assume that we can trust the firmware and that the user boot password is secure. We plan to add support for UEFI secure boot by using custom certificates generation in the future. + +## Detailled parameters for cryptographic algorithms + +For SPHINCS+, we use the set of parameters named `sphincs-sha2-256f`. +For SHA3, we use the standard implementation with the 512 bits variant. +For Argon2, we disable parallelism (this would be a pain to setup in a UEFI environnement) and use the following parameters: 96 bytes output (to match SPHINCS+ keypair seed size), 32 bytes salt, memory cost set to 262144 and time cost set to 3. We use the Argon2id variant. diff --git a/docs/boottools/bootanim.md b/docs/boottools/bootanim.md new file mode 100644 index 0000000..eaf0dd2 --- /dev/null +++ b/docs/boottools/bootanim.md @@ -0,0 +1,66 @@ +# Bootanim docs + +## Introduction + +The `bootanim` utility is responsible for generating the boot animation. It is used even if the bootloader configuration specify to not play the boot animation, as it contain the Vystem logo blueprint. + +## Detailled informations + +Folder: `Blastproof/bootanim` +Source code file: `bootanim.cpp` + +For building `bootanim`, use the following command: +``` bash +g++ bootanim.cpp -o bootanim -Ofast -march=native +``` +The `-Ofast -march=native` is recommended for better performances, as the boot animation generation is a bit heavy on computation. + +External library: +- stb_image, made by `Sean Barrett`, sourced from [nothings/stb](https://github.com/nothings/stb), provided in the public domain + +## Usage + +Prerequisites: having `ffmpeg` accessible in path for the use of folder mode, for generating the video. + +The `bootanim` can be used like this: +``` bash +bootanim [file/folder] +``` + +You can use `bootanim` in two differents mode: +- file mode: will export the boot animation as `bootanim.bin`, into a format readable by the bootloader +- folder mode: will export the boot animation as a folder named `frames` that will contain all the generated frames under the name format `frameXXXX.ppm`, as well as a video named `out.mp4` + +Command example: +``` bash +bootanim logo.png file 1280 720 +bootanim logo.png folder 2160 1440 +``` + +## Boot animation generation + +The first thing to know about Blastproof boot animation is that, due to poor FAT32 read performance, we decided to find a compromise between the amount of data being stored into the generated file and the amount of data computed at each boot. In order to maintain a fluid, 60 fps animation at each boot on most hardware, we landed on this compromise: +- We only store 16 frames per second, under the form of points coordinates +- The 48 others frames per second are computed using linear interpolation at each boot +This result in the `bootanim.bin` file being only 14 megabytes in size, instead of 56 megabytes, for the actual Vystem logo and a resolution of 1920x1080. + +The detailled generation procedure is as follow: +1) Locate and open the logo file in monochrom mode (with only one color channel, conversion is made by stb_image). The utility will reject any non-square logo. +2) Center the logo on a black canva the size provided by `width` and `height` arguments. +3) Scan the canva to detect all points that have their value over 127. Save these points with their coordinates. In the Vystem logo, there is over 45k points. +4) For each of these points, generate a random position in the canva, the seed doesn't have any cryptographic implication and is randomised for every new build. +5) Apply two quadratic bezier curves: one for the "explosion" (all points starting from the exact center of the screen and being scattered around the screen to their random position) and one for the logo appearence (each point is assigned a random coordinates in the logo points and will be attrected to this position from their random position). +6) Generate the selected output media and write it into the files. + +It is recommanded that the image file provided is already monochrom. + +## Output file structure + +The outputted file named `bootanim.bin` is the format that is usable by the bootloader. The file header is structured like this (all uint64_t are in little endian): +- Signature: an array of 8 bytes containing `BootAnim` in ASCII +- The width of canva used for generating the boot animation, stored as an uint64_t +- The height of canva used for generating the boot animation, stored as an uint64_t +- Number of frame inside the boot animation, stored as an uint64_t +- Number of points inside each frame, stored as an uint64_t + +Then, each frame is written inside the file. Each frame is made of points. Each points is made of two uint16_t, one for the x position and one for the y position. diff --git a/docs/boottools/fontgen.md b/docs/boottools/fontgen.md new file mode 100644 index 0000000..c29d517 --- /dev/null +++ b/docs/boottools/fontgen.md @@ -0,0 +1,52 @@ +# Fontgen docs + +## Introduction + +The `fontgen` utility is responsible for generating the font used by Blastproof. It can only generate FBM font. + +## Detailled informations + +Folder: `Blastproof/fontgen` +Source code file: `fontgen.cpp` + +For building `fontgen`, use the following command: +``` bash +g++ fontgen.cpp -o fontgen +``` + +External library: +- stb_image, made by `Sean Barrett`, sourced from [nothings/stb](https://github.com/nothings/stb), provided in the public domain + +## Usage + +The `fontgen` utility can be used like this: +``` bash +fontgen [ascii/utf8/utf16] +``` + +The text and background color arguments must be given under a hexadecimal RGB color code, starting with `#`. They serve as boundary for generating the shading scale. +The encoding argument can be `ascii`, `utf8` or `utf16`, however in Vystem build script, only the `ascii` encoding is used. Others supported encoding modes haven't been tested. +The final argument is the path to the folder that contain all images files for the font characters. +Each character is encoded with the following format: +- they must named with this format `0xXXXXXXXX` where `X` is an hexadecimal character. All `X` aren't mandatory, and the utility interprete them as characters codepoint. For example, file name `0x00` will be interpreted as a codepoint of value `0x00000000`, resulting in the `\0` character for ASCII +- each file inside the provided folder must be a PNG file +- each file must only contain pixel with colors inside the generated shading scale. For instance, the `bitra` font only use color from the two extremities of the shading scale. Transparency isn't checked as the shading scale is in RGB and all images files are opened in 3 channels mode. +- all files must have the same height and width, that will indicate the dimensions of the bitmap grid for each pixel + +Any failure to meet the above points will result in font compilation error. + +Command example: +``` bash +fontgen "#FFFFFF" "#000000" ascii ./chars +``` + +## Detailled generation process + +1) Generate the shading scale by associating colors from the linear interpolation of the two provided colors to values ranging from 0 to 15. The text color will be occupy the value 15 and the background color the value 0. +2) For each image file in the provided folders: extract the character codepoint from the file name, endode it to FBM codepoint format, extract the raw pixel data +3) For each valid character: extract the codepoint bits that will be inserted into pixel bytes, iterate for each pixel of the character: identify which value in the shading scale the pixel color is, encode it into a byte with codepoint bits and on/off value (off if the shading value is 0, on otherwise) +4) Write the header and all the encoded characters into the final file that will be named `font.fbm` + +Any failure to meet the above criteria (except some of the second step that will only trigger a warning) will result in font compilation error. + +For a detailled overview of how a `.fbm` file is structured, please see [FBM docs](../blastproof/fbm.md). diff --git a/docs/boottools/index.md b/docs/boottools/index.md new file mode 100644 index 0000000..917d22e --- /dev/null +++ b/docs/boottools/index.md @@ -0,0 +1,11 @@ +# Bootloader tools Docs + +In order to generate all necessary components for the build of a Vystem disk image, we have created several C++ tools designed to help generating these components. All of these tools have been intented to be used only in a normal Linux environnement. Here is the list of bootloader tools: +1) [bootanim](bootanim.md) +2) [fontgen](fontgen.md) +3) [initfsgen](initfsgen.md) +4) [keygen](keygen.md) + +Here a few precisions about the global mecanisms inside each tools: +- when a tool is iterating on the content of a folder, it rely on the order provided by the filesystem. But tool like `keygen` get there files list through arguments specifying direct files path rather than a folder path, making it a deterministic order if the build script doesn't change +- when we say 'securely erased', it mean we are using the `explicit_bzero` function to prevent compiler optimization diff --git a/docs/boottools/initfsgen.md b/docs/boottools/initfsgen.md new file mode 100644 index 0000000..91e6475 --- /dev/null +++ b/docs/boottools/initfsgen.md @@ -0,0 +1,50 @@ +# Initfsgen documentation + +## Introduction + +The `initfsgen` utility is responsible for generating the InitFS and SignSyst filesystems. + +## Detailled informations + +Folder: `Blastproof/initfsgen` +Source code file: `initfsgen.cpp` + +For building `initfsgen`, use the provided build script `build.sh` while being in the utility directory. + +External library: +- SPHINCS+, made by the SPHINCS+ team, sourced from [sphincs/sphincsplus](https://github.com/sphincs/sphincsplus), under the MIT-0 license +- tiny_sha3, made by mjosaarinen, sourced from [mjosaarinen/tiny_sha3](https://github.com/mjosaarinen/tiny_sha3), under the MIT license + +## Usage + +The `initfsgen` utility can be used like this: +``` bash +initfsgen +``` + +The folder must contain only regular files. Any links, block device or folders inside the root folder will result in an error. + +Command example: +``` bash +initfsgen ./initfs_dir +``` + +## Detailled generation process + +1) Iterate over the content of the provided folder, checking if all elements are regular files, collecting their file sizes. +2) Collect 48 bytes of entropy for installation ID and 8 bytes of entropy for entropy check, by opening `/dev/urandom`. No others method of entropy generation are or will be supported. +3) Copying installation ID inside InitFS and SignSyst headers, filling all possible informations about files and signatures quantity and sizes inside the headers. +4) Generating entropy check, putting it into InitFS header. +5) Computing installation ID hash and saving it into `initfs-footprint.bin`, saved into the current directory. +6) Computing hash of the content of `initfs-footprint.bin` and saving it into the InitFS header. +7) Iterate for each file: loading the entire file, generating InitFS file name, copying file data into files content area, computing file content hash, generating SPHINCS+ keypair unique to this file, generating signature for file by using private key, securely erasing private key (without compiler optimization), saving file hash and public key into file entry, saving file entry into files table and adding generated signature to SignSyst +8) Generating files table, files content area and signatures area hash, saving them into headers by xoring them with part of the installation ID +9) Generating and saving padding for InitFS and SignSyst headers. Computing headers hash and finalizing filesystems raw data. +10) Saving InitFS under `initfs.bin`, hashing SignSyst header and saving it into `signsyst-hash.bin`, saving SignSyst under `signsyst.bin` + +For the full and detailled layout of InitFS and SignSyst, please see [customs filesystems docs](../blastproof/fs.md). + +All hashing operations use SHA3-512. All entropy generations use secure entropy from `/dev/urandom`. +This utility generate four files: +- `initfs.bin` and `signsyst.bin` are the filesystems expected to be flashed into their respectives partitions on disk +- `initfs-footprint.bin` and `signsyst-hash.bin` are files expected to be protected by SPFIE and put inside the ESP partition diff --git a/docs/boottools/keygen.md b/docs/boottools/keygen.md new file mode 100644 index 0000000..41727ae --- /dev/null +++ b/docs/boottools/keygen.md @@ -0,0 +1,47 @@ +# Keygen docs + +## Introduction + +The `keygen` utility is responsible for generating SPFIE keys, files and boot password. This file focus on how these ressources are generated and setuped, not how the verification is made at boot time. The explenations in this file are complementary to the informations in [SPFIE docs](../blastproof/spfie.md). + +## Detailled informations + +Folder: `Blastproof/keygen` +Source code file: `keygen.cpp` + +For building `keygen`, first make sure that you have the `libargon2.a` static library file inside the utility directory. This one and the source of Argon2 for the `keygen` isn't included inside the main Vystem repository but downloaded and compiled by the build script. So you may have to compile Argon2 yourself before compiling `keygen`. To compile `keygen`, use the provided build script `build.sh` while being in the utility directory. + +External libraries: +- SPHINCS+, made by the SPHINCS+ team, sourced from [sphincs/sphincsplus](https://github.com/sphincs/sphincsplus), under the MIT-0 license +- tiny_sha3, made by mjosaarinen, sourced from [mjosaarinen/tiny_sha3](https://github.com/mjosaarinen/tiny_sha3), under the MIT license +- Argon2, made by the Argon2 team, sourced from [P-H-C/phc-winner-argon2](https://github.com/P-H-C/phc-winner-argon2), under the CC0-1.0 license + +## Usage + +The `keygen` utility can be used like this: +``` bash +sudo keygen ... +``` + +You can provide as much paths as you want, as long as they all lead to regular files. +It's recommanded the `keygen` utility be launched as root. + +## Detailled generation processus + +1) Firstly, the `keygen` processus disable all possibilty of process memory dumps or memory swapping before doing anything else. Due the very thight security here, `keygen` should be running as root to ensure his own security. +2) Then, it check for the existence of all the provided files and lock private and public keys buffer as well as signature buffer to ensure that all the secrets aren't dumped on disk due to process fault or swapping. +3) Iterating for all the provided files: loading the file into memory, generating SPHINCS+ keypair for it, computing and signing the SHA3-512 hash of the file content if it's above 1 megabyte, or signing directly the file if under 1 megabyte, securely erasing all temporary keys and signature buffer, saving signature and public key into their dedicated buffer. +4) Saving all signatures into a directory named `sign`, created if non existing, under the name format `.sig`, then securely erasing the signatures buffer +5) Letting the user enter a boot password. If password is empty, it will ask for a number multiple of 2 to generate this many hexadecimal characters that will serve as password, the default amount of hexadecimal characters being 16. If the user enter a password, the user will have to confirm it +6) Checking that the password fill all criterias: should be (not enforced) at least 12 characters, can (enforced) only include ASCII characters due to EFI shell limitations, shouldn't be longer that 512 characters (enforced), and shoudn't use the numerical pad at all (not well supported in the EFI shell, but the user can still use numbers) +7) Offering the user the possibility to translate their password into a QWERTY representation of it, if they doesn't use a QWERTY keyboard. That way, the user can enter his password like he would do on his keyboard but the password that will be hashed will be the password translated in QWERTY. + +**Warning:** This feature is still very experimental, the only translation supported is AZERTY to QWERTY and the user experience is still very unsettling. For testing purposes, it's recommanded using a password with no characters differences between keyboard layout such as `test`, for example compatibility between the AZERTY and QWERTY keyboard. A better password inputing experience with support for various keyboard layout is planned for future release. + +8) Converting the final password to UTF16LE for EFI environnement easier compatibilty. Obtaining 32 bytes of entropy from `/dev/urandom`. Hashing the password with the Argon2id algorithm using no parralelism, time cost of 3 and memory cost of 262144, with the salt, resulting in a 96 bytes output. +9) Using the password as seed for generating a master SPHINCS+ keypair. Generating a signature of the public keys of each files concatenated into a single block. Securely erasing both keys of the master keypair. +10) Saving the final datas the bootloader will need into a file named `key.h`, containing the master signature, the block of files public keys, the password salt and the list of all files to verify, in order. + +This utility generate the following files: +- `key.h`: header file containing important datas, made for single use only, should be placed into `Blastproof/src/libs/include` in order for the bootloader to succesfully compile +- all the `.sig` files: binary files containg the signature for each file, should be placed inside the ESP partition. diff --git a/docs/build.md b/docs/build.md new file mode 100644 index 0000000..2a2d47e --- /dev/null +++ b/docs/build.md @@ -0,0 +1,54 @@ +# Build process + +## Introduction + +In this file, we will see how you can build and boot successfully into a VM containing a Vystem disk image. It's recommanded to read the whole documentation for better understanding of why certains parts are necessary. + +## Prerequisites + +You should have cloned the full repository on your device. Please keep in mind that Vystem hasn't been tested on real hardware yet, so the only way to test it is through a virtual machine. + +Vystem has been designed to be build and run from a standard Linux system. Any Linux distro should work. A WSL environnement can also work but the VM might be very slow, because of KVM unavailability. Make sure you have KVM enable and accessible before starting, or else remove the `--enable-kvm` argument at the end of the build script. + +Before starting, please make sure that you have the following binary reachable in your terminal path: +``` +python3 python nasm g++ gcc iasl git make sed wget unzip fallocate parted mkfs.fat sgdisk partprobe losesetup qemu-system-x86_64 +``` + +Finally, please make sure that you have an internet connexion (required for downloading additionnal assets like EDK II and Argon2) as well as root access/sudo permissions, as it is recommanded for running `keygen` and required for creating the disk image. Root permissions is only obtained as soon as it's needed. If you don't want to give root permissions, you can also run the commands inside the build script one by one just to be sure. + +## Build and run + +Once you are ready, run the following command while being in the root folder: +``` bash +./build.sh +``` + +This will download, compile, and assemble every asset together. This can take a while depending on your internet connexion, since EDK II is quite heavy and will be downloaded from it's source repository and locally compiled, with OVMF. For the moment, using packaged versions of EDK II and/or OVMF provided by packages managers isn't supported. + +The VM will automatically be launched with 4 gigabytes of RAM (you can obviously raise or lower that amount depending on your device), KVM enabled and serial port output redirected inside the terminal. + +## Detailled build process + +The folder used for InitFS base is named `initfs_dir` and is placed in the root folder of the repository. +The build process is as follow: +1) Checking virtual memory layout +2) Generating payloads for TAB subsystem +3) Compiling Keycard and putting it into `initfs_dir` +4) Compiling Vyld +5) Compiling Shelter and putting it into `initfs_dir` +6) Cloning EDK II, building required tools and OVMF +7) Compiling `bootanim` and generating boot animation +8) Compiling `fontgen` and generating font +9) Compiling `initfsgen` and generating InitFS and SignSyst +10) Patching bootloader configuration template to insert random partition GUID and fixed partition type GUID +11) Cloning and building Argon2, compiling `keygen` and generating required files for SPFIE +12) Compiling bootloader +13) Generating disk image +14) Launching virtual machine + +## Troubleshooting + +If anything goes wrong, the script will stop and show you the error. Sometimes it's just a dependency that isn't reachable. Carefully analyse the error message and try to install the missing dependencies, also check that the required dependencies listed above are all installed. + +If it's related to EDK II (which occur the majority of the time), it could be that a new update in EDK II has broken something. Don't hesitate to open an issue, and this should be fix fairly quickly. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..4506b69 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,22 @@ +# Vystem Docs + +**Warning:** this documentation only include currently implemented elements and doesn't include plannned future elements, except some confirmed planned updates scattered within the documentation + +## Introduction + +Welcome to Vystem documentation. This documentation is divided into severals parts for easier understanding. + +## Components + +Vystem is an OS made of severals components which can be divided into severals categories: +1) [Blastproof](blastproof/index.md): the bootloader +2) [Bootloader tools](boottools/index.md): all the tools needed to generate components for the boot process +3) [Vyld](vyld/index.md): the VYX executable format linker and VYX executable format specification +4) [Shelter](shelter/index.md): the kernel +5) [Kernel tools](kerneltools/index.md): all the `.py` scripts used for various usage around the kernel + +## Ressources + +- [Build process](build.md) +- [Contributing and licensing](licenses.md) +- [Roadmap](roadmap.md) diff --git a/docs/kerneltools/index.md b/docs/kerneltools/index.md new file mode 100644 index 0000000..1ec6346 --- /dev/null +++ b/docs/kerneltools/index.md @@ -0,0 +1,10 @@ +# Kernel tools + +## Introduction + +For somes usages, like generating payloads datas or checking certains files, a few python scripts are included with the kernel. + +## Summary + +1) [Virtual memory checker](vmemcheck.md) +2) [Payloads generator](payloads.md) diff --git a/docs/kerneltools/payloads.md b/docs/kerneltools/payloads.md new file mode 100644 index 0000000..06a6127 --- /dev/null +++ b/docs/kerneltools/payloads.md @@ -0,0 +1,28 @@ +# Payloads generator + +## Introduction + +Payloads for the TAB subsystem are generated by special python scripts. Each script prints the generated file inside the terminal. For details about how the contents of the payloads are used, see [TAB documentation](../shelter/tab.md) + +## Pez subsystem payload + +File: `shelter/tools/generator/pez_alloc_free_payload_gen.py` + +This script generates two arrays of `sh_uint64`: +- `test_pez_physical_size`: 2000 values that should be interpreted as pages count. Half of them are 1. The other half ranges from 2 to 1000. In the other half, higher values are less likely to appear as their value rises. +- `test_pez_physical_alloc`: 4000 values ranging 0 to 1999, each one appearing twice. + +## Radix tree subsystem + +File: `shelter/tools/generator/radix_tree_payload_gen.py` + +This script generates three arrays of `sh_uint64`, containing 10000 random 8 bytes unsigned integers each: `test_keys`, `test_values` and `test_search`. + +## Malloc subsystem + +File: `shelter/tools/generator/malloc_payload_gen.py` + +This script generate three arrays of `sh_uint64`: +- `test_malloc_small_size`: 10000 values that should be interpreted as size in bytes. They range from 1 to 1024, higher values are less likely to appear as their value rises. +- `test_malloc_big_size`: 1000 values ranging from 1 to 25, higher values are less likely to appear as their value rises. +- `test_malloc_big_alloc`: 2000 values ranging 0 to 999, each one appearing twice. diff --git a/docs/kerneltools/vmemcheck.md b/docs/kerneltools/vmemcheck.md new file mode 100644 index 0000000..2ec9f4c --- /dev/null +++ b/docs/kerneltools/vmemcheck.md @@ -0,0 +1,24 @@ +# Virtual memory checker + +## Introduction + +This file is responsible for checking that there is no overlapp between virtual pages range defined in `shelter/lib/include/memory/vmem_layout.h`. For the full documentation about this file, please see [virtual memory layout documentation](../shelter/memory/vmemlayout.md). + +## Usage + +The script is located inside `shelter/tools/checker/vmem_layout_checker.py`. + +It can be used like that: +``` bash +python shelter/tools/checker/vmem_layout_checker.py +``` + +## Detailled processus + +1) Open provided file, parse it and print the list of valid regions. +2) Generate `cfile.c` to let the compiler do the hard work of parsing macro declaration +3) Compile (the `gcc` compiler must be accessible inside the path) and run the generated C program +4) Recover the output of the program, parse it to obtain start and size of each virtual region +5) Check for overlaps and return an error if something went bad during the processus + +The compiled program and the C file are deleted afterward. diff --git a/docs/licenses.md b/docs/licenses.md new file mode 100644 index 0000000..50ee858 --- /dev/null +++ b/docs/licenses.md @@ -0,0 +1,40 @@ +# Contributing and licensing + +All license files can be found in the `licenses/` directory. + +License texts for Vystem components are derived from [IQAndreas/markdown-licenses](https://github.com/IQAndreas/markdown-licenses), while third-party license texts are extracted from their respective original repositories. + +## Licenses + +All source code and build-related files are MPL-2.0 unless otherwise explicitly stated. + +All `.md` files stored in the root directory and in the `docs` folder are under the MIT license. Code snippets copied from MPL-2.0 licensed source files remain under MPL-2.0. + +## Third party licenses + +The following third party libraries are used in Vystem: + +Original source code | Author | License +--- | --- | --- +[mjosaarinen/tiny_sha3](https://github.com/mjosaarinen/tiny_sha3) | mjosaarinen | MIT +[sphincs/sphincsplus](https://github.com/sphincs/sphincsplus) | SPHINCS+ team | MIT-0 +[P-H-C/phc-winner-argon2](https://github.com/P-H-C/phc-winner-argon2) | Argon2 team | CC0-1.0 +[tianocore/edk2](https://github.com/tianocore/edk2) | Tianocore and all the contributing companies | BSD-2-Clause-Patent +[serge1/ELFIO](https://github.com/serge1/ELFIO) | Serge Lamikhov-Center | MIT + +EDK II copyright: Copyright (c) Intel Corporation and other contributors. + +## Contribution workflow + +Contributors must work through public forks of this repository. + +To propose changes for inclusion in the mainline, contributors should open an issue referencing the fork and the relevant commits. Changes are manually reviewed and selectively integrated by the maintainer. + +## Contribution licensing grant + +By submitting an issue proposing changes from a fork for inclusion in the mainline, the contributor: + +- retains full copyright ownership of their contribution; +- agrees that the contribution remains licensed under MPL-2.0 where applicable; +- grants the mainline maintainer a perpetual, worldwide, irrevocable right to copy, modify, merge, redistribute, sublicense, and relicense the contributed changes as part of as part of the mainline project and any future versions or derivative works. +- agrees to be credited in the relevant source files and/or commit history where reasonably possible. diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 0000000..d644439 --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,13 @@ +# Roadmap + +## Version 0.1 (actual) + +Initial release + +## Version 0.2 (future versions with planned additions) + +- Vyld overhaul, support for multiples compilations modes with JSON config files for project settings +- VYX format overhaul, supporting various sections and more feature +- Blastproof: custom keyboard layout driver +- Shelter: Support for IDT, GDT, TSS, interruptions, interruptions handler, ACPI timers table parsing and ACPI driver +- Shelter: Slab allocators overhaul and performance boosts diff --git a/docs/shelter/bench.md b/docs/shelter/bench.md new file mode 100644 index 0000000..c2a616f --- /dev/null +++ b/docs/shelter/bench.md @@ -0,0 +1,149 @@ +# Benchmark results + +In this file, you will be able to see examples of benchmark results. These are provided as indicative results. Depending on your configuration, you might get way differents results + +## Tests environnement + +- Host OS: EndeavourOS x86_64 +- Host kernel: Linux 6.19.10-arch1-1 +- Host CPU: Ryzen 7 9800x3d @ 5.27 GHz +- Environnement: VM running in qemu-system-x86_64 version 10.2.2 +- VM parameters: KVM enabled, 4096M of RAM, cpu set to host, running in single thread + +The following tests are shown in the order the kernel run them. Please keep in mind that Pez isn't available until the Pez physical plane benchmark. + +## Results for region objects slab allocators + +Physical regions object slab allocator: +``` +Result for benchmark "allocations" : +Min: 141 | Med: 235 | Avg: 2736 | Max: 3194120 | Total : 27362977 (TSC) +[P00-P90] 141-282 : [##################--] 90% +[P90-P99] 282-282 : [#-------------------] 9% +[P99-Max] 282-3194120 : [--------------------] 1% + +Result for benchmark "deallocations" : +Min: 94 | Med: 141 | Avg: 132 | Max: 376 | Total : 1320700 (TSC) +[P00-P90] 94-141 : [##################--] 90% +[P90-P99] 141-141 : [#-------------------] 9% +[P99-Max] 141-376 : [--------------------] 1% +``` + +Virtual regions object slab allocator: +``` +Result for benchmark "allocations" : +Min: 141 | Med: 235 | Avg: 2781 | Max: 2840445 | Total : 27816339 (TSC) +[P00-P90] 141-282 : [##################--] 90% +[P90-P99] 282-329 : [#-------------------] 9% +[P99-Max] 329-2840445 : [--------------------] 1% + +Result for benchmark "deallocations" : +Min: 94 | Med: 141 | Avg: 130 | Max: 517 | Total : 1304109 (TSC) +[P00-P90] 94-141 : [##################--] 90% +[P90-P99] 141-141 : [#-------------------] 9% +[P99-Max] 141-517 : [--------------------] 1% +``` + +## Results for radix node slab allocator (PBA based) + +Radix node slab allocator (PBA based): +``` +Result for benchmark "allocations" : +Min: 141 | Med: 188 | Avg: 441 | Max: 534531 | Total : 4410339 (TSC) +[P00-P90] 141-235 : [##################--] 90% +[P90-P99] 235-282 : [#-------------------] 9% +[P99-Max] 282-534531 : [--------------------] 1% + +Result for benchmark "deallocations" : +Min: 94 | Med: 94 | Avg: 97 | Max: 329 | Total : 973370 (TSC) +[P00-P90] 94-94 : [##################--] 90% +[P90-P99] 94-141 : [#-------------------] 9% +[P99-Max] 141-329 : [--------------------] 1% +``` + +## Results for radix trees subsystem + +Radix trees subsystem: +``` +Result for benchmark "insertions into radix trees" : +Min: 2350 | Med: 3196 | Avg: 5194 | Max: 544448 | Total : 51944259 (TSC) +[P00-P90] 2350-3666 : [##################--] 90% +[P90-P99] 3666-134373 : [#-------------------] 9% +[P99-Max] 134373-544448 : [--------------------] 1% + +Result for benchmark "reading into radix trees" : +Min: 470 | Med: 517 | Avg: 553 | Max: 31631 | Total : 5537399 (TSC) +[P00-P90] 470-564 : [##################--] 90% +[P90-P99] 564-987 : [#-------------------] 9% +[P99-Max] 987-31631 : [--------------------] 1% + +Result for benchmark "searching value with lower bound key" : +Min: 423 | Med: 893 | Avg: 923 | Max: 30832 | Total : 9234184 (TSC) +[P00-P90] 423-1128 : [##################--] 90% +[P90-P99] 1128-1645 : [#-------------------] 9% +[P99-Max] 1645-30832 : [--------------------] 1% + +Result for benchmark "deleting values" : +Min: 1645 | Med: 1880 | Avg: 1888 | Max: 35203 | Total : 18889770 (TSC) +[P00-P90] 1645-2021 : [##################--] 90% +[P90-P99] 2021-2162 : [#-------------------] 9% +[P99-Max] 2162-35203 : [--------------------] 1% +``` + +## Results for Pez physical plane + +Pez physical plane: +``` +Result for benchmark "allocations for single page" : +Min: 1833 | Med: 2444 | Avg: 3496 | Max: 8225 | Total : 3496518 (TSC) +[P00-P90] 1833-5922 : [##################--] 90% +[P90-P99] 5922-6815 : [#-------------------] 9% +[P99-Max] 6815-8225 : [--------------------] 1% + +Result for benchmark "free for single page" : +Min: 1974 | Med: 2820 | Avg: 3807 | Max: 10105 | Total : 3807376 (TSC) +[P00-P90] 1974-6862 : [##################--] 90% +[P90-P99] 6862-8131 : [#-------------------] 9% +[P99-Max] 8131-10105 : [--------------------] 1% + +Result for benchmark "allocations for multiple pages" : +Min: 2209 | Med: 5593 | Avg: 5716 | Max: 35861 | Total : 5716798 (TSC) +[P00-P90] 2209-7191 : [##################--] 90% +[P90-P99] 7191-8178 : [#-------------------] 9% +[P99-Max] 8178-35861 : [--------------------] 1% + +Result for benchmark "free for multiple pages" : +Min: 1927 | Med: 4935 | Avg: 5095 | Max: 31772 | Total : 5095599 (TSC) +[P00-P90] 1927-7896 : [##################--] 90% +[P90-P99] 7896-10011 : [#-------------------] 9% +[P99-Max] 10011-31772 : [--------------------] 1% +``` + +## Results for malloc subsystem + +Malloc subsystem: +``` +Result for benchmark "sh_malloc for small size" : +Min: 188 | Med: 423 | Avg: 535 | Max: 380277 | Total : 5358752 (TSC) +[P00-P90] 188-799 : [##################--] 90% +[P90-P99] 799-1363 : [#-------------------] 9% +[P99-Max] 1363-380277 : [--------------------] 1% + +Result for benchmark "sh_free for small size" : +Min: 141 | Med: 329 | Avg: 403 | Max: 27401 | Total : 4039556 (TSC) +[P00-P90] 141-658 : [##################--] 90% +[P90-P99] 658-1128 : [#-------------------] 9% +[P99-Max] 1128-27401 : [--------------------] 1% + +Result for benchmark "sh_malloc for pages allocations" : +Min: 4935 | Med: 11468 | Avg: 11890 | Max: 47893 | Total : 11890013 (TSC) +[P00-P90] 4935-16450 : [##################--] 90% +[P90-P99] 16450-20257 : [#-------------------] 9% +[P99-Max] 20257-47893 : [--------------------] 1% + +Result for benchmark "sh_free for pages allocations" : +Min: 5593 | Med: 11421 | Avg: 12005 | Max: 50760 | Total : 12005398 (TSC) +[P00-P90] 5593-17296 : [##################--] 90% +[P90-P99] 17296-21432 : [#-------------------] 9% +[P99-Max] 21432-50760 : [--------------------] 1% +``` diff --git a/docs/shelter/bootconfig.md b/docs/shelter/bootconfig.md new file mode 100644 index 0000000..3e3f3a9 --- /dev/null +++ b/docs/shelter/bootconfig.md @@ -0,0 +1,64 @@ +# Shelter kernel boot configuration + +## Introduction + +The Shelter kernel requires a boot configuration in order to start. It doesn't come under the form of a command line but under the form of a C struct. For details on how to transmit the configuration to the kernel, please see [boot contract docs](bootcontract.md). + +## Overview + +Shelter boot configuration come under the following layout (extracted from `shelter/lib/include/kernel/conf.h`) : +``` C +typedef struct __attribute__((aligned(8))) { + sh_uint8 sig_start[8]; + sh_uint8 log_level; + sh_uint16 page_table_allocator_level; + sh_page_PHYSICAL_ADDRESS page_table_pool_pa; + sh_page_VIRTUAL_ADDRESS page_table_pool_va; + sh_bool test_benchmark; + sh_uint64 bench_iterations; + sh_bool log_disable_serial_port; + sh_bool disable_serial_port; + sh_uint16 log_ring_size; + sh_uint8 sig_end[8]; +} sh_conf_BOOT_CONFIG; +``` + +The starting signature must be `ShCfgBeg` in ASCII and the starting signature must be `ShCfgEnd` in ASCII. The alignment to respect for the boot config is 8 bytes. + +## List of all keys + +**log_level:** +- Type: 1 byte unsigned integer +- Description: represent the minimal (inclusive) log level for any log payload to be logged, can range from 0 to 6. The value of this key is provided by the `kernel_log_level` from the Blastproof boot config + +**page_table_allocator_level:** +- Type: 2 bytes unsigned integer +- Description: the value of the bumb allocator after all mapping inside the kernel boot PTP + +**page_table_pool_pa:** +- Type: physical address, aka 8 bytes unsigned integer +- Description: the physical address of the first page of the kernel boot PTP + +**page_table_pool_va:** +- Type: virtual address, aka 8 bytes unsigned integer +- Description: the virtual address of the first page of the kernel boot PTP, inside kernel virtual memory layout + +**test_benchmark:** +- Type: boolean, aka 1 byte unsigned integer +- Description: define if the kernel should test and benchmark his subsystems. The value of this key is provided by the `kernel_test_benchmark` from the Blastproof boot config + +**bench_iterations:** +- Type: 8 bytes unsigned integer +- Description: define the amount of iterations to apply for compatible benchmarks. Not all benchmarks will follow this value. The value of this key is provided by the `kernel_bench_iterations` from the Blastproof boot config + +**log_disable_serial_port:** +- Type: boolean, aka 1 byte unsigned integer +- Description: if true, the kernel will not output his logs on the serial port. The value of this key is provided by the `kernel_log_disable_serial_port` from the Blastproof boot config + +**disable_serial_port:** +- Type: boolean, aka 1 byte unsigned integer +- Description: define if the kernel should allow serial port usage. The value of this key is provided by the `kernel_disable_serial_port` from the Blastproof boot config + +**log_ring_size:** +- Type: 2 bytes unsigned integer +- Description: define the amount of pages used for logging ring buffer diff --git a/docs/shelter/bootcontract.md b/docs/shelter/bootcontract.md new file mode 100644 index 0000000..9bc0b5e --- /dev/null +++ b/docs/shelter/bootcontract.md @@ -0,0 +1,66 @@ +# Shelter boot contract + +## Introduction + +The Shelter boot contract is the list of steps necessary in order to successfully boot Shelter. + +## Prerequisites + +We assume the following: +- you understand the concept of pages table pool, Shelter boot configuration and Shelter memory map +- you have a bootloader capable of mapping things into into pages table pool +- your bootloader is capable of generating Shelter boot configuration and Shelter memory map +- your bootloader is capable of allocating or call the UEFI firmware to allocates pages to copy data into them +- your bootloader is capable of parsing VYX executable +- your bootloader is capable of accessing the content of the Shelter VYX executable and keycard binary +While this documentation is redacted like a tutorial, this isn't a tutorial nor intended to be understand as one. + +## Step 1: allocate pages + +Your bootloader should be allocating 4KB physical pages ranges and store their physical start address for the following elements with the following type if you use the `AllocatePages` from the EFI firmware: +- pages for `.text` section should be `EfiLoaderCode` type +- pages for `.data`, `.rodata`, `.bss` sections should be `EfiLoaderData` type +- pages for stack should be `EfiLoaderCoe` type. The standard amount of stack pages is 64 +- page(s) for keycard should be `EfiLoaderCode` type. Keycard should fit into one page, but you are free to allocate more if you want +- page(s) for Shelter boot configuration, memory map and pages table pool should be `EfiLoaderCode`. Shelter boot configuration should fit into one page, but you are free to allocate more if you want +- pages for Shelter logging ring should be `EfiLoaderCode` type + +Note: even if the logging ring size specified in the configuration is less than 4 pages, at least 4 pages should be allocated, because the kernel logs some things before loading the boot configuration that specify that log in ring buffer is disabled + +## Step 2: prepare pages + +The following steps should be completed before mapping the pages: +- zeroing the `.bss` section pages range +- zeroing the stack pages range +- zeroing the entire memory map pages range +- zeroing the entire logging ring buffer +- copying the content of the `.text`, `.data` and `.rodata` section inside their respectives pages range +- copying the content of Keycard inside his pages range +- copying the content of the boot configuration inside his pages range + +## Step 3: mapping pages + +The prerequisites to this step is having a PTP-compatible page mapping function. A working implementation can be found in the `Blastproof/src/libs/src/vyx.c` file. +The following pages ranges will be mapped at the following virtual address in the following order: +- the pages table pool pages range should be mapped first at the virtual address computed like this `text_base + text_size + data_size + rodata_size + bss_size + 0x200000` where `text_base` is the base VA of the `.text` section and the differents sizes are the size of each section, padded to the nearest 4096 bytes (normally it's already the case in the VYX executable header). It should be mapped in read-write with no execution permissions +- the `.text` section should be mapped at the VA indicated by `text_base`, with execution but read-only permissions +- the `.data` section should be mapped at the VA indicated by `text_base + text_size`, with no execution and read write permissions +- the `.rodata` section should be mapped at the VA indicated by `text_base + text_size + data_size` with no execution and read-only permissions +- the `.bss` section should be mapped at the VA indicated by `text_base + text_size + data_size + rodata_size`, with no execution and read-write permissions +- the 64 pages of the stack should be mapped at the VA indicated by `stack_base`, with no execution and read-write permissions +- Keycard pages ranges should be identity mapped to the same virtual address as his physical address, with execution but read-only permissions +- Shelter boot configuration pages should be mapped at VA `0x00180000`, with no execution and read-write permissions +- memory map pages should be mapped at VA `0x00190000`, with no execution and read-only permissions +- logging ring pages should be mapped at VA `0xFFFFFFFFF0000000`, with no execution and read-write permissions + +## Step 4: completing configuration + +The boot configuration should be completed with the PTP physical and virtual address as well as the level of the counter allocator of the PTP. Don't forget to update the boot configuration already copied inside the boot configuration pages range. + +## Step 5: generating memory map + +After obtaining the EFI memory map, the memory map should be generated inside her dedicated pages range. The complete syntax of the Shelter memory map can be found [here](memmap.md). + +## Step 6: jump + +Once all of this is done, the bootloader can exit the EFI boot services, prepare the register and jump to Keycard. The details about completing the registers is provided [here](keycard.md). diff --git a/docs/shelter/bootprocess.md b/docs/shelter/bootprocess.md new file mode 100644 index 0000000..837a14e --- /dev/null +++ b/docs/shelter/bootprocess.md @@ -0,0 +1,89 @@ +# Shelter Boot Process + +## Introduction + +Shelter has a very precise boot process to ensure everything goes to plan. In this file, we detail everything that is done for the kernel boot but it doesn't include how to use the kernel API, algorithms, etc. For that, please dig through the documentation. + +## 1) Basic kernel initialization + +1.1) Print kernel version + +1.2) Detect and parse boot config + +1.3) Load basic settings + +1.3.1) Load logging ring size + +1.3.2) Load serial port settings + +1.3.3) Load PTP virtual address + +1.3.4) Load kernel log level + +--- + +Starting here, all logs go through the standard log API. + +--- + +## 2) Memory subsystem initialization + +### 2.1) Page subsystem initialization + +2.1.1) Copying memory map to a secured buffer + +2.1.2) Initializing current PTP using boot configuration information + +2.1.3) Parsing and validating memory map, initializing physical pages bitmap + +### 2.2) Pez dependencies subsystems initialization + +2.2.1) Initializing physical region slab allocator + +2.2.1.a) Initializing slab allocator structure + +2.2.1.b) Adding first slab and verifying slab metadata + +2.2.2) Initializing virtual region slab allocator + +2.2.2.a) Initializing slab allocator structure + +2.2.2.b) Adding first slab and verifying slab metadata + +2.2.3) Initializing radix tree node slab allocator + +2.2.3.a) Initializing page block allocator structure for slab allocator + +2.2.3.b) Initializing slab allocator structure + +2.2.3.c) Adding first slab and verifying slab metadata + +2.2.4) Benchmarking and testing all slabs allocator + +2.2.5) Benchmarking and testing radix tree subsystem + +## 2.3) Pez physical subsystem initialization + +2.3.1) Creating Pez physical allocation plane + +2.3.2) Benchmarking and testing Pez physical allocation plane + +## 2.4) Pez virtual subsystem initialization + +2.4.1) Creating Pez virtual allocation plane for kernel heap + +(Pez virtual plane for kernel heap isn't benchmarked and tested because it's exactly the same algorithm and metadatas under the hood) + +## 2.5) Kernel heap initialization + +2.5.1) Initializing kernel heap structure + +2.5.2) For each slab allocator inside kernel heap: + +2.5.2.a) Initializing corresponding page block allocator structure + +2.5.2.b) Initializing corresponding slab allocator structure + +2.5.2.c) Adding first slab and verifying slab metadata + +2.5.3) Benchmarking and testing kernel heap diff --git a/docs/shelter/cpu/asmint.md b/docs/shelter/cpu/asmint.md new file mode 100644 index 0000000..9edfbf6 --- /dev/null +++ b/docs/shelter/cpu/asmint.md @@ -0,0 +1,13 @@ +# ASM instructions + +## Introduction + +While the Shelter kernel is made in C, somes basic CPU functions require using ASM instructions. For that, we define wrappers that can be used anywhere in the kernel. These wrappers are defined inside the header file `shelter/lib/include/cpu/asm.h` which act as the only place in the entire kernel where the `__asm__` should be used. The subsystem prefix is `sh_asm_`. + +## Overview + +Here is a list of all the wrappers available. They are all defined as `static inline` function: +- `inb`: take a `sh_uint16` in input for specifying the port and return the inputed byte +- `outb`: take a `sh_uint16` for specifying the port and a `sh_uint8` for specifying the byte to output +- `rdtsc`: take no arguments and return the current TSC as a `sh_uint64` +- `invlpg`: take a `void *` as an address and return nothing diff --git a/docs/shelter/cpu/cpuabstract.md b/docs/shelter/cpu/cpuabstract.md new file mode 100644 index 0000000..e982ab2 --- /dev/null +++ b/docs/shelter/cpu/cpuabstract.md @@ -0,0 +1,11 @@ +# CPU Abstraction + +## Introduction + +This component of the Shelter kernel allow for basic CPU functions abstractions, like ASM instruction, serial output and TSC utilities. + +## Summary + +1) [ASM instructions](asmint.md) +2) [Serial outputing API](serial.md) +3) [TSC API](tsc.md) diff --git a/docs/shelter/cpu/serial.md b/docs/shelter/cpu/serial.md new file mode 100644 index 0000000..05a0f1d --- /dev/null +++ b/docs/shelter/cpu/serial.md @@ -0,0 +1,12 @@ +# Serial outputing API + +## Introduction + +Shelter provide an abstraction around the serial port for outputing on it. This should act as the central point for outputing on the serial. The API is defined in `shelter/lib/include/cpu/serial.h` and implemented in `shelter/lib/src/cpu/serial.c`. This API obey the `serial_port_disabled` killswitch defined in kernel boot configuration. The subsystem prefix is `sh_serial_`. + +## Overview + +The API define the following elements: +- `sh_serial_load_serial_port_setting`: should be used only once at the start of the boot process to load the serial port setting using the argument `is_disabled` which is a `sh_bool` +- `sh_serial_send_byte`: safely (wait for previous byte to finish being send) send a byte to the serial port +The default and only serial port available is COM1. diff --git a/docs/shelter/cpu/tsc.md b/docs/shelter/cpu/tsc.md new file mode 100644 index 0000000..7d53ddd --- /dev/null +++ b/docs/shelter/cpu/tsc.md @@ -0,0 +1,20 @@ +# TSC API + +## Introduction + +In order to be able to measure and approximate time as soon as the kernel start the boot process, the TSC API is implemented in a volontary minimal way. The TSC API is defined inside `shelter/lib/include/cpu/tsc.h` and implemented inside `shelter/lib/src/cpu/tsc.c`. The API prefix is `sh_tsc_`. + +## Overview + +The TSC API being intented for measuring time during the boot process, the provided features are extremely basic. In order for any TSC value to start at 0, we define two concept: +- `kernel_init_tsc`: a TSC value initialized at the very start of the kernel boot process +- `kernel_current_tsc`: a TSC value which is the result of `kernel_init_tsc` substracted to the value returned by `sh_asm_rdtsc()` + +## API content + +The API define the following elements: +- `sh_tsc_TSC_VALUE`: a value of the TSC register, which is a wrapper of a `sh_uint64` +- `sh_tsc_read_value()`: an inline function reading the TSC register using `sh_asm_rdtsc()`, return a `sh_tsc_TSC_VALUE` +- `sh_tsc_init_tsc()`: a function initializing `kernel_init_tsc`. This function should only be called once as soon as the kernel start. Return a `SH_STATUS` +- `sh_tsc_get_kernel_init_tsc()`: return `kernel_init_tsc` under a `sh_tsc_TSC_VALUE` +- `sh_tsc_get_kernel_current_tsc()`: return the result of `sh_asm_rdtsc()` less `kernel_init_tsc`, under a `sh_tsc_TSC_VALUE` diff --git a/docs/shelter/index.md b/docs/shelter/index.md new file mode 100644 index 0000000..1edbbee --- /dev/null +++ b/docs/shelter/index.md @@ -0,0 +1,35 @@ +# Shelter Docs + +## Introduction + +Shelter is an hybrid kernel developped alongside and for the Vystem project. + +## Principles + +Shelter stricly follow these principles: +- absolutely no external code inside it. All the code that run in ring 0 should be sovereign +- absolutely no UNIX dependency on design philosophy. Shelter follow his own rules for kernel design (but doesn't forbid itself to reuse somes UNIX concepts), even if it mean spending years designing, refining and standardizing them +- a very strict boot process that ensure that everything goes well during the system boot +- fully made in C +- focuses on auditability, self-testing and benchmarking, and determinism +- use his own algorithms for various very important components of every kernel (mainly memory management, scheduling, etc), except on cryptographic algorithms, which follow a very strict integration processus + +The Shelter kernel is currently in very early developpement stage and isn't suitable at all for every-day usage. + +## Summary + +- Key principles to know in order to boot Shelter + - [Kernel bootstrapping using keycard](keycard.md) + - [Introduction to Pages Table Pool](ptp.md) + - [Shelter kernel boot configuration](bootconfig.md) + - [Shelter kernel memory map layout](memmap.md) + - [Shelter boot contract](bootcontract.md) +- [Shelter Boot Process](bootprocess.md) +- [Naming scheme](naming.md) +- Detailled components + - [CPU abstraction](cpu/cpuabstract.md) + - [Shelter standard library](std/std.md) + - [Kernel-specific APIs](kernel/kernel.md) + - [Memory subsystem](memory/index.md) + - [Test-and-benchmark framework](tab.md) +- [Benchmark results](bench.md) diff --git a/docs/shelter/kernel/config.md b/docs/shelter/kernel/config.md new file mode 100644 index 0000000..0102a85 --- /dev/null +++ b/docs/shelter/kernel/config.md @@ -0,0 +1,3 @@ +# Config parsing + +In Shelter, the parsing of the config is made by the `SH_STATUS sh_conf_get_boot_config(sh_conf_BOOT_CONFIG **config)` function. It allow for quick and secure configuration parsing. It create a `sh_conf_BOOT_CONFIG` object. These elements are defined inside `shelter/lib/include/kernel/conf.h` and implemented inside `shelter/lib/src/kernel/conf.c`. For the full configuration documentation, please see [boot configuration documentation](../bootconfig.md). diff --git a/docs/shelter/kernel/kernel.md b/docs/shelter/kernel/kernel.md new file mode 100644 index 0000000..5c81b48 --- /dev/null +++ b/docs/shelter/kernel/kernel.md @@ -0,0 +1,11 @@ +# Kernel-specific APIs + +## Introduction + +The Shelter kernel define his own specific APIs like logging and configuration parsing. + +## Summary + +1) [Log API](log.md) +2) [Config parsing](config.md) +3) [Tests utilities](testutils.md) diff --git a/docs/shelter/kernel/log.md b/docs/shelter/kernel/log.md new file mode 100644 index 0000000..41d20d9 --- /dev/null +++ b/docs/shelter/kernel/log.md @@ -0,0 +1,137 @@ +# Log API + +## Introduction + +The logging API is one of the most important of the entire kernel: it allow it to output informations with various level of importance and various sources. + +## Output methods + +The logging API can log informations with two differents methods: +- serial output: use the serial API to output in real time all the provided informations. Can be disabled by setting it to true using the `sh_log_load_serial_setting(sh_bool is_disabled)` function. The serial API being disabled, this method can also be disabled by that. +- ring buffer output: use the ring buffer API to store all the provided informations into a ring buffer, mapped by the bootloader. Can be disabled by setting the `logging_ring_size` boot configuration key to 0 + +The following functions provide way to set and get those settings: +- `void sh_log_load_serial_setting(sh_bool is_disabled)`: specify if serial log outputting should be disabled +- `sh_bool sh_log_get_serial_setting()` return the activation status of serial log outputting (`SH_TRUE` mean disabled) +- `void sh_log_load_logging_ring_size(sh_uint16 pages_count)`: set logging ring size +- `sh_uint32 sh_log_get_logging_ring_size()`: return logging ring size +- `sh_uint64 sh_log_get_total_bytes_written()`: return total amount of bytes written inside logging ring + +## Output primitives + +The following functions doesn't care about log level or log source and are specialized into specific data type: +- `void sh_log_byte(sh_uint8 byte)`: log a byte, basic entry point of the entire logging system +- `SH_STATUS sh_log_string(const char* str)`: log a table of characters +Here are the numbers logging functions under their naming scheme: +``` +sh_log_(u)int[8/16/32/64](_hex) +``` + +Basically, there is 12 functions that can output numbers, in various sizes (8 to 64 bits), signed or unsigned, in decimal or hexadecimal. The hexadecimal variant doesn't exist for signed integers. Examples: `sh_log_uint8`, `sh_log_int16`, `sh_log_uint64_hex`... + +There are two mores functions: +- `SH_STATUS sh_log_uint64_hex_fixed(sh_uint64 n)`: log a `sh_uint64` in hexadecimal without removing useless zeros, useful for logging pointers and address +- `SH_STATUS sh_log_double(double value)`: log a double encoded in decimal + +## Output channels and log level + +The Shelter logging API provide higher level functions to output on specific log channels. Here are all the log channels: +- `SH_LOG_DEBUG`: log level 0 +- `SH_LOG_LOG`: log level 1 +- `SH_LOG_WARNING`: log level 2 +- `SH_LOG_ERROR`: log level 3 +- `SH_LOG_CRITICAL`: log level 4 +- `SH_LOG_FATAL`: log level 5 +- `SH_LOG_TEST`: log level 6 + +Each log channel has a log level. The log level is set by the `log_level` boot configuration key. The log API will only output log messages with log level equal or higher to current log level. This restriction doesn't apply to log channel `SH_LOG_TEST`. + +The `sh_log_OUTPUT_TYPE` type is used to store output type by log level. It's a wrapper of `sh_uint8`. The `sh_log_output_type_valid(sh_log_OUTPUT_TYPE t)` can be used to check if a log level is valid. + +Two functions allow to set and get the current log level: +- `SH_STATUS sh_log_load_log_level(sh_uint8 log_level)` +- `sh_uint8 sh_log_get_log_level()` + +## Output sources + +The Shelter logging API also require a source in order to log informations. Here are all the possible sources: +- `SH_LOG_SOURCE_MAIN` +- `SH_LOG_SOURCE_CONF` +- `SH_LOG_SOURCE_PAGE` +- `SH_LOG_SOURCE_SLAB` +- `SH_LOG_SOURCE_TEST` +- `SH_LOG_SOURCE_PEZ` +- `SH_LOG_SOURCE_PBA` +- `SH_LOG_SOURCE_HEAP` +- `SH_LOG_SOURCE_STD` + +The `sh_log_OUTPUT_SOURCE` type is used to store log source. It's a wrapper of `sh_uint16`. The `sh_log_output_source_valid(sh_log_OUTPUT_SOURCE s)` can be used to check if a log level is valid. + +## Output payloads + +In order to compile all of those informations into one payload, the logging API provide this structure : +``` C +typedef struct { + sh_log_OUTPUT_TYPE output_type; + sh_log_OUTPUT_SOURCE output_source; + sh_tsc_TSC_VALUE tsc_value; + const char* message_pointer; +} sh_log_OUTPUT_PAYLOAD; +``` + +This allow to contain the message content, the output channel, the output source and the TSC value of any log message. + +There is two functions that can manipulate output payloads: +- `SH_STATUS sh_log_payload(sh_log_OUTPUT_PAYLOAD *payload)`: output a payload +- `SH_STATUS sh_log_payload_format(sh_log_OUTPUT_PAYLOAD *payload,va_list args)`: format then output a payload, not directly callable, see below for more informations + +## Higher levels logging functions + +The following functions will automatically create the payload depending on the parameters they receive. + +### Logging with automatic line break + +These functions are named like this: +``` +sh_log_[test/debug/log/warning/error/critical/fatal] +``` + +They all need two arguments in the following order (except `sh_log_test` which only need the first one): +- `const char* str`: the text to log +- `sh_log_OUTPUT_SOURCE source`: the source of the log message + +These functions will automatically add the `\n` characters at the end of each log message. + +### Logging without automatic line break + +These functions are named like this: +``` +sh_log_l[test/debug/log/warning/error/critical/fatal] +``` + +They all need two arguments in the following order (except `sh_log_ltest` which only need the first one): +- `const char* str`: the text to log +- `sh_log_OUTPUT_SOURCE source`: the source of the log message + +### Logging with formating + +In order to allow for formatting log messages, the log API provide functions named like this: +``` +sh_log_f[test/debug/log/warning/error/critical/fatal] +``` + +They all need at least two arguments in the following order (except `sh_log_ltest` which only need the second one): +- `sh_log_OUTPUT_SOURCE source`: the source of the log message +- `const char* format`: the format text +- all the others argument after that are used as content for formatting the log message + +All of these functions use `sh_log_payload_format()` under the hood which itself use `SH_STATUS sh_log_format(const char* format,va_list args)` to format log messages. + +The format syntax is as follow: +- `%s`: insert a string made of `char`, passed as `char*` +- `%d`: insert a double +- `%x`: insert a integer that need to be provided as a `sh_uint64`, format it as hexadecimal without removing useless zeros. Used to log addresses +- `%c`: insert a `char` +- `%%`: insert a `%` + +There is also a specific syntax for formatting integers. It's start with the `%` symbol, then need a amount of bytes (can be 1, 2, 4 or 8) and a output format: `u` for unsigned decimal, `s` for signed decimal, `U` for unsigned decimal. Example: `%1s` output a `sh_int8` in decimal, `%2u` output a `sh_uint16` in decimal, `%4U` output a unsigned `sh_uint32` in hexadecimal. diff --git a/docs/shelter/kernel/testutils.md b/docs/shelter/kernel/testutils.md new file mode 100644 index 0000000..c4fb80a --- /dev/null +++ b/docs/shelter/kernel/testutils.md @@ -0,0 +1,23 @@ +# Tests utilities + +## Introduction + +To support the test-and-benchmark framework, the Shelter kernel include a common base of utilities that provide to all tests a basic way to store and expose their results. This file only cover the basic utilities usable by all tests, not the detail and typical results of each test. These utilities are defined inside `shelter/lib/include/kernel/tests/test_utils.h` and implemented inside `shelter/lib/src/kernel/tests/test_utils.c`. + +## Overview + +The test-and-benchmark (TAB) framework is responsible for testing differents subsystems to ensure their stability and performances over differents devices. But it also provides a common base for showing results and others little things. + +Firsty, it define internally a buffer of 10000 `sh_tsc_TSC_VALUE` for storing cycles count and compute statistics. The pointer to this buffer is obtainable through the `sh_tsc_TSC_VALUE* sh_test_get_tsc_values_buffer_ptr()` function. + +Then, the TAB framework also define the `void sh_test_load_iterations_count(sh_uint64 iterations_num)` function. This function load the iterations count defined by the `bench_iterations` boot configuration key. Some tests doesn't support this feature, so this function will only set iterations count for tests that support custom iterations count. + +Finally, each test uses the `SH_STATUS sh_test_compute_print_stats(char* benchname,sh_tsc_TSC_VALUE *tsc_value_array,sh_uint64 array_size)` function. This function will compute various statistics and print them like this: +``` +Result for benchmark "" : +Min: | Med: | Avg: | Max: | Total : (TSC) +[P00-P90] -P90 : [##################--] 90% +[P90-P99] P90-P99 : [#-------------------] 9% +[P99-Max] P99- : [--------------------] 1% +``` +Here, `PXX` where `XX` is a percentage mean a percentile. diff --git a/docs/shelter/keycard.md b/docs/shelter/keycard.md new file mode 100644 index 0000000..c18ac55 --- /dev/null +++ b/docs/shelter/keycard.md @@ -0,0 +1,21 @@ +# Kernel bootstrapping using keycard + +## Introduction + +Shelter being a kernel that need to start with pagging enabled, it need to have a intermediary program to safely jump to it: Keycard. Keycard and Shelter have been designed and thinked for x86-64 only. + +## Overview + +Keycard is a very simple assembly program with the following role in the following order: +- loading the kernel page table and enabling pagging +- loading and aligning the kernel stack +- jumping to kernel entry point + +In order to achieve that, it need to be identity mapped into virtual lower half into the kernel page table, using a physical address provided by UEFI firmware. The bootloader map it into a page (keycard compiled code fit into a single page) with allowed execution but read-only. It also have a kind of 'ABI' which specify which value to put into which register before jumping to Keycard: +- `rax`: should contain the stack top virtual address (VA) +- `rbx`: should contain the physical adrdess of the page table root +- `rcx`: should contain the VA of the kernel entry point + +Due to security concerns, the `keycard.bin` file contain the compiled code of Keycard and is stored inside the InitFS with the kernel VYX executable. + +The Keycard source code is stored inside `Blastproof/src/keycard.asm`. diff --git a/docs/shelter/memmap.md b/docs/shelter/memmap.md new file mode 100644 index 0000000..c266be2 --- /dev/null +++ b/docs/shelter/memmap.md @@ -0,0 +1,37 @@ +# Shelter kernel memory map layout + +## Introduction + +In order to transmit efficiently the EFI memory map to the kernel, Shelter have his own memory map layout that need to be created from the EFI memory map in order for Shelter to understand it. For details about mapping the Shelter memory map inside the kernel virtual memory space, please see [boot contract docs](bootcontract.md). + +## Header + +The Shelter memory map is first assigned a buffer of 64 kilobytes or 16 pages mapped with non-execution and read only permissions. Each memory map has an header described by the following C structure (sourced from `shelter/lib/include/memory/page.h`) +``` C +#pragma pack(1) +typedef struct { + sh_uint8 sig_start[8]; + sh_uint64 entry_count; + sh_uint64 entry_size; + sh_uint8 mmap_syntax_version; +} sh_page_MEMORY_MAP_HEADER; +#pragma pack() +``` + +Each field is packed inside the struct. The signature must be `SheMmapB`. The number of entry is indicated inside the `entry_count` field. The size of an entry is indicated inside the `entry_size` field. Both of these fields are little endian 8 bytes unsigned integer. The `mmap_syntax_version` is always set to `0x01`, as the current version of the Shelter memory map syntax. The kernel also compute the total size of the memory map (header + all entries) and raise an error if it's above the size of the memory map buffer. + +## Entries + +After the header, there are entries that describe which part of the physical memory is currently unusable (due to various factors like ACPI table, etc). A entry is described by the following C structure: +``` C +#pragma pack(1) +typedef struct { + sh_uint32 type; + sh_uint64 physical_start; + sh_uint64 pages_count; + sh_uint64 attributes; +} sh_page_MEMORY_MAP_ENTRY; +#pragma pack() +``` + +Each field is packed inside the struct. The `type` field is a 4 bytes unsigned integer that give the type of the region, as the EFI specification specify it. The `physical_start` field is a 8 bytes unsigned integer that contain the physical address of the start of the region. The `pages_count` field is a 8 bytes unsigned integer that contain the number of 4 kilobytes pages that constitute the region. The `attributes` field contain any other attributes provided by the UEFI firmware. All fields are in little endian. diff --git a/docs/shelter/memory/heap.md b/docs/shelter/memory/heap.md new file mode 100644 index 0000000..3d65174 --- /dev/null +++ b/docs/shelter/memory/heap.md @@ -0,0 +1,45 @@ +# Kernel heap manager + +## Introduction + +In order to allocate small-sized object or medium-sized buffers (approximately between one and one hundred pages), the lower half have been designed for hosting the heap of the kernel. This is an early design subject to changes for perfomances and/or robustess in the coming updates. It's defined inside `shelter/lib/include/memory/heap.h` and implemented inside `shelter/lib/src/memory/heap.c`. + +## Overview + +The kernel heap manager is based on a dual allocation strategy: +- small objects, less or equal to 1024 bytes goes into slab allocators +- medium buffers, greater than 1024 bytes, are allocated using the Pez allocator for physical and virtual planes management + +The heap metadatas are stored inside the `sh_heap_KERNEL_HEAP` structure. The `alloc_size_tree` field is a radix tree mapping virtual pages offset of any pages mapping with their size in pages. This allow for quick retrieval of any pages allocations on the heap. + +## Slab allocators + +These are described into [generic slab allocator documentation](slabs.md#generic-slab-allocators). Each one is setted up with a PBA containing 12 terabytes of virtual memory + +Here are a table describing the virtual pages ranges of all allocators: + +Object size range (bytes) | Allocator level | Virtual pages range start address +--- | --- | --- +1-8 | 0 | `0x0000200000000000` +9-16 | 1 | `0x00002C0000000000` +17-32 | 2 | `0x0000380000000000` +33-64 | 3 | `0x0000440000000000` +65-128 | 4 | `0x0000500000000000` +129-256 | 5 | `0x00005C0000000000` +257-512 | 6 | `0x0000680000000000` +513-1024 | 7 | `0x0000740000000000` + +## Pages allocations + +If the needed amount of bytes is greater than 1024 bytes, pages are allocated on the heap. The number of pages allocated is the nearest amount that can satisfy the demand. The pages are allocated inside a virtual pages range starting at `0x0000100000000000` and contain 16 terabytes of virtual memory. + +## API + +The heap manager provide the following API, that should only be called by the abstraction provided by `sh_malloc`, except the first three one: +- `sh_heap_KERNEL_HEAP *sh_heap_get_default_heap()`: return a pointer to the kernel heap structure +- `void sh_heap_load_default_heap(sh_heap_KERNEL_HEAP *heap)`: set the pointer to the kernel heap structure +- `SH_STATUS sh_heap_init_heap(sh_pez_PHYSICAL_PLANE *phys_plane,sh_pez_VIRTUAL_PLANE *virt_plane,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_heap_KERNEL_HEAP *kernel_heap)`: initialize the heap structure, the calling entity need to setup and place the generic slab allocators pointers manually inside the returned structure +- `SH_STATUS sh_heap_allocate_pages(sh_uint32 pages_count,sh_page_VIRTUAL_ADDRESS *address)`: allocate a certain amount of pages, provided by the Pez physical backend and mapped on the heap pages allocation range +- `SH_STATUS sh_heap_free_pages(sh_page_VIRTUAL_ADDRESS va)`: free an allocated region of pages from the heap. Return the pages to Pez physical backend +- `SH_STATUS sh_heap_allocate_object(sh_uint32 size_bytes,sh_page_VIRTUAL_ADDRESS *address)`: allocate a certain object based on his size, automatically uses the most adapted generic slab allocator. Will refuse any size superior to 1024 bytes +- `SH_STATUS sh_heap_free_object(sh_page_VIRTUAL_ADDRESS va)`: free a certain object by providing his VA. Automatically free from the previously used slab allocator. Will refuse any VA not in the range of the object allocation range diff --git a/docs/shelter/memory/index.md b/docs/shelter/memory/index.md new file mode 100644 index 0000000..0570514 --- /dev/null +++ b/docs/shelter/memory/index.md @@ -0,0 +1,16 @@ +# Memory subsystem + +## Introduction + +The memory subsystem is responsible for handling tasks like physical pages allocations, virtual memory management, initial memory map analysis, pages mapping and unmapping and kernel heap management. + +## Summary + +1) [Page subsystem](page.md) +2) [Virtual memory layout](vmemlayout.md) +3) [Ring buffer](ring.md) +4) [Pages block allocator](pba.md) +5) [Slabs allocator](slabs.md) +6) [Radix trees subsystem](radix.md) +7) [Pez plane manager](pez.md) +8) [Kernel heap manager](heap.md) diff --git a/docs/shelter/memory/page.md b/docs/shelter/memory/page.md new file mode 100644 index 0000000..f827fc0 --- /dev/null +++ b/docs/shelter/memory/page.md @@ -0,0 +1,90 @@ +# Page subsystem + +## Introduction + +The Page subsystem is a part of the memory subsystem responsible for managing initial memory map analysis, physical pages bitmap, pages tables pool management, mapping and unmapping pages as well as memory allocations before Pez initialization and memory statistics. It's defined inside `shelter/lib/include/memory/page.h` and implemented inside `shelter/lib/src/memory/page.c` + +## Macros and types + +The Page header defines a few usefuls macros like: +- the start and end of the kernel big allocations virtual range +- the size of a page +- the max memory count in bytes as well as the max physical pages count +- the standard size of a pages table pool pages range +- all the types of EFI memory type +- the null value of physical and virtual addresses +- the flags for pages table entry + +The Page header also defines the following types: +- `sh_page_MEMORY_TYPE`: a wrapper of `sh_uint32` +- `sh_page_PHYSICAL_ADDRESS` and `sh_page_VIRTUAL_ADDRESS`: wrappers of `sh_uint64` + +## Structures + +The Page header defines the following structure: +- `sh_page_MEMORY_MAP_ENTRY` and `sh_page_MEMORY_MAP_HEADER`: internal structures used for memory map analysis +- `sh_page_PAGE_TABLE_POOL`: structure for pages table pool, containing the physical and virtual address of a pages table pool as well as the internal bitmap for the internal allocator +- `sh_page_MEM_STATS`: a structure filled by a specific function to get statistics on physical memory using physical pages bitmap + +## Pages table pool + +It's recommended to read [pages table pool documentation](../ptp.md) before reading this. + +The Page subsystem provide the following functions regarding PTP management: +- `SH_STATUS sh_page_load_boot_ptp_va(sh_page_VIRTUAL_ADDRESS pt_pool_va)`: load the virtual address of the boot PTP provided by bootloader. This VA is the address of the root PML4, not the address of the PTP structure. Should only be used once +- `sh_page_VIRTUAL_ADDRESS sh_page_get_boot_ptp_va()`: return the pointer to root PM4 stored inside boot PTP +- `SH_STATUS sh_page_init_ptp(sh_page_PHYSICAL_ADDRESS ptp_pa,sh_page_VIRTUAL_ADDRESS ptp_va,sh_uint64 initial_fill_level,sh_page_PAGE_TABLE_POOL *page_table_pool)`: initialize the boot PTP structure with the fill level of the PTP, provided by bootloader. Doesn't allocate pages for a new PTP but it use the pages already allocated for the boot PTP. Should only be used once for boot PTP +- `SH_STATUS sh_page_dump_ptp_bitmap(sh_page_PAGE_TABLE_POOL *ptp)`: dump the PTP bitmap on the log output, intended for debug purposes +- `sh_page_PHYSICAL_ADDRESS sh_page_ptp_alloc_one_page(sh_page_PAGE_TABLE_POOL *pt_pool)`: allocate one page from the PTP internal bitmap allocator +- `static inline sh_uint64 *sh_page_ptp_pa_to_va(sh_page_PAGE_TABLE_POOL *ptp,sh_uint64 pa)`: convert a PA from the PTP pages range to a usable VA using the current PTP +- `SH_STATUS sh_page_ptp_va_to_pa(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_page_PHYSICAL_ADDRESS *pa)`: return equivalent physical address by searching inside provided PTP + +## Memory map analysis + +The Page subsystem is responsible for the initial memory map analysis. It provide the following functions: +- `SH_STATUS sh_page_copy_memory_map()`: copy the memory map from her original VA to a dedicated buffer +- `SH_STATUS sh_page_check_memory_map()`: check for memory map signature and detect eventual buffers overflow +- `SH_STATUS sh_page_analyse_memory_map(sh_page_PAGE_TABLE_POOL *ptp)`: analyse memory map, initialize physical pages bitmap by allocating into a free area of the physical memory layout. This allow for dynamic sizing for physical pages bitmap. + +All three functions should be run in order and only once. + +## Physical pages bitmap + +In order to ensure a reliable source of thruth and early allocations, the Page subsystem manage a physical pages bitmap where one bit on mean one page occupied. +The Page subsystem provides the following functions regarding physical pages bitmap: +- `SH_STATUS sh_page_set_pages_range_bitmap(sh_uint8 *bitmap,sh_uint64 page_count_in_bitmap,sh_uint64 page_index,sh_uint64 page_count,sh_bool state)`: allow for manipulation of any bitmap, but should only be used for physical pages bitmap +- `static inline sh_bool sh_page_is_allocated(sh_uint8 *bitmap,sh_uint64 page_index)`: return the status of a specific page inside the bitmap +- `sh_page_VIRTUAL_ADDRESS sh_page_get_physical_bitmap_ptr()`: return physical pages bitmap pointer + +## Pages mapping and unmapping + +The Page subsystem provide the following functions regarding pages mapping: +- `SH_STATUS sh_page_map_one_page_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_page_PHYSICAL_ADDRESS pa,sh_uint64 flags)`: map one page inside the pages table contained in the provided PTP +- `SH_STATUS sh_page_is_va_mapped_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va)`: return `SH_STATUS_VA_NOT_MAPPED` or `SH_STATUS_VA_MAPPED`, which aren't errors, according to page mapping state +- `SH_STATUS sh_page_is_va_range_mapped_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes)`: return `SH_STATUS_VA_NOT_MAPPED`, `SH_STATUS_VA_FULLY_MAPPED` or `SH_STATUS_VA_PARTIALLY_MAPPED` according to pages range mapping state +- `SH_STATUS sh_page_map_contiguous_pages_range_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_page_PHYSICAL_ADDRESS pa,sh_uint64 flags,sh_uint64 size_bytes)`: map a pages range that has to be contiguous virtually and physically. VAs availability is checked, not physicals pages availability +- `SH_STATUS sh_page_unmap_one_page_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va)`: unmap one page from the pages table contained inside provided PTP +- `SH_STATUS sh_page_unmap_contiguous_pages_range_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes)`: unmap a pages range from the pages table inside provided PTP. Both physical and virtual ranges have to be contiguous. Virtual pages range is checked, not physical ranges occupation + +## Basic memory allocations + +The Page subsystem implement basic memory allocation, available before Pez initialization, using a first fit algorithm. +The Page subsystem provides the following functions regarding basic memory allocations: +- `sh_uint64 sh_page_get_one_page_na()`: return the index of the first available page inside physical pages bitmap +- `SH_STATUS sh_page_search_available_va_range(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS range_base,sh_page_VIRTUAL_ADDRESS range_size_bytes,sh_uint64 size_bytes,sh_page_VIRTUAL_ADDRESS *address_found)`: search a free region inside PTP using provided size and search area boundaries provided +- `SH_STATUS sh_page_search_physical_contiguous_block_na(sh_uint64 pages_needed,sh_page_PHYSICAL_ADDRESS *pa)`: search a free region of provided size inside physical pages bitmap +- `SH_STATUS sh_page_alloc_contiguous(sh_page_PAGE_TABLE_POOL *ptp,sh_uint64 size_bytes,sh_page_VIRTUAL_ADDRESS* va)`: allocate a specified amount of pages and return the start VA of the allocated region +- `SH_STATUS sh_page_alloc_contiguous_extended(sh_page_PAGE_TABLE_POOL *ptp,sh_uint64 size_bytes,sh_page_VIRTUAL_ADDRESS* va,DEFAULT sh_uint64 flags,DEFAULT sh_page_VIRTUAL_ADDRESS va_range_start,DEFAULT sh_uint64 va_range_size_bytes)`: allow for extensive allocations parameters for selecting mapping flags and VAs search range +- `SH_STATUS sh_page_unalloc_one_page(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va)`: free one page. Check if page is mapped before hand. PA is calculated from searching into PTP +- `SH_STATUS sh_page_unalloc_contiguous(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes)`: free a contiguous memory region. Check if the entire region is allocated before hand + +This role of memory allocations is lost once Pez is initialized. + +## Memory statistics + +The Page subsystem provide the following functions regarding memory statistics: +- `sh_uint64 sh_page_get_physical_memory_amount_pages()`: return amount of physical memory installed in pages +- `sh_uint64 sh_page_get_physical_memory_amount_bytes()`: return amount of physical memory installed in bytes +- `SH_STATUS sh_page_get_memory_stats(sh_page_MEM_STATS *mem_stats)`: provide a extensive amount of statistics on physical memory. + +For a more human-readable output, the function `sh_log_mem_stats` can be used. diff --git a/docs/shelter/memory/pba.md b/docs/shelter/memory/pba.md new file mode 100644 index 0000000..4b419ce --- /dev/null +++ b/docs/shelter/memory/pba.md @@ -0,0 +1,19 @@ +# Pages block allocator + +## Introduction + +The pages block allocator is a simple configurable bumb allocator designed to be the pages source of slabs allocator inside the kernel. It's defined inside `shelter/lib/include/memory/pba.h` and implemented inside `shelter/lib/src/memory/pba.c`. + +## Overview + +A pages block allocator is conifigurable with various parameters. + +Firstly, it need to know the size of each block that are allocated. This is specified inside the `block_pages` field of the PBA structure, under the `sh_uint64` type. + +The `start_va` field, a `sh_page_VIRTUAL_ADDRESS`, specify the starting VA of the virtual pages ranges dedicated to the allocator. It need to be aligned on a boundary of a region `block_pages` pages. The `total_pages` field, a `sh_uint64`, specify the size of the decicated virtual pages range, in pages. It need to be a multiple of `block_pages`. + +Finally, the `block_count` field, a `sh_uint64`, store the amount of blocks already allocated. The `max_blocks` field, a `sh_uint64`, is computed at initialization and allow to know instantly if the PBA is full, which happen when the dedicated virtual pages range is fully mapped. + +All those fields are stored inside the `sh_pba_PAGE_BLOCK_ALLOCATOR` structure. This structure can be manipulated by the following functions: +- `SH_STATUS sh_pba_init(sh_pba_PAGE_BLOCK_ALLOCATOR *pba,sh_page_VIRTUAL_ADDRESS start_va,sh_uint64 area_pages_amount,sh_uint64 block_pages)`: initialize a PBA, using provided parameters. It ensure the previously specified constraints are met with the provided parameters +- `SH_STATUS sh_pba_alloc(sh_pba_PAGE_BLOCK_ALLOCATOR *pba,sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS *ptr)`: allocate a block from the PBA. Take care of the physical region search (using Page subsystem or Pez if it's initialized), VA computation (using provided virtual pages range) and pages mapping, as well as physical pages bitmap update if Pez isn't initialized. diff --git a/docs/shelter/memory/pez.md b/docs/shelter/memory/pez.md new file mode 100644 index 0000000..958d862 --- /dev/null +++ b/docs/shelter/memory/pez.md @@ -0,0 +1,86 @@ +# Pez plane manager + +## Introduction + +Pez is the allocator that manages physical pages allocation and virtual pages ranges allocation. It's a custom design, (normally) never seen before. It's defined into `shelter/lib/include/memory/pez/pez.h` and implemented into `shelter/lib/src/memory/pez/pez.c`. + +## Planes + +Pez manages memory using planes. It can be as granular as a 4KB pages. Since Pez uses `sh_uint32` for representating pages index and regions sizes, a plane can technically manages up to 16 terabytes of memory. + +In the implementation, Pez separate the physical plane from virtual planes. They work the same in their logic and algorithm but use differents source of truth and the virtual plane can provide an offset to each address it allocate. + +The physical plane uses the physical pages bitmap that the Page subsystem maintain as source of truth. While the source of truth for virtual planes are the corresponding pages mapping into the virtual pages range the virtual plane manages, for the moment, only empty (not allocated at all) virtual planes can be created. + +## Metadatas + +### Regions objects + +The Pez allocator uses regions objects allocated from their dedicated (physical or virtual) slab allocators. + +The physical version is: +``` C +#pragma pack(1) +typedef struct { + sh_uint32 start_page_index; + sh_uint32 region_size_pages; + sh_uint8 next_region_index[3]; + sh_uint8 flags; +} sh_pez_REGION_PHYSICAL_OBJECT; +#pragma pack() +``` + +The virtual version is: +``` C +#pragma pack(1) +typedef struct { + sh_uint32 start_page_index; + sh_uint32 region_size_pages; + sh_uint32 next_region_index; +} sh_pez_REGION_VIRTUAL_OBJECT; +#pragma pack() +``` + +The `start_page_index` field is the index of the first page of the region, starting at 0 from the start of the plane. The `region_size_pages` field is the size of the region in pages. The `next_region_index` field is the index of the next region in the size list. It's one byte longer on virtual region object to account for the 29 bits index. The `flags` field, while not being used in this implementation, can be used to store flags. + +These regions objects represente free regions that are ready to be allocated. + +### Size lists radix tree + +Each plane maintain lists of free regions by their sizes. These are call size lists. They are linked through the `next_region_index` field, using their object index inside their respective slab allocator. These lists only contain regions of exact size. To signal the end of a size list, the last element in the list have their `next_region_index` field set to 0. This prevent the first slot of any of the slab inside the slab allocator to be allocated. + +Each plane maintain a radix tree that link sizes of free regions (in page count) to indexes of the first region in this size list. This radix tree is called the size lists radix tree and is 8 levels deep. + +This architecture allow for two allocations methods that are stricly time-constant or nearly time-constant: + +Each time the allocator need to allocate a region, it search for this size into the corresponding size list. Two possibles results: +- if a free region of exactly this size is found, it's popped of the size list and allocated +- if no free region of exactly this size is found, the algorithm search for another region immediatly above in size of the requested size using the backtracking algorithm. This free region is popped of the size list and splitted in two. The first part is allocated and the second part is inserted into the corresponding size list. + +This allocation scheme allow for zero internal fragmentation at the granularity of 4KB pages. + +### Boundary radix tree + +Each plane maintain what we call a boundary radix tree. This radix tree map the start and end pages index in the plane of each free regions to a boundary entry. For one-page free region, there is only one boundary. The two boundaries for free regions with more than one page are strictly identical. + +A boundary is structured like this: +- The lower 32 bits of the boundary are the region object index of the region to which the boundary belong +- The upper 32 bits of the boundary are the previous region object index in the size list of the region to which the boundary belong + +This radix tree allow for two things: +- constant-time passive fusion of free regions with deallocated occupied regions, at each free of occupied region. The external fragmentation is repaired as soon as the allocated region that cause it is deallocated. +- instant element suppression of any free region in any size lists + +## API + +The Pez allocator implementation inside the Shelter kernel provide the following functions: +- `void sh_pez_set_available()`: executed once to signal that Pez has taken over the role of Page for allocating pages +- `sh_bool sh_pez_is_available()`: return the current status of availability of Pez +- `sh_pez_PHYSICAL_PLANE* sh_pez_get_reference_phys_plane()`: return the reference physical plane that is used to manage physical memory +- `SH_STATUS sh_pez_init_physical_plane(sh_uint8 *physical_bitmap,sh_uint64 physical_page_count,sh_slab_reg_phys_SLAB_ALLOCATOR *slab_reg_phys,struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_pez_PHYSICAL_PLANE *phys_plane)`: initialize the physical plane by scanning the physical pages bitmap +- `SH_STATUS sh_pez_alloc_physical_pages(sh_pez_PHYSICAL_PLANE *phys_plane,sh_uint32 pages_count,sh_page_PHYSICAL_ADDRESS *address)`: allocate pages range from the physical plane. Doesn't map the pages at all +- `SH_STATUS sh_pez_free_physical_pages(sh_pez_PHYSICAL_PLANE *phys_plane,sh_page_PHYSICAL_ADDRESS *address,sh_uint32 pages_count)`: free a pages range and return them to the physical plane. Doesn't unmap the pages at all +- `SH_STATUS sh_pez_debug_physical(sh_pez_PHYSICAL_PLANE *phys_plane)`: print debugging information about Pez physical plane, intented for debug +- `SH_STATUS sh_pez_init_virtual_plane(sh_page_VIRTUAL_ADDRESS plane_offset,sh_slab_reg_virt_SLAB_ALLOCATOR *slab_reg_virt,struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_page_PAGE_TABLE_POOL *reference_ptp,sh_pez_VIRTUAL_PLANE *virt_plane)`: initialize an empty virtual plane +- `SH_STATUS sh_pez_alloc_virtual_pages(sh_pez_VIRTUAL_PLANE *virt_plane,sh_uint32 pages_count,sh_page_VIRTUAL_ADDRESS *address)`: allocate virtual pages range from the provided virtual plane +- `SH_STATUS sh_pez_free_virtual_pages(sh_pez_VIRTUAL_PLANE *virt_plane,sh_page_VIRTUAL_ADDRESS *address,sh_uint32 pages_count)`: free virtual pages range and return them to the provided virtual plane diff --git a/docs/shelter/memory/radix.md b/docs/shelter/memory/radix.md new file mode 100644 index 0000000..58d6daf --- /dev/null +++ b/docs/shelter/memory/radix.md @@ -0,0 +1,37 @@ +# Radix trees subsystem + +## Introduction + +The memory subsystem require a capable radix trees to unleash the full power of the Pez allocator. It's defined inside `shelter/lib/include/memory/pez/radix.h` and implemented inside `shelter/lib/src/memory/pez/radix.c`. + +## Overview + +In Shelter, the radix nodes are 128 bytes wide, capable of storing 16 `sh_uint64` values. This mean all radix trees have a 16 fanout, they filter 4 bits per level, and require 16 levels for 64 bits keys and 8 levels for 32 bits keys. + +The node is defined by this sructure: +``` C +typedef struct { + sh_page_VIRTUAL_ADDRESS ptr[16]; +} sh_radix_NODE; +``` + +A radix tree is defined into the following structure: +``` C +typedef struct { + sh_radix_NODE *root_node; + sh_uint8 depth; +} sh_radix_TREE; +``` + +The API for modifying nodes is as follow: +- `SH_STATUS sh_radix_node_read_value(sh_radix_NODE *node,sh_uint8 index,sh_page_VIRTUAL_ADDRESS* value)`: read a value from the node at the following index +- `SH_STATUS sh_radix_node_set_value(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_radix_NODE *node,sh_uint8 index,sh_page_VIRTUAL_ADDRESS value)`: set the value inside the node at the provided node + +The API for manipulating radix trees is as follow: +- `SH_STATUS sh_radix_tree_init(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_radix_TREE *tree,sh_uint8 depth)`: initialize a radix tree, fail if depth is greater than 16. Allocate the root node +- `SH_STATUS sh_radix_tree_get_value(sh_radix_TREE *tree,sh_uint64 key,sh_page_VIRTUAL_ADDRESS *value)`: search in a straight line inside the tree to provide the associated value of a key. Stop and return `SH_STATUS_NOT_FOUND` as soon as it hit an empty pointer where there should be one +- `SH_STATUS sh_radix_tree_insert_value(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_radix_TREE *tree,sh_uint64 key,sh_page_VIRTUAL_ADDRESS value)`: insert a value associated with a key inside the tree. Can allocates new nodes and overwrite previously setted value. +- `SH_STATUS sh_radix_tree_delete_value(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_radix_TREE *tree,sh_uint64 key)`: delete a vale and deallocates all nodes (including leaf) that form a path to the leaf if the deletion make them empty +- `SH_STATUS sh_radix_tree_search_smallest_min_bound(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_radix_TREE *tree,sh_uint64 lower_bound_key,sh_page_VIRTUAL_ADDRESS *value)`: backtracking algorithm used to quickly search the value with a key equal or greater than the provided key. Can't allocate new nodes + +The radix trees subsystem uses the same slab allocator for all radix trees. diff --git a/docs/shelter/memory/ring.md b/docs/shelter/memory/ring.md new file mode 100644 index 0000000..349d2b6 --- /dev/null +++ b/docs/shelter/memory/ring.md @@ -0,0 +1,11 @@ +# Ring buffer + +## Introduction + +The memory subsystem provide a simple ring buffer API, mainly used by the log API. This implementation doesn't allow for automatic structure initialization with allocation. The structure has to be manually created for it to work. This ring buffer implementation is defined inside `shelter/lib/include/memory/ring.h` and implemented inside `shelter/lib/src/memory/ring.c` + +## Overview + +The main structure for a ring buffer is `sh_ring_RING_BUFFER_HEADER`. The function provided by the API are volontary very simple for the moment: +- `SH_STATUS sh_ring_write_byte(sh_ring_RING_BUFFER_HEADER *ring_buffer,sh_uint8 byte)`: write a byte inside provided ring buffer +- `SH_STATUS sh_ring_write_string(sh_ring_RING_BUFFER_HEADER *ring_buffer,char *string)`: write a null-terminated string inside provided ring buffer diff --git a/docs/shelter/memory/slabs.md b/docs/shelter/memory/slabs.md new file mode 100644 index 0000000..2b9eb19 --- /dev/null +++ b/docs/shelter/memory/slabs.md @@ -0,0 +1,91 @@ +# Slabs allocator + +## Introduction + +The memory subsystem provides 4 independant but similars slab allocators dedicated to various purposes. + +## Regions objects slabs allocator + +These slabs allocator are dedicated to the storage of region objects. These objects are 12 bytes in size. There is two slab allocators that store region objects: +- `slab_reg_phys`: the slab allocator for physical region objets. It uses 24 bits index, making it able to store up to 16 millions objects. Defined in `shelter/lib/include/memory/slabs/slab_reg_phys.h` and implemented in `shelter/lib/src/memory/slabs/slab_reg_phys.c` +- `slab_reg_virt`: the slab allocator for virtual region objets. It uses 29 bits index, making it able to store up to 536 millions objects. Defined in `shelter/lib/include/memory/slabs/slab_reg_virt.h` and implemented in `shelter/lib/src/memory/slabs/slab_reg_virt.c` + +They uses 3 pages per slab, capable of storing 1024 objects per slabs. They store the slab header outside, into a dedicated, pre allocated area. The slabs are allocated, reused, but never deallocated. + +The slab allocator uses indexes to identify objects slots. These indexes are what the user is given upon allocation and need to be provided back in order to free objects slots. The indexes are composed of two parts: +- index in the slab: 10 lower bits +- slab index: 14 or 19 upper bits + +The structures `sh_slab_reg_phys_SLAB_STRUCT` and `sh_slab_reg_virt_SLAB_STRUCT` define the headers of each slab. The structures `sh_slab_reg_phys_SLAB_ALLOCATOR` and `sh_slab_reg_virt_SLAB_ALLOCATOR` are used to store allocators metadata. The `sh_slab_reg_phys_OBJECT_INDEX` and `sh_slab_reg_virt_OBJECT_INDEX` are both wrapper of `sh_uint32`. + +The only difference between these two are the amount of slabs they can store and their dedicated virtual pages ranges. + +The API for physical region objects slab allocator is as follow: +- `SH_STATUS sh_slab_reg_phys_alloc_init(sh_slab_reg_phys_SLAB_ALLOCATOR* slab_alloc,sh_page_PAGE_TABLE_POOL *ptp)`: initialize the allocator, allocate all the headers but does not add the first slab +- `SH_STATUS sh_slab_reg_phys_add_slab(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_phys_SLAB_STRUCT** out_slab)`: add a new slab, can allocate from Page or Pez subsystem, do all the mapping itself +- `void* sh_slab_reg_phys_ref_to_ptr(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_slab_reg_phys_OBJECT_INDEX ref)`: transform an index into a pointer to the referenced object +- `SH_STATUS sh_slab_reg_phys_alloc(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_phys_OBJECT_INDEX* out_index)`: allocate a new object and return his index. Can allocate new slabs +- `SH_STATUS sh_slab_reg_phys_dealloc(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_slab_reg_phys_OBJECT_INDEX index)`: deallocate a object using the provided index. Doesn't delete slabs that are made empty by objects deallocation + +The API for virtual region objects slab allocator is as follow: +- `SH_STATUS sh_slab_reg_virt_alloc_init(sh_slab_reg_virt_SLAB_ALLOCATOR* slab_alloc,sh_page_PAGE_TABLE_POOL *ptp)`: initialize the allocator, allocate all the headers but does not add the first slab +- `SH_STATUS sh_slab_reg_virt_add_slab(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_virt_SLAB_STRUCT** out_slab)`: add a new slab, can allocate from Page or Pez subsystem, do all the mapping itself +- `void* sh_slab_reg_virt_ref_to_ptr(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_slab_reg_virt_OBJECT_INDEX ref)`: transform an index into a pointer to the referenced object +- `SH_STATUS sh_slab_reg_virt_alloc(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_virt_OBJECT_INDEX* out_index)`: allocate a new object and return his index. Can allocate new slabs +- `SH_STATUS sh_slab_reg_virt_dealloc(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_slab_reg_virt_OBJECT_INDEX index)`: deallocate a object using the provided index. Doesn't delete slabs that are made empty by objects deallocation + +## PBA-based slab allocators + +These slab allocators relies on a PBA to automatically allocate and map slab pages inside their dedicated virtual pages ranges. They doesn't create their own PBA. While this responsability fall on the user, this allow the user to set personnalized virtual pages range, even if they aren't intented to have multiples instances of them at the same time. + +These slabs allocator also store the header of each slabs inside the slab, making it possible to allocate slabs headers on the fly. The slab header contain pointers that can form a partial slabs linked list. + +Finally, these slab allocators provide infinite slabs without any software limits. They reuse but doesn't deallocate slabs. They also don't use indexes but direct pointers. + +### Radix node slab allocator + +This allocator use 32 pages slabs, and manage 128 bytes object. The slab contain 1024 objects slots. But due to the space taken by the header, each slab only contain 1006 objects due to the header taking space. + +Inside the slab header, the slab allocator also maintain a 16 bit extra object per radix node. It serve at a bitmap for radix nodes. + +The `sh_slab_radix_node_SLAB` structure describe the header of each slab, as well as each object slot. The `sh_slab_radix_node_SLAB_ALLOCATOR` structure contain the metadatas of the allocator. The header is 256 bytes wide. + +The API for radix nodes slab allocator is as follow: +- `SH_STATUS sh_slab_radix_node_alloc_init(struct sh_slab_radix_node_SLAB_ALLOCATOR* slab_alloc,sh_pba_PAGE_BLOCK_ALLOCATOR *pba)`: initialize the slab allocator, doesn't allocate any slab +- `SH_STATUS sh_slab_radix_node_add_slab(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_radix_node_SLAB** out_slab)`: add a new slab, allocate from the PBA +- `SH_STATUS sh_slab_radix_node_alloc(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_radix_NODE** out_obj)`: allocate a new object and return his pointer. Can allocate new slabs +- `SH_STATUS sh_slab_radix_node_dealloc(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_radix_NODE *object_ptr)`: deallocate a object using the provided pointer. Doesn't delete slabs that are made empty by objects deallocation +- `sh_uint16 *sh_slab_radix_node_get_node_bitmap(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_radix_NODE *object_ptr)`: return a pointer to the bitmap of the node + +It's defined inside `shelter/lib/include/memory/slabs/slab_radix_node.h` and implemented inside `shelter/lib/src/memory/slabs/slab_radix_node.c`. + +### Generic slab allocators + +These slab allocators are used to store objects allocated on the heap. The header is always 128 bytes. Each slab contain 512 objects but the actual size of each slab depend on the level of the allocator: + +Level | Object size in bytes | Amount of pages per slab | Amount of actual objects per slab +--- | --- | --- | --- +0 | 8 | 1 | 496 +1 | 16 | 2 | 504 +2 | 32 | 4 | 508 +3 | 64 | 8 | 510 +4 | 128 | 16 | 511 +5 | 256 | 32 | 511 +6 | 512 | 64 | 511 +7 | 1024 | 128 | 511 + +The `sh_slab_generic_SLAB` structure describe the header of each slab, as well as each object slot. The `sh_slab_generic_SLAB_ALLOCATOR` structure contain the metadatas of the allocator. + +The API for generic slab allocators is as follow: +- `SH_STATUS sh_slab_generic_alloc_init(sh_uint8 level,struct sh_slab_generic_SLAB_ALLOCATOR* slab_alloc,sh_pba_PAGE_BLOCK_ALLOCATOR *pba)`: initialize the slab allocator, doesn't allocate any slab +- `SH_STATUS sh_slab_generic_add_slab(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_generic_SLAB** out_slab)`: add a new slab, allocate from the PBA +- `SH_STATUS sh_slab_generic_alloc(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,void** out_obj)`: allocate a new object and return his pointer. Can allocate new slabs +- `SH_STATUS sh_slab_generic_dealloc(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,void *object_ptr)`: deallocate a object using the provided pointer. Doesn't delete slabs that are made empty by objects deallocation + +It's defined inside `shelter/lib/include/memory/slabs/slab_generic.h` and implemented inside `shelter/lib/src/memory/slabs/slab_generic.c`. + +## Notes + +The current implementation of all slabs allocator uses a bitmap stored inside the headers to find the first available object slot. It's planned the next update to test the freelist approch. If it's concluant, it will be used as the default approch in the next update. + +It's also planned that all slabs allocators uses PBAs in the next update. This switch will not changes the indexes-based approch for region objects slab allocators. diff --git a/docs/shelter/memory/vmemlayout.md b/docs/shelter/memory/vmemlayout.md new file mode 100644 index 0000000..c77db93 --- /dev/null +++ b/docs/shelter/memory/vmemlayout.md @@ -0,0 +1,9 @@ +# Virtual memory layout + +## Introduction + +The virtual memory layout is a file `shelter/lib/include/memory/vmem_layout.h` defining which area of virtual memory serve which purposes. It's automatically checked for no overlaps by a python script. + +## Overview + +Virtual memory regions are defined with macros. Any macro ending with `_VA` will create a new virtual region for the script. The script will then look for the size of this virtual region in another macro that start with the same prefix and end with `_SIZE_BYTES`. If a macro ending with `_VA` doesn't have a corresponding macro ending with `_SIZE_BYTES`, the script will trigger an error and the kernel compilation will fail. If a macro ending with `_SIZE_BYTES` doesn't have a corresponding macro ending with `_VA`, the script will ignore it. The start of each virtual region must be aligned to 4096 bytes and the size must be provided in bytes. Any overlapping virtual region will trigger a compilation error. Consider using this file as the source of trust for everything related to static virtual regions. Any macro that doesn't end with `_VA` or `_SIZE_BYTES` or that doesn't correspong to the behaviour described above will be ignored. diff --git a/docs/shelter/naming.md b/docs/shelter/naming.md new file mode 100644 index 0000000..1157e61 --- /dev/null +++ b/docs/shelter/naming.md @@ -0,0 +1,21 @@ +# Naming scheme + +Shelter has a very specific way of organizing subsystems. +First, each part of the kernel (except `main.c` which is responsible for the boot process) is in his own folder. To this date, there is 4 main parts: +- `cpu`: CPU abstractions +- `kernel`: kernel services +- `std`: Shelter standard library +- `memory`: the whole memory subsystem + +Then, each of these parts have specific subsystem or API prefix. These prefixes always start by `sh_` and doesn't include the part each subsystem or API is in. For exemple, the serial outputing API prefix is `sh_serial_`. + +The `std` part is the only part where, when not precised, the prefix will be shortened to only `sh_`, like `sh_malloc` or `SH_STATUS`. + +In all the subsystems and API described in this documentation, the prefix will always be indicated. + +These prefixes extand to names of functions, macros and type/structures. Here is the naming style for each of these entity: +- functions: all letters should be lowercase, starting with the prefix in lowercase. Example: `sh_serial_send_byte` +- macros: all letters should be uppercase, starting with the prefix in uppercase. Example: `SH_PAGE_RW` +- types/structure: the name should start with the prefix in lowercase and continue with the type/structure name in uppercase. Example: `sh_page_PAGE_TABLE_POOL`. + +Please avoid abreviations/acronyms in the type name, instead define the abreviation/acronym inside the short comment preceding the declaration. Abreviations/acronyms anywhere else is tollerated as long as it's defined somewhere obvious. Don't hesitate to ask if you are not sure. diff --git a/docs/shelter/ptp.md b/docs/shelter/ptp.md new file mode 100644 index 0000000..45d0722 --- /dev/null +++ b/docs/shelter/ptp.md @@ -0,0 +1,23 @@ +# Introduction to Pages Table Pool + +## Introduction + +In order to easely manage pages that are used for pages tables (PT), the Vystem project use pages tables pool (PTP). For details about mapping the PTP inside the kernel virtual memory space, please see [boot contract docs](bootcontract.md). + +## Overview + +In order to map any page into a pages table on x86-64, you need to descend all four layers of the PT. But what happen when you need a new page for an intermediate layer or directly a pages table ? To solve this problem, we have decided to introduce Pages Table Pool (PTP), a very simple way to allocate new pages for pages table. + +Each pages table pool is made from a range of pages, contiguous both physically (this is what allow us to instantely know which value to put into a PTE for a intermediate layer) and virtually. The standard size for this range is currently 4096 pages, giving us plenty of rooms for various allocations accross the virtual memory. This size is evolutive and can changed with versions. The PML4 root page is by standard the first page of the PTP, making it suitable to use the starting VA of any PTP (as long as it's aligned on 4096 bytes) as value for the CR3 register. + +There is two kinds of PTP: +- modifiable PTP: this is any PTP that is modifiable by the code that run into the virtual layout the PTP describe. To accomplish that, we map the entire pages range of the PTP into itself before putting it into CR3. We use this kind of PTP for the Shelter kernel +- unmodifiable PTP: here, we just don't do the recursive mapping of the pages range of the PTP into itself. This is the kind of PTP that will be used for any userland program + +PTP, when created from the bootloader, are mapped within themself read-write and non-execution permissions. There is no primitive to create PTP from the kernel for the moment. + +## Allocator + +The purpose of any PTP is to have a reserved amount of pages to permit the quick allocation of single page to allow any mapping. There is two kinds of allocator, depending on the needs of the situation: +- bumb/counter allocator: here, we simply increment a counter that represent an index inside the pages range of the PTP, allowing for very quick PTP initialization. Intended for PTP creation and mapping without unmapping by the program creating the PTP. This is the kind of allocator used into the Blastproof bootloader +- bitmap allocator: a bitmap for storing which pages of the pages range is allocated or not. Intended when heavy unmapping is made, to free pages more easely. This is the allocator used in the Shelter Kernel. It can be initialized from the counter value, passed by the bootloader for example. For the moment, automatic pages freeing from the page table isn't implemented. diff --git a/docs/shelter/std/malloc.md b/docs/shelter/std/malloc.md new file mode 100644 index 0000000..a48eeea --- /dev/null +++ b/docs/shelter/std/malloc.md @@ -0,0 +1,7 @@ +# Heap memory allocations + +The Shelter standard library provide a single API for memory allocations on the heap. The heap is only initialized at the end of the memory subsystem and can't be used before. It's not adapted for memory mapped I/O or big buffers allocations (larger than one hundrer pages). The heap internal documentation can be found inside the memory subsystem documentation. + +The memory allocations API provide the two following functions (defined inside `shelter/lib/include/std/malloc.h` and implemented inside `shelter/lib/src/std/malloc.c`): +- `void* sh_malloc(sh_uint64 size)`: allocate `size` amount of bytes. Return `SH_NULLPTR` if an error occured. The heap internal will trigger a heap crash (essentially a `while (SH_TRUE)` loop) to prevent any further damage if something very bad happen. +- `void sh_free(void *ptr)`: free the memory allocated at `ptr`. The heap internal will trigger a heap crash (essentially a `while (SH_TRUE)` loop) to prevent any further damage if something very bad happen. diff --git a/docs/shelter/std/mem.md b/docs/shelter/std/mem.md new file mode 100644 index 0000000..268f81c --- /dev/null +++ b/docs/shelter/std/mem.md @@ -0,0 +1,6 @@ +# Basic memory operations + +The Shelter standard library provide very basic memory operations primitives, defined in `shelter/lib/include/std/mem.h` and implemented inside `shelter/lib/src/std/mem.c`: +- `SH_STATUS sh_mem_compare(const void *a,const void *b,sh_uint64 size)`: compare two memory regions with the same size. Return `SH_STATUS_SUCCESS` if both regions are equal, or `SH_STATUS_MEM_NOT_EQUAL` if one byte is different. +- `SH_STATUS sh_mem_copy(const void *destination,const void *source,sh_uint64 size)`: copy one region of memory to another. Return `SH_STATUS_SUCCESS` +- `SH_STATUS sh_mem_set_8(sh_uint8 *ptr,const sh_uint8 byte,sh_uint64 count)`: set a provided amount of bytes to the value of one provided byte. Return `SH_STATUS_SUCCESS` diff --git a/docs/shelter/std/status.md b/docs/shelter/std/status.md new file mode 100644 index 0000000..d697c50 --- /dev/null +++ b/docs/shelter/std/status.md @@ -0,0 +1,20 @@ +# Return status + +## Introduction + +The vast majority of functions in Shelter return status code know as the `SH_STATUS` type. All the status codes are defined inside `shelter/lib/include/std/status.h`. + +## Overview + +Shelter define a status code with the `SH_STATUS` type. It's a wrapper of `sh_int64`. + +The sign of a status play a big role: +- If the sign is negative, the status code isn't an error but rather an indication of something not that bad that happened during the function call. It can also serve the purpose of indicating a more detailled result. +- If the status code is 0, it's equivalent to a success, defined as `SH_STATUS_SUCCESS` +- If the sign is positive, the status code signify an error. You can use the `sh_status_error(SH_STATUS status)` function to know if a status code is an error. Return `SH_TRUE` if so. + +Please check `shelter/lib/include/std/status.h` for the list of all status codes. + +In Shelter, most function will returned to the caller the status code of all the functions calls they made if an error happened. Sometimes, it's necessary to dig through the code to understand from where an error come from. + +If at any moment, a function that return a `SH_STATUS` call a function that return a pointer and the pointer is `SH_NULLPTR`, the status code `SH_STATUS_ERROR_NULLPTR_RETURNED` (not all error start with `SH_STATUS_ERROR`) will be returned. diff --git a/docs/shelter/std/std.md b/docs/shelter/std/std.md new file mode 100644 index 0000000..e6265c9 --- /dev/null +++ b/docs/shelter/std/std.md @@ -0,0 +1,14 @@ +# Shelter standard library + +## Introduction + +In order to function properly, any kernel need a standard library. Shelter define his own minimal standard library with his own particularities. + +## Summary + +1) [Basic types](types.md) +2) [Return status](status.md) +3) [Basic memory operations](mem.md) +4) [Heap memory allocations](malloc.md) + +You can include the file `shelter/lib/include/std/stdlib.h` to include all necessary headers to access the Shelter standard library. diff --git a/docs/shelter/std/types.md b/docs/shelter/std/types.md new file mode 100644 index 0000000..e230183 --- /dev/null +++ b/docs/shelter/std/types.md @@ -0,0 +1,35 @@ +# Basic types + +The Shelter standard library define the following type in `shelter/lib/include/std/type.h`: +- `sh_int8`: 1 byte signed integer +- `sh_uint8`: 1 byte unsigned integer +- `sh_int16`: 2 bytes signed integer +- `sh_uint16`: 2 bytes unsigned integer +- `sh_int32`: 4 bytes signed integer +- `sh_uint32`: 4 bytes unsigned integer +- `sh_int64`: 8 bytes signed integer +- `sh_uint64`: 8 bytes unsigned integer +- `sh_bool`: wrapper of `sh_uint8` +- `sh_iter64`: wrapper of `sh_uint64`, used for loops +- `va_list`: wrapper of `__builtin_va_list` + +The Shelter standard library the following macros and values in `shelter/lib/include/std/type.h`: +- `SH_INT8_MIN`: the minimum possible value of `sh_int8` +- `SH_INT8_MAX`: the maximum possible value of `sh_int8` +- `SH_UINT8_MAX`: the maximum possible value of `sh_uint8` +- `SH_INT16_MIN`: the minimum possible value of `sh_int16` +- `SH_INT16_MAX`: the maximum possible value of `sh_int16` +- `SH_UINT16_MAX`: the maximum possible value of `sh_uint16` +- `SH_INT32_MIN`: the minimum possible value of `sh_int32` +- `SH_INT32_MAX`: the maximum possible value of `sh_int32` +- `SH_UINT32_MAX`: the maximum possible value of `sh_uint32` +- `SH_INT64_MIN`: the minimum possible value of `sh_int64` +- `SH_INT64_MAX`: the maximum possible value of `sh_int64` +- `SH_UINT64_MAX`: the maximum possible value of `sh_uint64` +- `SH_TRUE`: a wrapper of `(sh_bool)1` +- `SH_FALSE`: a wrapper of `(sh_bool)0` +- `SH_DEFVALUE`: the default value for any argument marked by `DEFAULT` +- `SH_NULLPTR`: wrapper of `(void*)0` +- `va_start(v,l)`: wrapper of `__builtin_va_start(v,l)` +- `va_end(v)`: wrapper of `__builtin_va_end(v)` +- `va_arg(v,l)`: wrapper of `__builtin_va_arg(v,l)` diff --git a/docs/shelter/tab.md b/docs/shelter/tab.md new file mode 100644 index 0000000..b3a8bf0 --- /dev/null +++ b/docs/shelter/tab.md @@ -0,0 +1,53 @@ +# Test-and-benchmark framework + +## Introduction + +The Shelter kernel includes his own test-and-benchmark (TAB) framework to support the measurements of performances and reliability of differents subsystems. This file describe the logic behind all the tests included inside the Shelter kernel and the embarqued payload, not the common API base described in [tests utilities](kernel/testutils.md) or the benchmark results. + +A payload is a file generated at compilation, mostly random, used for accurate testing in specific situations. + +## Slab allocators tests + +These tests are designed to test the reliability and performances of the three main slab allocators behind the Pez subsystem: physical and virtual region objects slab allocators as well as the radix tree nodes slab allocators. They are defined inside `shelter/lib/include/kernel/tests/test_slabs.h` and implemented inside `shelter/lib/src/kernel/tests/test_slabs.c`. This test doesn't require a payload. + +They consist in allocating 10000 objects from these three slabs and deallocating them afterward. This allocate a few slabs for each allocator, meaning they will consume a few pages at the start but nothing too expensive for the systems. This also allow for quick allocations of the first few thousands objects of these allocators since the slabs are already allocated. + +## Radix tree subsystem tests + +These tests are designed to test the reliability and performances of the radix tree subsystem. They are defined inside `shelter/lib/include/kernel/tests/test_radix.h` and implemented inside `shelter/lib/src/kernel/tests/test_radix.c`. They use the `shelter/lib/src/kernel/tests/payloads/test_radix_payload.h`. + +The payload contain the following datas: +- `test_keys`: an array of 10000 random `sh_uint64` serving as keys for the pairs +- `test_values`: an array of 10000 random `sh_uint64` serving as values for the pairs +- `test_search`: an array of 10000 random `sh_uint64` serving as keys for the lower bound keys for the backtracking algorithm + +All tests are run on a 16-deep unique radix tree. The test are in the following order: +- Insertion: trying to insert 10000 pairs of keys-values defined inside `test_keys` and `test_values` +- Reading: trying to obtain the associated value of each of the 10000 pairs by providing the values of `test_keys`, checking if the correct value is returned by comparing to the values of `test_values` +- Searching: trying to search using the backtracking algorithm with lower bound key using the values of `test_search` +- Deleting: trying to delete all pairs using the keys contained inside `test_keys` + +## Pez subsystem tests + +This test is designed to test the reliability and performances of the Pez subsystem. They are defined inside `shelter/lib/include/kernel/tests/test_pez.h` and implemented inside `shelter/lib/src/kernel/tests/test_pez.c`. They use the `shelter/lib/src/kernel/tests/payloads/test_pez_payload.h`. + +This specific test has been designed to see how Pez react under high pressure. It's only apply it on Pez's physical plane, as the virtual plane uses the same logic. + +The payload contain the following datas: +- `test_pez_physical_size`: an array of 2000 `sh_uint64` serving as sizes in pages for the allocation. Half of them are 1. The other half is between 2 and 1000. +- `test_pez_physical_alloc`: an array of 4000 `sh_uint64`, serving as reference for when to allocate and when to deallocate each allocated region. These numbers range from 1 to 2000 and appear exactly 2 times for each. + +The test work like this: we iterate on all the value of `test_pez_physical_alloc`. When we hit for the first time a number, we allocate the corresponding size inside `test_pez_physical_size` by using the allocation number as index. When we hit for the second time a number, we deallocate this allocation. + +The test will separate single page allocations and frees from multi-pages allocations and frees when showing the results. + +## Malloc subsystem test + +These tests are design to test the reliability and performances of the Pez subsystem. They are defined inside `shelter/lib/include/kernel/tests/test_malloc.h` and implemented inside `shelter/lib/src/kernel/tests/test_malloc.c`. They use the `shelter/lib/src/kernel/tests/payloads/test_malloc_payload.h`. + +The payload contain the following datas: +- `test_malloc_small_size`: contain 10000 sizes for small allocations (under or equal to 1024 bytes) +- `test_malloc_big_size`: contain 1000 sizes in pages, ranging from 1 to 25, serving the same role as `test_pez_physical_size` +- `test_malloc_big_alloc`: contain 2000 values, serving the same role and generated like `test_pez_physical_alloc` + +The malloc test combine two tests: 10000 allocations and deallocations of variously sized small objects using the generic slab allocators, and 1000 allocations and frees of pages on the heap, on the same logic of the Pez subsystem test, but adapted to the quantity used in this test. diff --git a/docs/vyld/index.md b/docs/vyld/index.md new file mode 100644 index 0000000..b2c08e6 --- /dev/null +++ b/docs/vyld/index.md @@ -0,0 +1,5 @@ +# Vyld Docs + +Vyld, standing for Vystem Linker, is a specialised linker designed to convert ELF binaries to the VYX executable format. This documentation is divided into two parts: +- [The VYX executable format](vyx.md) +- [The Vyld utility](vyld.md) diff --git a/docs/vyld/vyld.md b/docs/vyld/vyld.md new file mode 100644 index 0000000..571d0bf --- /dev/null +++ b/docs/vyld/vyld.md @@ -0,0 +1,67 @@ +# Vyld Docs + +## Introduction + +Vyld, standing for Vystem Linker, is the utility responsible for creating VYX executable from ELF binaries. Vyld has been intended to be used only on a regular Linux system. For a better understanding, it's recommanded you read the [VYX format docs](vyx.md) first. + +## Detailled informations + +Folder: `vyld` +Source code file: `vyld.cpp` + +For building the `vyld` utility, use the following command: +``` bash +g++ vyld.cpp -o vyld +``` + +External library: +- ELFIO, made by Serge Lamikhov-Center, sourced from [serge1/ELFIO](https://github.com/serge1/ELFIO), under the MIT license + +## Usage + +Requirement: having the `gcc` compiler and `ld` linker reachable in path. For the moment, only the `gcc` compiler is supported. + +The `vyld` utility can be used like this: +``` bash +vyld ... +``` + +Each path provided except the last one has to be C file, or else the compilation might fail. The last path can be any extension, but for standard respect, it should be a VYX file. + +Vyld can accept custom compilation flags for provided C files if they are passed by using the `VYLD_COMPILATION_FLAGS` environnement variable. They are added to the mandatory compilation flags for provided C files but they will not replace them. The mandatory compilation flags for provided C files are: +``` bash +-fno-pic -m64 -mcmodel=large +``` + +The mandatory compilation flag for the `_vyx_start.c` entry point are: +``` bash +-ffreestanding -nostdlib -nostartfiles -Wall -Wextra -fno-stack-protector -fno-builtin -fno-pic -m64 -mcmodel=large +``` +These flags aren't applied to provided C files unless passed inside `VYLD_COMPILATION_FLAGS`. + +Vyld need to have access to the `_vyx_start.c` file in his executable directory in order to compile any VYX binary. This file is the entry point of any VYX executable. + +Command example: +``` bash +vyld hello.c io.c binary.vyx +VYLD_COMPILATION_FLAGS="-Wall -Wextra -Werror -Wpedantic" vyld hello.c io.c binary.vyx +``` + +We plane to expand drastically the capabilities and versatility of the `vyld` utility and the VYX format in the future, but for the moment, it's sufficient for what we need (loading the Shelter kernel). + +## Detailled generation processus + +1) Obtaining his own executable path by reading `/proc/self/exe` in order to obtain a deterministic working directory +2) Searching for flags provided into the `VYLD_COMPILATION_FLAGS` +3) Checking for existence of `_vyx_start.c ` and compiling it into `_vyx_start.o` using mandatory compilation flags for entry point. The resulting object file is stored inside the `vyld` executable directory +4) Iterating for each provided C files: finding his location and generating his output name under the format `.o` +5) Iterating for each provided C files: compiling each C file into an object file using mandatory compilation flags for provided C files and flags passed through `VYLD_COMPILATION_FLAGS`. The resulting objects files are stored inside the `vyld` executable directory +6) Combining all objects files (entry point and all provided files under their object form) into one object file named `combined.o` and stored inside the `vyld` executable directory. For that, we use the `gcc -r` feature of `gcc`. The entry point object file is specifically added in first position +7) Opening `combined.o` and obtaining sections size for `.text`, `.data`, `.rodata` and assimiled sections as well as `.bss` section. Padding these sizes to the nearest multiple of 4096 bytes superior to these sizes +8) Generating the `.text` section base virtual address (VA) and stack base VA, aligned on 4096 bytes pages +9) Generating a custom linker script to correctly link the `combined.o` file with the right VAs inside the code and putting in order all the sections into the final binary +10) Linking the `combined.o` file into an ELF executable named `compiled.elf` and stored inside the `vyld` executable folder +11) Opening the `compiled.elf` binary, extracting all sections, generating VYX header and executable + +The range for generating `.text` section base is currently `0xFFFF800000000000` to `0xFFFF900000000000-1`, then padded to the nearest page boundary. +The range for generating stack base is currently `0xFFFFF00000000000` to `0xFFFFFF8000000000-0x100000`, then padded to the nearest page boundary. diff --git a/docs/vyld/vyx.md b/docs/vyld/vyx.md new file mode 100644 index 0000000..b071224 --- /dev/null +++ b/docs/vyld/vyx.md @@ -0,0 +1,32 @@ +# The VYX executable format + +## Introduction + +The VYX format, standing for Vystem Executable, is a very simple binary format made for simple binary loading from any environnement. As long as you can access the `.vyx` file, you can load a VYX binary. + +## Header + +Because VYX format has been designed to provide a simple way to load any kind of binary, we wanted to make it as easely updatable as possible. For the moment, it's just a very simple format built for loading the Shelter kernel, but in a few updates, it will be use for loading all kind of binaries. No spoiler on that. The header is designed like that: +- Signature: three bytes spelling `VyX` in ASCII +- VYX format version: the version of the VYX format used in the binary, represented as an uint16_t. Depending on the version number, the following structure will probably changed a lot. The current version is `0x0001`. +- `.text` base address: the virtual address (VA) at which the `.text` should be loaded. It should be aligned on the boundary of a 4 kilobytes page. Represented as an uint64_t +- stack base: the VA at which the stack base should be setuped. Be careful, it indicate the base, not the top. It should be aligned on the boundary of a 4 kilobytes page. Represented as an uint64_t +- the size of the `.text` section: It should be aligned on a multiple of 4096 bytes. Represented as an uint64_t +- the size of the `.data` section: It should be aligned on a multiple of 4096 bytes. Represented as an uint64_t +- the size of the `.rodata` section: It should be aligned on a multiple of 4096 bytes. Represented as an uint64_t +- the size of the `.bss` section: It should be aligned on a multiple of 4096 bytes. Represented as an uint64_t + +All the multibytes integers should be written in little endian. The VYX executable format has been made for x86-64 only binary. + +## Sections organizations + +The VYX format only support three sections inside the binary that should be in the following order: `.text`, `.data`, `.rodata`. The `.bss` should be initialized and zeroed by the loader just after the `.rodata` section. The space occupied by the three sections inside the binary should occupy a multiple of 4096 bytes, according to the size indicated in the header, padded with zeroes if necessary. +For example, if each sections take 2 pages, after being padded to the nearest multiple of 4096 bytes superior to their real size, and that the `.text` base is 0x1000, `.text` should be located at 0x1000, `.data` at 0x3000, `.rodata` at 0x5000 and `.bss` at 0x7000 + +## Specifications + +The VYX binaries use the SystemV ABI. They doesn't support rellocations nor dynamic linking. While relative addressing into the binary code is theorically working, all the Vystem binaries will be compiled 64 bits static addressing with position-dependent code (non-PIC), especially the kernel. That why respecting the indicated `.text` section base and order of sections loading is extremely important. +Obviously, the right privileges should be applied in term of pagging: no execution unless `.text` pages, read only `.text` and `.rodata` sections, read-write `.data` and `.bss` sections. +For the moment, the entry point is located at the base of the `.text` section. +The stack size can be decided at the convenience of the loader, but the stack size used for the Shelter kernel is, for the moment, 256 kilobytes. The stack growing downward, the initial stack pointer should be set to stack base + stack size. +A reference implementation of a VYX loader, exploiting all currently documented features, can be found into the Blastproof bootloader source code. diff --git a/licenses/MIT.md b/licenses/MIT.md new file mode 100644 index 0000000..96d39d0 --- /dev/null +++ b/licenses/MIT.md @@ -0,0 +1,25 @@ +The MIT License (MIT) +===================== + +Copyright © `2026` `lolo859` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/MPL-2.0.md b/licenses/MPL-2.0.md new file mode 100644 index 0000000..c21b426 --- /dev/null +++ b/licenses/MPL-2.0.md @@ -0,0 +1,355 @@ +Mozilla Public License Version 2.0 +================================== + +### 1. Definitions + +**1.1. “Contributor”** + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +**1.2. “Contributor Version”** + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +**1.3. “Contribution”** + means Covered Software of a particular Contributor. + +**1.4. “Covered Software”** + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +**1.5. “Incompatible With Secondary Licenses”** + means + +* **(a)** that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or +* **(b)** that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +**1.6. “Executable Form”** + means any form of the work other than Source Code Form. + +**1.7. “Larger Work”** + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +**1.8. “License”** + means this document. + +**1.9. “Licensable”** + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +**1.10. “Modifications”** + means any of the following: + +* **(a)** any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or +* **(b)** any new file in Source Code Form that contains any Covered + Software. + +**1.11. “Patent Claims” of a Contributor** + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +**1.12. “Secondary License”** + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +**1.13. “Source Code Form”** + means the form of the work preferred for making modifications. + +**1.14. “You” (or “Your”)** + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, “control” means **(a)** the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or **(b)** ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + + +### 2. License Grants and Conditions + +#### 2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +* **(a)** under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and +* **(b)** under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +#### 2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +#### 2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +* **(a)** for any code that a Contributor has removed from Covered Software; + or +* **(b)** for infringements caused by: **(i)** Your and any other third party's + modifications of Covered Software, or **(ii)** the combination of its + Contributions with other software (except as part of its Contributor + Version); or +* **(c)** under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +#### 2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +#### 2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +#### 2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +#### 2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + + +### 3. Responsibilities + +#### 3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +#### 3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +* **(a)** such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +* **(b)** You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +#### 3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +#### 3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +#### 3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + + +### 4. Inability to Comply Due to Statute or Regulation + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: **(a)** comply with +the terms of this License to the maximum extent possible; and **(b)** +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + + +### 5. Termination + +**5.1.** The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated **(a)** provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and **(b)** on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +**5.2.** If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +**5.3.** In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + + +### 6. Disclaimer of Warranty + +> Covered Software is provided under this License on an “as is” +> basis, without warranty of any kind, either expressed, implied, or +> statutory, including, without limitation, warranties that the +> Covered Software is free of defects, merchantable, fit for a +> particular purpose or non-infringing. The entire risk as to the +> quality and performance of the Covered Software is with You. +> Should any Covered Software prove defective in any respect, You +> (not any Contributor) assume the cost of any necessary servicing, +> repair, or correction. This disclaimer of warranty constitutes an +> essential part of this License. No use of any Covered Software is +> authorized under this License except under this disclaimer. + +### 7. Limitation of Liability + +> Under no circumstances and under no legal theory, whether tort +> (including negligence), contract, or otherwise, shall any +> Contributor, or anyone who distributes Covered Software as +> permitted above, be liable to You for any direct, indirect, +> special, incidental, or consequential damages of any character +> including, without limitation, damages for lost profits, loss of +> goodwill, work stoppage, computer failure or malfunction, or any +> and all other commercial damages or losses, even if such party +> shall have been informed of the possibility of such damages. This +> limitation of liability shall not apply to liability for death or +> personal injury resulting from such party's negligence to the +> extent applicable law prohibits such limitation. Some +> jurisdictions do not allow the exclusion or limitation of +> incidental or consequential damages, so this exclusion and +> limitation may not apply to You. + + +### 8. Litigation + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + + +### 9. Miscellaneous + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + + +### 10. Versions of the License + +#### 10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +#### 10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +#### 10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +#### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +## Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +## Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/licenses/third-party/bsd-2-clause-patent_edk2.md b/licenses/third-party/bsd-2-clause-patent_edk2.md new file mode 100644 index 0000000..130d11c --- /dev/null +++ b/licenses/third-party/bsd-2-clause-patent_edk2.md @@ -0,0 +1,51 @@ +Copyright (c) 2019, TianoCore and contributors. All rights reserved. + +SPDX-License-Identifier: BSD-2-Clause-Patent + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +Subject to the terms and conditions of this license, each copyright holder +and contributor hereby grants to those receiving rights under this license +a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except for failure to satisfy the conditions of this license) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer this software, where such license applies only to those patent +claims, already acquired or hereafter acquired, licensable by such copyright +holder or contributor that are necessarily infringed by: + +(a) their Contribution(s) (the licensed copyrights of copyright holders and + non-copyrightable additions of contributors, in source or binary form) + alone; or + +(b) combination of their Contribution(s) with the work of authorship to + which such Contribution(s) was added by such copyright holder or + contributor, if, at the time the Contribution is added, such addition + causes such combination to be necessarily infringed. The patent license + shall not apply to any other combinations which include the + Contribution. + +Except as expressly stated above, no rights or licenses from any copyright +holder or contributor is granted under this license, whether expressly, by +implication, estoppel or otherwise. + +DISCLAIMER + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/third-party/cc0-1.0_argon2.md b/licenses/third-party/cc0-1.0_argon2.md new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/licenses/third-party/cc0-1.0_argon2.md @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/licenses/third-party/mit-0_sphincsplus.md b/licenses/third-party/mit-0_sphincsplus.md new file mode 100644 index 0000000..8532ac5 --- /dev/null +++ b/licenses/third-party/mit-0_sphincsplus.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Markku-Juhani O. Saarinen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/mit_elfio.md b/licenses/third-party/mit_elfio.md new file mode 100644 index 0000000..c6badc6 --- /dev/null +++ b/licenses/third-party/mit_elfio.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/third-party/mit_tiny_sha3.md b/licenses/third-party/mit_tiny_sha3.md new file mode 100644 index 0000000..8532ac5 --- /dev/null +++ b/licenses/third-party/mit_tiny_sha3.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Markku-Juhani O. Saarinen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..fbf8f74 --- /dev/null +++ b/readme.md @@ -0,0 +1,19 @@ +# Vystem + +The Vystem project aim to designs, develops and provides a stable operating system based on the following principles: +- built completely from scratch: no code from others repositories that run in the runtime environnement (starting at kernel boot) +- with his own hybrid kernel, bootloader, userspace, all designed from scratch to fit perfectly together +- using post-quantum cryptography everywhere it's pertinent to use it +- with a very high emphasize on security, integrity and auditability +- featuring his own design for filesystems, fonts, memory allocator, and various others components + +Please note that the project has just begun and it's still in very early developpement stage. + +## Quick links + +The documentation of every components already designed and implemented can be found [here](docs/index.md). + +Here is somes quick links: +- [Licenses and contribute guide](docs/licenses.md) +- [Build process](docs/build.md) +- [Roadmap](docs/roadmap.md) diff --git a/shelter/lib/include/cpu/asm.h b/shelter/lib/include/cpu/asm.h new file mode 100644 index 0000000..23a064a --- /dev/null +++ b/shelter/lib/include/cpu/asm.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_ASM_H +#define SH_LIB_ASM_H +#include "std/type.h" +#include "std/status.h" +// inb instruction wrapper +static inline sh_uint8 sh_asm_inb(sh_uint16 port) { + sh_uint8 val; + __asm__ volatile ("inb %1, %0":"=a"(val):"Nd"(port)); + return val; +} +// outb instruction wrapper +static inline void sh_asm_outb(sh_uint16 port,sh_uint8 val) { + __asm__ volatile ("outb %0, %1"::"a"(val),"Nd"(port)); +} +// rdtsc instruction wrapper +static inline sh_uint64 sh_asm_rdtsc() { + sh_uint32 lo,hi; + __asm__ volatile ("rdtsc":"=a"(lo),"=d"(hi)); + return ((sh_uint64)hi<<32)|lo; +} +// invlpg instruction wrapper +static inline void sh_asm_invlpg(void *addr) { + __asm__ volatile ("invlpg (%0)"::"r"(addr):"memory"); +} +#endif diff --git a/shelter/lib/include/cpu/serial.h b/shelter/lib/include/cpu/serial.h new file mode 100644 index 0000000..06aec50 --- /dev/null +++ b/shelter/lib/include/cpu/serial.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_SERIAL_H +#define SH_LIB_SERIAL_H +#include "std/type.h" +#include "std/status.h" +#define SH_SERIAL_PORT_COM1 0x3F8 +// Load serial port setting +void sh_serial_load_serial_port_setting(sh_bool is_disabled); +// Send safely (wait for (SH_SERIAL_PORT_COM1+5) & 0x20 to be at 1) a byte +void sh_serial_send_byte(sh_uint8 b); +#endif diff --git a/shelter/lib/include/cpu/tsc.h b/shelter/lib/include/cpu/tsc.h new file mode 100644 index 0000000..0e57e61 --- /dev/null +++ b/shelter/lib/include/cpu/tsc.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_TSC_H +#define SH_LIB_TSC_H +#include "std/type.h" +#include "std/status.h" +#include "cpu/asm.h" +typedef sh_uint64 sh_tsc_TSC_VALUE; +// Reas TSC register. +static inline sh_tsc_TSC_VALUE sh_tsc_read_tsc() { + return sh_asm_rdtsc(); +} +// Init kernel start tsc. Intended for single use only. +SH_STATUS sh_tsc_init_tsc(); +// Return kernel start tsc. +sh_tsc_TSC_VALUE sh_tsc_get_kernel_init_tsc(); +// Return kernel current tsc. +sh_tsc_TSC_VALUE sh_tsc_get_kernel_current_tsc(); +#endif diff --git a/shelter/lib/include/kernel/conf.h b/shelter/lib/include/kernel/conf.h new file mode 100644 index 0000000..5757bf4 --- /dev/null +++ b/shelter/lib/include/kernel/conf.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_CONF_H +#define SH_LIB_CONF_H +#include "std/type.h" +#include "std/status.h" +#include "memory/page.h" +#include "memory/vmem_layout.h" +#define SH_CONF_BOOT_CONFIG_VA SH_VMEM_LAYOUT_BOOT_CONFIG_VA +// Boot config structure. +typedef struct __attribute__((aligned(8))) { + sh_uint8 sig_start[8]; + sh_uint8 log_level; + sh_uint16 page_table_allocator_level; + sh_page_PHYSICAL_ADDRESS page_table_pool_pa; + sh_page_VIRTUAL_ADDRESS page_table_pool_va; + sh_bool test_benchmark; + sh_uint64 bench_iterations; + sh_bool log_disable_serial_port; + sh_bool disable_serial_port; + sh_uint16 log_ring_size; + sh_uint8 sig_end[8]; +} sh_conf_BOOT_CONFIG; +// Check and create boot config structure. +SH_STATUS sh_conf_get_boot_config(sh_conf_BOOT_CONFIG **config); +#endif diff --git a/shelter/lib/include/kernel/log.h b/shelter/lib/include/kernel/log.h new file mode 100644 index 0000000..5755dae --- /dev/null +++ b/shelter/lib/include/kernel/log.h @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_LOG_H +#define SH_LIB_LOG_H +#include "std/type.h" +#include "std/status.h" +#include "cpu/tsc.h" +typedef sh_uint8 sh_log_OUTPUT_TYPE; +#define SH_LOG_DEBUG ((sh_log_OUTPUT_TYPE)0) +#define SH_LOG_LOG ((sh_log_OUTPUT_TYPE)1) +#define SH_LOG_WARNING ((sh_log_OUTPUT_TYPE)2) +#define SH_LOG_ERROR ((sh_log_OUTPUT_TYPE)3) +#define SH_LOG_CRITICAL ((sh_log_OUTPUT_TYPE)4) +#define SH_LOG_FATAL ((sh_log_OUTPUT_TYPE)5) +#define SH_LOG_TEST ((sh_log_OUTPUT_TYPE)6) +typedef sh_uint16 sh_log_OUTPUT_SOURCE; +#define SH_LOG_SOURCE_MAIN ((sh_log_OUTPUT_SOURCE)0) +#define SH_LOG_SOURCE_CONF ((sh_log_OUTPUT_SOURCE)1) +#define SH_LOG_SOURCE_PAGE ((sh_log_OUTPUT_SOURCE)2) +#define SH_LOG_SOURCE_SLAB ((sh_log_OUTPUT_SOURCE)3) +#define SH_LOG_SOURCE_TEST ((sh_log_OUTPUT_SOURCE)4) +#define SH_LOG_SOURCE_PEZ ((sh_log_OUTPUT_SOURCE)5) +#define SH_LOG_SOURCE_PBA ((sh_log_OUTPUT_SOURCE)6) +#define SH_LOG_SOURCE_HEAP ((sh_log_OUTPUT_SOURCE)7) +#define SH_LOG_SOURCE_STD ((sh_log_OUTPUT_SOURCE)8) +typedef struct { + sh_log_OUTPUT_TYPE output_type; + sh_log_OUTPUT_SOURCE output_source; + sh_tsc_TSC_VALUE tsc_value; + const char* message_pointer; +} sh_log_OUTPUT_PAYLOAD; +// Return SH_TRUE if provided output type is valid. +static inline sh_bool sh_log_output_type_valid(sh_log_OUTPUT_TYPE t) { + return t<=SH_LOG_TEST; +} +// Return SH_TRUE if provided source type is valid. +static inline sh_bool sh_log_output_source_valid(sh_log_OUTPUT_SOURCE s) { + return s<=SH_LOG_SOURCE_STD; +} +// Load serial logging setting +void sh_log_load_serial_setting(sh_bool is_disabled); +// Return current serial logging setting +sh_bool sh_log_get_serial_setting(); +// Load logging ring size in pages +void sh_log_load_logging_ring_size(sh_uint16 pages_count); +// Return logging ring size in bytes +sh_uint32 sh_log_get_logging_ring_size(); +// Return total bytes written to the ring buffer +sh_uint64 sh_log_get_total_bytes_written(); +// Log a byte +void sh_log_byte(sh_uint8 byte); +// Log a string +SH_STATUS sh_log_string(const char* str); +// Log an sh_int8 encoded in decimal +SH_STATUS sh_log_int8(sh_int8 n); +// Log an sh_int16 encoded in decimal +SH_STATUS sh_log_int16(sh_int16 n); +// Log an sh_int32 encoded in decimal +SH_STATUS sh_log_int32(sh_int32 n); +// Log an sh_int64 encoded in decimal +SH_STATUS sh_log_int64(sh_int64 n); +// Log an sh_uint8 encoded in decimal +SH_STATUS sh_log_uint8(sh_uint8 n); +// Log an sh_uint16 encoded in decimal +SH_STATUS sh_log_uint16(sh_uint16 n); +// Log an sh_uint32 encoded in decimal +SH_STATUS sh_log_uint32(sh_uint32 n); +// Log an sh_uint64 encoded in decimal +SH_STATUS sh_log_uint64(sh_uint64 n); +// Log an sh_uint8 encoded in hexadecimal +SH_STATUS sh_log_uint8_hex(sh_uint8 n); +// Log an sh_uint16 encoded in hexadecimal +SH_STATUS sh_log_uint16_hex(sh_uint16 n); +// Log an sh_uint32 encoded in hexadecimal +SH_STATUS sh_log_uint32_hex(sh_uint32 n); +// Log an sh_uint64 encoded in hexadecimal +SH_STATUS sh_log_uint64_hex(sh_uint64 n); +// Log an sh_uint64 encoded in hexadecimal without trimming useless zeros +SH_STATUS sh_log_uint64_hex_fixed(sh_uint64 n); +// Log a double encoded in decimal +SH_STATUS sh_log_double(double value); +// Format a string using the following format string +SH_STATUS sh_log_format(const char* format,va_list args); +// All logs functions starting below take into account log_level. +// Send an output payload to logging system. +SH_STATUS sh_log_payload(sh_log_OUTPUT_PAYLOAD *payload); +// Send an output payload to logging system, format it before sending it. +SH_STATUS sh_log_payload_format(sh_log_OUTPUT_PAYLOAD *payload,va_list args); +// Change log level. Intended for single use only. +SH_STATUS sh_log_load_log_level(sh_uint8 log_level); +// Return log level. +sh_uint8 sh_log_get_log_level(); +// Print a string to test channel. +SH_STATUS sh_log_test(const char* str); +// Print a string to debug channel, from provided source. +SH_STATUS sh_log_debug(const char* str,sh_log_OUTPUT_SOURCE source); +// Print a string to log channel, from provided source. +SH_STATUS sh_log_log(const char* str,sh_log_OUTPUT_SOURCE source); +// Print a string to warning channel, from provided source. +SH_STATUS sh_log_warning(const char* str,sh_log_OUTPUT_SOURCE source); +// Print a string to error channel, from provided source. +SH_STATUS sh_log_error(const char* str,sh_log_OUTPUT_SOURCE source); +// Print a string to critical channel, from provided source. +SH_STATUS sh_log_critical(const char* str,sh_log_OUTPUT_SOURCE source); +// Print a string to fatal channel, from provided source. +SH_STATUS sh_log_fatal(const char* str,sh_log_OUTPUT_SOURCE source); +// Print a string to test channel. Doesn't include the new line caracter. +SH_STATUS sh_log_ltest(const char* str); +// Print a string to debug channel, from provided source. Doesn't include the new line caracter. +SH_STATUS sh_log_ldebug(const char* str,sh_log_OUTPUT_SOURCE source); +// Print a string to log channel, from provided source. Doesn't include the new line caracter. +SH_STATUS sh_log_llog(const char* str,sh_log_OUTPUT_SOURCE source); +// Print a string to warning channel, from provided source. Doesn't include the new line caracter. +SH_STATUS sh_log_lwarning(const char* str,sh_log_OUTPUT_SOURCE source); +// Print a string to error channel, from provided source. Doesn't include the new line caracter. +SH_STATUS sh_log_lerror(const char* str,sh_log_OUTPUT_SOURCE source); +// Print a string to critical channel, from provided source. Doesn't include the new line caracter. +SH_STATUS sh_log_lcritical(const char* str,sh_log_OUTPUT_SOURCE source); +// Print a string to fatal channel, from provided source. Doesn't include the new line caracter. +SH_STATUS sh_log_lfatal(const char* str,sh_log_OUTPUT_SOURCE source); +// The following functions format the output before logging it. The syntax is as follow: +// - %s : char* +// - %d : double +// - %x : sh_uint64 outputed in hexadecimal without trimming useless zeros +// - %c : char +// - %% : just insert a % +// The syntax for formating integers is as follow : %[1|2|4|8][u|s|U] : +// - the number indicate the size in bytes of the integer +// - the letter indicate the formatting mode : u for unsigned, s for signed, U for hexadecimal unsigned +// Somes examples for integers : %1s (8 bits signed integer), %4u (32 bits unsigned integers), %8U (64 bits hexadecimal unsigned) +// Print a string to test channel. Format it before hand +SH_STATUS sh_log_ftest(const char* format,...); +// Print a string to debug channel, from provided source. Format it before hand +SH_STATUS sh_log_fdebug(const sh_log_OUTPUT_SOURCE source,const char* format,...); +// Print a string to log channel, from provided source. Format it before hand +SH_STATUS sh_log_flog(const sh_log_OUTPUT_SOURCE source,const char* format,...); +// Print a string to warning channel, from provided source. Format it before hand +SH_STATUS sh_log_fwarning(const sh_log_OUTPUT_SOURCE source,const char* format,...); +// Print a string to error channel, from provided source. Format it before hand +SH_STATUS sh_log_ferror(const sh_log_OUTPUT_SOURCE source,const char* format,...); +// Print a string to critical channel, from provided source. Format it before hand +SH_STATUS sh_log_fcritical(const sh_log_OUTPUT_SOURCE source,const char* format,...); +// Print a string to fatal channel, from provided source. Format it before hand +SH_STATUS sh_log_ffatal(const sh_log_OUTPUT_SOURCE source,const char* format,...); +// Print every information about memory statictics. Require log level to be 1 or 0 +SH_STATUS sh_log_mem_stats(sh_log_OUTPUT_SOURCE source); +#endif diff --git a/shelter/lib/include/kernel/test.h b/shelter/lib/include/kernel/test.h new file mode 100644 index 0000000..50a8253 --- /dev/null +++ b/shelter/lib/include/kernel/test.h @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MPL-2.0 +// This file serve the purpose of including all types of tests +#include "kernel/tests/test_slabs.h" +#include "kernel/tests/test_radix.h" +#include "kernel/tests/test_pez.h" +#include "kernel/tests/test_malloc.h" +#include "kernel/tests/test_utils.h" diff --git a/shelter/lib/include/kernel/tests/test_malloc.h b/shelter/lib/include/kernel/tests/test_malloc.h new file mode 100644 index 0000000..e43a240 --- /dev/null +++ b/shelter/lib/include/kernel/tests/test_malloc.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_TEST_MALLOC_H +#define SH_LIB_TEST_MALLOC_H +#include "std/type.h" +#include "std/status.h" +#include "cpu/tsc.h" +// Test and benchmark malloc subsystem +SH_STATUS sh_test_malloc_benchmark(); +#endif diff --git a/shelter/lib/include/kernel/tests/test_pez.h b/shelter/lib/include/kernel/tests/test_pez.h new file mode 100644 index 0000000..41f5a72 --- /dev/null +++ b/shelter/lib/include/kernel/tests/test_pez.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_TEST_PEZ_H +#define SH_LIB_TEST_PEZ_H +#include "std/type.h" +#include "std/status.h" +#include "memory/pez/pez.h" +#include "cpu/tsc.h" +#define SH_TEST_PEZ_TOTAL_TSC_TIME_COUNT 4000 +// Test and benchmark pez subsystem +SH_STATUS sh_test_pez_benchmark_physical(sh_pez_PHYSICAL_PLANE *phys_plane); +#endif diff --git a/shelter/lib/include/kernel/tests/test_radix.h b/shelter/lib/include/kernel/tests/test_radix.h new file mode 100644 index 0000000..34a4c28 --- /dev/null +++ b/shelter/lib/include/kernel/tests/test_radix.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_TEST_RADIX_H +#define SH_LIB_TEST_RADIX_H +#include "std/type.h" +#include "std/status.h" +#include "memory/page.h" +#include "memory/slab.h" +#include "memory/pez/radix.h" +#include "cpu/tsc.h" +// Load the number of iterations for benchmakrs for radix test. +void sh_test_radix_load_iterations_count(sh_uint64 iterations_num); +// Test and benchmark radix trees subsystem +SH_STATUS sh_test_radix_benchmark(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp); +#endif diff --git a/shelter/lib/include/kernel/tests/test_slabs.h b/shelter/lib/include/kernel/tests/test_slabs.h new file mode 100644 index 0000000..dac1f37 --- /dev/null +++ b/shelter/lib/include/kernel/tests/test_slabs.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_TEST_SLABS_H +#define SH_LIB_TEST_SLABS_H +#include "std/type.h" +#include "std/status.h" +#include "memory/page.h" +#include "memory/slab.h" +#include "cpu/tsc.h" +// Load the number of iterations for benchmakrs for slabs test. +void sh_test_slabs_load_iterations_count(sh_uint64 iterations_num); +// Test and benchmark physical region objects slab allocator +SH_STATUS sh_test_slabs_benchmark_region_physical(sh_slab_reg_phys_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp); +// Test and benchmark virtual region objects slab allocator +SH_STATUS sh_test_slabs_benchmark_region_virtual(sh_slab_reg_virt_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp); +// Test and benchmark radix nodes objects slab allocator +SH_STATUS sh_test_slabs_benchmark_radix_node(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp); +// Test and benchmark all slab allocators +SH_STATUS sh_test_slabs_benchmark(sh_slab_reg_phys_SLAB_ALLOCATOR *alloc_reg_phys,sh_slab_reg_virt_SLAB_ALLOCATOR *alloc_reg_virt,struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc_radix_node,sh_page_PAGE_TABLE_POOL *ptp); +#endif diff --git a/shelter/lib/include/kernel/tests/test_utils.h b/shelter/lib/include/kernel/tests/test_utils.h new file mode 100644 index 0000000..e5a8ef9 --- /dev/null +++ b/shelter/lib/include/kernel/tests/test_utils.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_TEST_UTILS_H +#define SH_LIB_TEST_UTILS_H +#include "std/type.h" +#include "std/status.h" +#include "cpu/tsc.h" +// Compute and print time stats based on provided list of TSC values. +SH_STATUS sh_test_compute_print_stats(char* benchname,sh_tsc_TSC_VALUE *tsc_value_array,sh_uint64 array_size); +// Return pointer to TSC values buffer +sh_tsc_TSC_VALUE* sh_test_get_tsc_values_buffer_ptr(); +// Load the number of iterations for benchmarks. +void sh_test_load_iterations_count(sh_uint64 iterations_num); +#endif diff --git a/shelter/lib/include/memory/heap.h b/shelter/lib/include/memory/heap.h new file mode 100644 index 0000000..d468c33 --- /dev/null +++ b/shelter/lib/include/memory/heap.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_HEAP_H +#define SH_LIB_HEAP_H +#include "memory/page.h" +#include "memory/pba.h" +#include "memory/pez/pez.h" +#include "memory/pez/radix.h" +#include "memory/slabs/slab_generic.h" +#include "std/status.h" +#include "std/type.h" +// Heap structure +typedef struct { + sh_pez_PHYSICAL_PLANE *phys_plane; + sh_pez_VIRTUAL_PLANE *virt_plane; + sh_page_PAGE_TABLE_POOL *kernel_ptp; + struct sh_slab_generic_SLAB_ALLOCATOR slabs_allocator[8]; + sh_pba_PAGE_BLOCK_ALLOCATOR pba[8]; + sh_radix_TREE alloc_size_tree; +} sh_heap_KERNEL_HEAP; +// Return default kernel heap +sh_heap_KERNEL_HEAP *sh_heap_get_default_heap(); +// Load default kernel heap +void sh_heap_load_default_heap(sh_heap_KERNEL_HEAP *heap); +// Initialize heap structure, calling entity need to input manually all slabs allocator. +SH_STATUS sh_heap_init_heap(sh_pez_PHYSICAL_PLANE *phys_plane,sh_pez_VIRTUAL_PLANE *virt_plane,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_heap_KERNEL_HEAP *kernel_heap); +// Allocate a certain amount of pages from Pez physical backend +SH_STATUS sh_heap_allocate_pages(sh_uint32 pages_count,sh_page_VIRTUAL_ADDRESS *address); +// Free a allocated region of pages from the heap +SH_STATUS sh_heap_free_pages(sh_page_VIRTUAL_ADDRESS va); +// Allocate a certain object based on his size +SH_STATUS sh_heap_allocate_object(sh_uint32 size_bytes,sh_page_VIRTUAL_ADDRESS *address); +// Free a certain object based on his size +SH_STATUS sh_heap_free_object(sh_page_VIRTUAL_ADDRESS va); +#endif diff --git a/shelter/lib/include/memory/page.h b/shelter/lib/include/memory/page.h new file mode 100644 index 0000000..23b8b6a --- /dev/null +++ b/shelter/lib/include/memory/page.h @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_PAGE_H +#define SH_LIB_PAGE_H +#include "std/type.h" +#include "std/status.h" +#include "memory/vmem_layout.h" +#define SH_PAGE_KERNEL_PERM_VA_BASE SH_VMEM_LAYOUT_KERNEL_ALLOC_SPACE_VA +#define SH_PAGE_KERNEL_PERM_VA_END SH_VMEM_LAYOUT_KERNEL_ALLOC_SPACE_VA_END +#define SH_PAGE_MEMORY_MAP_VA SH_VMEM_LAYOUT_MEMORY_MAP_VA +#define SH_PAGE_SIZE 4096 +#define SH_PAGE_MAX_MEM_COUNT 16ULL*1024*1024*1024*1024 +#define SH_PAGE_MAX_PAGES_COUNT (SH_PAGE_MAX_MEM_COUNT/SH_PAGE_SIZE) +#define SH_PAGE_PTP_ALLOCATOR_PAGES_COUNT 4096 +#define SH_PAGE_PTP_ALLOCATOR_BITMAP_UINT64 SH_PAGE_PTP_ALLOCATOR_PAGES_COUNT/64 +typedef sh_uint32 sh_page_MEMORY_TYPE; +#define SH_PAGE_RESERVED_MEMORY_TYPE (sh_page_MEMORY_TYPE)0 +#define SH_PAGE_LOADER_CODE (sh_page_MEMORY_TYPE)1 +#define SH_PAGE_LOADER_DATA (sh_page_MEMORY_TYPE)2 +#define SH_PAGE_BOOT_SERVICES_CODE (sh_page_MEMORY_TYPE)3 +#define SH_PAGE_BOOT_SERVICES_DATA (sh_page_MEMORY_TYPE)4 +#define SH_PAGE_RUNTIME_SERVICES_CODE (sh_page_MEMORY_TYPE)5 +#define SH_PAGE_RUNTIME_SERVICES_DATA (sh_page_MEMORY_TYPE)6 +#define SH_PAGE_CONVENTIONAL_MEMORY (sh_page_MEMORY_TYPE)7 +#define SH_PAGE_UNUSABLE_MEMORY (sh_page_MEMORY_TYPE)8 +#define SH_PAGE_ACPI_RECLAIM_MEMORY (sh_page_MEMORY_TYPE)9 +#define SH_PAGE_ACPI_MEMORY_NVS (sh_page_MEMORY_TYPE)10 +#define SH_PAGE_MEMORY_MAPPED_IO (sh_page_MEMORY_TYPE)11 +#define SH_PAGE_MEMORY_MAPPED_IO_PORT_SPACE (sh_page_MEMORY_TYPE)12 +#define SH_PAGE_PAL_CODE (sh_page_MEMORY_TYPE)13 +#define SH_PAGE_PERSISTENT_MEMORY (sh_page_MEMORY_TYPE)14 +#define SH_PAGE_RESERVED (sh_page_MEMORY_TYPE)15 +typedef sh_uint64 sh_page_PHYSICAL_ADDRESS; +typedef sh_uint64 sh_page_VIRTUAL_ADDRESS; +#define SH_PAGE_NULL_PA (sh_page_PHYSICAL_ADDRESS)0 +#define SH_PAGE_NULL_VA (sh_page_VIRTUAL_ADDRESS)0 +#define SH_PAGE_PRESENT (1ULL<<0) +#define SH_PAGE_TABLE_FLAGS (1ULL<<1) +#define SH_PAGE_RW (1ULL<<1) +#define SH_PAGE_US (1ULL<<2) +#define SH_PAGE_PWT (1ULL<<3) +#define SH_PAGE_PCD (1ULL<<4) +#define SH_PAGE_ACCESSED (1ULL<<5) +#define SH_PAGE_DIRTY (1ULL<<6) +#define SH_PAGE_PS (1ULL<<7) +#define SH_PAGE_GLOBAL (1ULL<<8) +#define SH_PAGE_NX (1ULL<<63) +// Memory map entry structure. +#pragma pack(1) +typedef struct { + sh_uint32 type; + sh_uint64 physical_start; + sh_uint64 pages_count; + sh_uint64 attributes; +} sh_page_MEMORY_MAP_ENTRY; +#pragma pack() +// Memory map header structure. +#pragma pack(1) +typedef struct { + sh_uint8 sig_start[8]; + sh_uint64 entry_count; + sh_uint64 entry_size; + sh_uint8 mmap_syntax_version; +} sh_page_MEMORY_MAP_HEADER; +#pragma pack() +// Page table pool structure. +#pragma pack(1) +typedef struct { + sh_page_PHYSICAL_ADDRESS page_table_pa; + sh_page_VIRTUAL_ADDRESS page_table_va; + sh_uint64 ptp_alloc_bitmap[SH_PAGE_PTP_ALLOCATOR_BITMAP_UINT64]; + sh_uint64 ptp_pages_count; + sh_uint64 ptp_alloc_bitmap_uint64_count; +} sh_page_PAGE_TABLE_POOL; +#pragma pack() +// Memory statistics structure +typedef struct { + sh_uint64 memory_total_pages; // memory_total is the size of the addressable physical space + sh_uint64 memory_total_bytes; + sh_uint64 memory_installed_pages; // memory_installed is the sum of the size of all free regions at kernel boot + sh_uint64 memory_installed_bytes; + sh_uint64 free_pages; + sh_uint64 used_pages; + double free_ratio; + double used_ratio; + sh_uint64 largest_free_block; + sh_uint64 largest_used_block; + sh_uint64 free_blocks_count; + sh_uint64 used_blocks_count; + sh_uint64 physical_bitmap_size_pages; + sh_uint64 physical_bitmap_size_bytes; +} sh_page_MEM_STATS; +// Load boot PTP VA. Intended for one usage only. +SH_STATUS sh_page_load_boot_ptp_va(sh_page_VIRTUAL_ADDRESS pt_pool_va); +// Return boot PTP VA. +sh_page_VIRTUAL_ADDRESS sh_page_get_boot_ptp_va(); +// Copy memory map provided bootloader into dedicated buffer. Intended for one usage only. +SH_STATUS sh_page_copy_memory_map(); +// Check for memory map signatures and read memory map header. +SH_STATUS sh_page_check_memory_map(); +// Return the amount of physical memory in pages +sh_uint64 sh_page_get_physical_memory_amount_pages(); +// Return the amount of physical memory in bytes +sh_uint64 sh_page_get_physical_memory_amount_bytes(); +// Return the first available physical page in physical bitmap. +sh_uint64 sh_page_get_one_page_na(); +// Set pages ranges into provided bitmap. +SH_STATUS sh_page_set_pages_range_bitmap(sh_uint8 *bitmap,sh_uint64 page_count_in_bitmap,sh_uint64 page_index,sh_uint64 page_count,sh_bool state); +// Return the status of a page inside the provided bitmap. +static inline sh_bool sh_page_is_allocated(sh_uint8 *bitmap,sh_uint64 page_index) { + sh_uint64 byte_index=page_index/8; + sh_uint8 bit_index=page_index%8; + return (bitmap[byte_index] & (1u<page_table_pa; + sh_uint64 size=ptp->ptp_pages_count*4096; + if (pa=base+size) return 0; + return (sh_uint64 *)(ptp->page_table_va+(pa-base)); +} +// Map one physical page to VA to provided PTP. +SH_STATUS sh_page_map_one_page_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_page_PHYSICAL_ADDRESS pa,sh_uint64 flags); +// Return according SH_STATUS if provided VA is mapped inside provided PTP. +SH_STATUS sh_page_is_va_mapped_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va); +// Return according SH_STATUS if provided VA is mapped inside provided PTP for all the range provided +SH_STATUS sh_page_is_va_range_mapped_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes); +// Search for an available amount of virtual memory inside provided range with the provided size +SH_STATUS sh_page_search_available_va_range(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS range_base,sh_page_VIRTUAL_ADDRESS range_size_bytes,sh_uint64 size_bytes,sh_page_VIRTUAL_ADDRESS *address_found); +// Map a range of pages from PA to VA. Both virtual and physic area has to be continuous. VAs availability is checked, not physical pages availability. +SH_STATUS sh_page_map_contiguous_pages_range_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_page_PHYSICAL_ADDRESS pa,sh_uint64 flags,sh_uint64 size_bytes); +// Search for an available amount of pages inside physical bitmap +SH_STATUS sh_page_search_physical_contiguous_block_na(sh_uint64 pages_needed,sh_page_PHYSICAL_ADDRESS *pa); +// Allocate the corresponding amount of pages to size_bytes. +SH_STATUS sh_page_alloc_contiguous(sh_page_PAGE_TABLE_POOL *ptp,sh_uint64 size_bytes,sh_page_VIRTUAL_ADDRESS* va); +// Allocate the corresponding amount of pages to size_bytes. Provide support for custom flags and VA range search. +SH_STATUS sh_page_alloc_contiguous_extended(sh_page_PAGE_TABLE_POOL *ptp,sh_uint64 size_bytes,sh_page_VIRTUAL_ADDRESS* va,DEFAULT sh_uint64 flags,DEFAULT sh_page_VIRTUAL_ADDRESS va_range_start,DEFAULT sh_uint64 va_range_size_bytes); +// Unmap one page from a PTP, assume VA is mapped. Does not unallocate associed physical page +SH_STATUS sh_page_unmap_one_page_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va); +// Map a range of pages to VA. Both virtual and physic area has to be continuous. VAs mapping is checked, not physical pages occupation. +SH_STATUS sh_page_unmap_contiguous_pages_range_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes); +// Convert a VA allocated in a PTP into his equivalent PA by searching inside the PTP +SH_STATUS sh_page_ptp_va_to_pa(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_page_PHYSICAL_ADDRESS *pa); +// Unalloc one page from a VA. Check if VA is mapped or not. PA is calculted trough searching in PTP +SH_STATUS sh_page_unalloc_one_page(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va); +// Unalloc a range of virtually contiguous pages. Check if the entire range is allocated before unallocating anything. +SH_STATUS sh_page_unalloc_contiguous(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes); +// Parse memory map, set all non usable pages into physical bitmap and compute available amount of physical memory. Intended for one usage only. +SH_STATUS sh_page_analyse_memory_map(sh_page_PAGE_TABLE_POOL *ptp); +// Return physical bitmap pointer +sh_page_VIRTUAL_ADDRESS sh_page_get_physical_bitmap_ptr(); +// Get physical memory statistics +SH_STATUS sh_page_get_memory_stats(sh_page_MEM_STATS *mem_stats); +#endif diff --git a/shelter/lib/include/memory/pba.h b/shelter/lib/include/memory/pba.h new file mode 100644 index 0000000..6299325 --- /dev/null +++ b/shelter/lib/include/memory/pba.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_PBA_H +#define SH_LIB_PBA_H +#include "std/type.h" +#include "std/status.h" +#include "memory/page.h" +#include "memory/pez/pez.h" +// Page block allocator +typedef struct { + sh_page_VIRTUAL_ADDRESS start_va; + sh_uint64 total_pages; + sh_uint64 block_pages; + sh_uint64 block_count; + sh_uint64 max_blocks; +} sh_pba_PAGE_BLOCK_ALLOCATOR; +// Initialize a page block allocator +SH_STATUS sh_pba_init(sh_pba_PAGE_BLOCK_ALLOCATOR *pba,sh_page_VIRTUAL_ADDRESS start_va,sh_uint64 area_pages_amount,sh_uint64 block_pages); +// Allocate a block and return corresponding pointer +SH_STATUS sh_pba_alloc(sh_pba_PAGE_BLOCK_ALLOCATOR *pba,sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS *ptr); +#endif diff --git a/shelter/lib/include/memory/pez/pez.h b/shelter/lib/include/memory/pez/pez.h new file mode 100644 index 0000000..a112849 --- /dev/null +++ b/shelter/lib/include/memory/pez/pez.h @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_PEZ_H +#define SH_LIB_PEZ_H +#include "std/type.h" +#include "std/status.h" +#include "memory/slabs/slab_reg_phys.h" +#include "memory/slabs/slab_reg_virt.h" +#include "memory/pez/radix.h" +#define SH_PEZ_REGION_OBJECT_INDEX_SIZE_BYTES 3 +// Physical region object +#pragma pack(1) +typedef struct { + sh_uint32 start_page_index; + sh_uint32 region_size_pages; + sh_uint8 next_region_index[3]; + sh_uint8 flags; +} sh_pez_REGION_PHYSICAL_OBJECT; +#pragma pack() +// Virtual region object +#pragma pack(1) +typedef struct { + sh_uint32 start_page_index; + sh_uint32 region_size_pages; + sh_uint32 next_region_index; +} sh_pez_REGION_VIRTUAL_OBJECT; +#pragma pack() +// Physical plane structure +typedef struct { + sh_uint32 free_pages; + sh_uint32 used_pages; + sh_radix_TREE region_radix_tree; + sh_radix_TREE boundary_radix_tree; + sh_slab_reg_phys_SLAB_ALLOCATOR *slab_reg_phys; + struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node; + sh_page_PAGE_TABLE_POOL *kernel_ptp; + sh_uint8 *physical_bitmap; + sh_uint64 physical_page_count; +} sh_pez_PHYSICAL_PLANE; +// Virtual plane structure +typedef struct { + sh_uint32 free_pages; + sh_uint32 used_pages; + sh_radix_TREE region_radix_tree; + sh_radix_TREE boundary_radix_tree; + sh_slab_reg_virt_SLAB_ALLOCATOR *slab_reg_virt; + struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node; + sh_page_PAGE_TABLE_POOL *kernel_ptp; + sh_page_PAGE_TABLE_POOL *reference_ptp; + sh_page_VIRTUAL_ADDRESS plane_offset; +} sh_pez_VIRTUAL_PLANE; +// Set Pez state to true +void sh_pez_set_available(); +// Return current state of Pez +sh_bool sh_pez_is_available(); +// Return reference Pez physical plane +sh_pez_PHYSICAL_PLANE* sh_pez_get_reference_phys_plane(); +// Initialize a physical plane +SH_STATUS sh_pez_init_physical_plane(sh_uint8 *physical_bitmap,sh_uint64 physical_page_count,sh_slab_reg_phys_SLAB_ALLOCATOR *slab_reg_phys,struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_pez_PHYSICAL_PLANE *phys_plane); +// Allocate physical pages from the physical plane +SH_STATUS sh_pez_alloc_physical_pages(sh_pez_PHYSICAL_PLANE *phys_plane,sh_uint32 pages_count,sh_page_PHYSICAL_ADDRESS *address); +// Free physical pages from the physical plane +SH_STATUS sh_pez_free_physical_pages(sh_pez_PHYSICAL_PLANE *phys_plane,sh_page_PHYSICAL_ADDRESS *address,sh_uint32 pages_count); +// Debug Pez Physical +SH_STATUS sh_pez_debug_physical(sh_pez_PHYSICAL_PLANE *phys_plane); +// Initialize a virtual plane +SH_STATUS sh_pez_init_virtual_plane(sh_page_VIRTUAL_ADDRESS plane_offset,sh_slab_reg_virt_SLAB_ALLOCATOR *slab_reg_virt,struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_page_PAGE_TABLE_POOL *reference_ptp,sh_pez_VIRTUAL_PLANE *virt_plane); +// Allocate virtual space from the virtual plane +SH_STATUS sh_pez_alloc_virtual_pages(sh_pez_VIRTUAL_PLANE *virt_plane,sh_uint32 pages_count,sh_page_VIRTUAL_ADDRESS *address); +// Free virtual space from the virtual plane +SH_STATUS sh_pez_free_virtual_pages(sh_pez_VIRTUAL_PLANE *virt_plane,sh_page_VIRTUAL_ADDRESS *address,sh_uint32 pages_count); +#endif diff --git a/shelter/lib/include/memory/pez/pez_debug.h b/shelter/lib/include/memory/pez/pez_debug.h new file mode 100644 index 0000000..c89b5fa --- /dev/null +++ b/shelter/lib/include/memory/pez/pez_debug.h @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_PEZ_DEBUG_H +#define SH_LIB_PEZ_DEBUG_H +#include "std/type.h" +#include "std/status.h" +#define SH_PEZ_DEBUG_ALLOC_HEADER 0 +#define SH_PEZ_DEBUG_FREE_HEADER 1 +#define SH_PEZ_DEBUG_SIZE_LIST_HEADER 2 +#define SH_PEZ_DEBUG_BOUNDARY_RADIX_HEADER 3 +#define SH_PEZ_DEBUG_BITMAP_REGIONS_HEADER 4 +#define SH_PEZ_DEBUG_SIZE_LIST_BLOCK_HEADER 5 +// Struct for Pez alloc +#pragma pack(1) +typedef struct { + sh_uint32 size_asked; + sh_uint32 found_region_start_index; + sh_uint32 found_region_size; + sh_bool is_exact_fit; + sh_uint32 remaining_size; + sh_uint32 new_start; + sh_uint32 reg_idx; +} sh_pez_debug_ALLOC; +#pragma pack() +// Struct for Pez free +#pragma pack(1) +typedef struct { + sh_uint32 region_start_freeed; + sh_uint32 size_freeed; + sh_uint32 left_region_idx; + sh_uint32 right_region_idx; + sh_uint32 left_region_size; + sh_uint32 right_region_size; + sh_uint32 left_region_start; + sh_uint32 right_region_start; + sh_uint32 final_inserted_region_idx; + sh_uint32 final_inserted_region_start; + sh_uint32 final_inserted_region_size; + sh_bool was_right_region_object_freeed; + sh_bool was_new_region_object_allocated; + sh_uint32 new_reg_obj_start; + sh_uint32 new_reg_obj_size; +} sh_pez_debug_FREE; +#pragma pack() +// Struct for Pez regions from size list +#pragma pack(1) +typedef struct { + sh_uint32 start; + sh_uint32 size; + sh_uint32 idx; + sh_uint32 next_idx; +} sh_pez_debug_REGION_SIZE_LIST; +#pragma pack() +// Struct for Pez size list +#pragma pack(1) +typedef struct { + sh_uint32 size; + sh_uint32 count; + sh_pez_debug_REGION_SIZE_LIST regions[512]; +} sh_pez_debug_SIZE_LIST; +#pragma pack() +// Struct for Pez region from boundary +#pragma pack(1) +typedef struct { + sh_uint32 pos_start; + sh_uint32 idx_start; + sh_uint32 prev_start; + sh_uint32 pos_end; + sh_uint32 idx_end; + sh_uint32 prev_end; +} sh_pez_debug_REGION_BOUNDARY; +#pragma pack() +// Struct for Pez boundary radix +#pragma pack(1) +typedef struct { + sh_uint32 count; + sh_pez_debug_REGION_BOUNDARY regions[512]; +} sh_pez_debug_BOUNDARY_RADIX; +#pragma pack() +// Struct for Pez regions from bitmap +#pragma pack(1) +typedef struct { + sh_uint32 start; + sh_uint32 size; +} sh_pez_debug_REGION_BITMAP; +#pragma pack() +// Struct for Pez bitmap +#pragma pack(1) +typedef struct { + sh_uint32 count; + sh_pez_debug_REGION_BITMAP regions[512]; +} sh_pez_debug_BITMAP; +#pragma pack() +#endif +// Send an alloc payload +SH_STATUS sh_pez_debug_send_alloc(sh_pez_debug_ALLOC *alloc); +// Send a free payload +SH_STATUS sh_pez_debug_send_free(sh_pez_debug_FREE *free); +// Send a size list payload. Sending size list radix header must be done prior to any size list being send. It consist of SH_PEZ_DEBUG_SIZE_LIST_BLOCK_HEADER and the count of sizes list +SH_STATUS sh_pez_debug_send_size_list(sh_pez_debug_SIZE_LIST *size_list); +// Send a boundary radix payload +SH_STATUS sh_pez_debug_send_boundary_radix(sh_pez_debug_BOUNDARY_RADIX *boundary_radix); +// Send a bitmap payload +SH_STATUS sh_pez_debug_send_bitmap(sh_pez_debug_BITMAP *bitmap); diff --git a/shelter/lib/include/memory/pez/radix.h b/shelter/lib/include/memory/pez/radix.h new file mode 100644 index 0000000..f311014 --- /dev/null +++ b/shelter/lib/include/memory/pez/radix.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_RADIX_H +#define SH_LIB_RADIX_H +#include "std/type.h" +#include "memory/page.h" +struct sh_slab_radix_node_SLAB_ALLOCATOR; +#define SH_RADIX_NODE_SIZE_BYTES 128 +// Radix node structure +typedef struct { + sh_page_VIRTUAL_ADDRESS ptr[16]; +} sh_radix_NODE; +// Radix tree structure +typedef struct { + sh_radix_NODE *root_node; + sh_uint8 depth; +} sh_radix_TREE; +// Return the value at the indicated index. Return SH_STATUS_NOT_FOUND if index indicate an empty ptr. Automatically fill the 16 higher bits in the returned value if index=0, no matter that provided node is a intermediary node or a leaf +SH_STATUS sh_radix_node_read_value(sh_radix_NODE *node,sh_uint8 index,sh_page_VIRTUAL_ADDRESS* value); +// Modify the value at the indicated index. Update the bitmap accordingly, all non-zero value corresponding to 1 in the bitmap, otherwise 0. +SH_STATUS sh_radix_node_set_value(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_radix_NODE *node,sh_uint8 index,sh_page_VIRTUAL_ADDRESS value); +// Initialize a radix tree. Fail if depth is greater than 16 +SH_STATUS sh_radix_tree_init(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_radix_TREE *tree,sh_uint8 depth); +// Search in a straight line for a key inside the tree. Stop and return SH_STATUS_NOT_FOUND as soon as it hit a empty ptr where there should be one +SH_STATUS sh_radix_tree_get_value(sh_radix_TREE *tree,sh_uint64 key,sh_page_VIRTUAL_ADDRESS *value); +// Insert a value inside the tree. Can allocate new nodes if necessary. Will overwrite previous value if there was one already inserted. Automatically update bitmap on his path +SH_STATUS sh_radix_tree_insert_value(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_radix_TREE *tree,sh_uint64 key,sh_page_VIRTUAL_ADDRESS value); +// Delete a value and deallocates all nodes (including leaf) that form a path to it if this deletion make them empty +SH_STATUS sh_radix_tree_delete_value(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_radix_TREE *tree,sh_uint64 key); +// Return the value that has the smallest key equal or greater than the provided key. Can't allocate new nodes. +SH_STATUS sh_radix_tree_search_smallest_min_bound(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_radix_TREE *tree,sh_uint64 lower_bound_key,sh_page_VIRTUAL_ADDRESS *value); +#endif diff --git a/shelter/lib/include/memory/ring.h b/shelter/lib/include/memory/ring.h new file mode 100644 index 0000000..dc8115f --- /dev/null +++ b/shelter/lib/include/memory/ring.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_RING_H +#define SH_LIB_RING_H +#include "std/type.h" +#include "std/status.h" +// Ring buffer header structure +typedef struct { + sh_uint32 head; + sh_uint32 tail; + sh_uint32 buffer_bytes_size; + sh_uint8* data_start; + sh_uint64 total_bytes_written; +} sh_ring_RING_BUFFER_HEADER; +// Write a byte into the provided ring buffer +SH_STATUS sh_ring_write_byte(sh_ring_RING_BUFFER_HEADER *ring_buffer,sh_uint8 byte); +// Write a null terminated string into the provided ring buffer +SH_STATUS sh_ring_write_string(sh_ring_RING_BUFFER_HEADER *ring_buffer,char *string); +#endif diff --git a/shelter/lib/include/memory/slab.h b/shelter/lib/include/memory/slab.h new file mode 100644 index 0000000..f489183 --- /dev/null +++ b/shelter/lib/include/memory/slab.h @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MPL-2.0 +// This file serve the purpose of including all types of slabs allocators +#include "memory/slabs/slab_reg_phys.h" +#include "memory/slabs/slab_reg_virt.h" +#include "memory/slabs/slab_radix_node.h" +#include "memory/slabs/slab_generic.h" diff --git a/shelter/lib/include/memory/slabs/slab_generic.h b/shelter/lib/include/memory/slabs/slab_generic.h new file mode 100644 index 0000000..ba6b7f1 --- /dev/null +++ b/shelter/lib/include/memory/slabs/slab_generic.h @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MPL-2.0 +// This is the implementation for the slab allocator for generic sizes +#ifndef SH_LIB_SLAB_GENERIC_H +#define SH_LIB_SLAB_GENERIC_H +#include "memory/page.h" +#include "std/status.h" +#include "std/type.h" +#include "memory/pba.h" +#define SH_SLAB_GENERIC_SLAB_DATA_PAGES {1,2,4,8,16,32,64,128} +#define SH_SLAB_GENERIC_OBJECT_SIZE_BYTES {8,16,32,64,128,256,512,1024} +#define SH_SLAB_GENERIC_OBJECTS_PER_SLAB 512 +#define SH_SLAB_GENERIC_ACTUAL_OBJECTS_PER_SLAB {496,504,508,510,511,511,511,511} +#define SH_SLAB_GENERIC_SLAB_BITMAP_INIT {0xFFFF,0xFF,0xF,0x3,1,1,1,1} +#define SH_SLAB_GENERIC_SLAB_BITMAP_SIZE_BYTES 64 +#define SH_SLAB_GENERIC_NULL_REF (sh_uint64)0 +#define SH_SLAB_GENERIC_SLAB_SIG {'S','h','S','l','G','e','n','e'} +#define SH_SLAB_GENERIC_MAGIC 0x656E65476C536853ULL // little endian +// Generic slab structure +#pragma pack(1) +typedef struct sh_slab_generic_SLAB { + sh_uint8 sig[8]; + sh_uint16 used_count; + sh_uint64 slab_index; + struct sh_slab_generic_SLAB* next_slab; + struct sh_slab_generic_SLAB* prev_slab; + struct sh_slab_generic_SLAB* next_partial; + struct sh_slab_generic_SLAB* prev_partial; + sh_uint8 padding[14]; + sh_uint64 free_bitmap[SH_SLAB_GENERIC_SLAB_BITMAP_SIZE_BYTES/8]; // First objects will be mark as unavailable due to being replaced by the header and bitmap, their amount depend on the object size +} sh_slab_generic_SLAB; +#pragma pack() +// Radix node slab allocator structure +struct sh_slab_generic_SLAB_ALLOCATOR { + sh_slab_generic_SLAB* first_slab; + sh_slab_generic_SLAB* partial_head; + sh_uint64 level; + sh_uint64 slab_count; + sh_uint64 object_size_bytes; + sh_uint64 object_per_slab; + sh_uint64 actual_object_per_slab; + sh_uint64 bitmap_init; + sh_uint64 slab_pages_count; + sh_pba_PAGE_BLOCK_ALLOCATOR* pba; +}; +typedef sh_uint16 sh_slab_generic_OBJECT_INDEX_IN_SLAB; +// Initialize slab allocator structure. Does not allocate any slab +SH_STATUS sh_slab_generic_alloc_init(sh_uint8 level,struct sh_slab_generic_SLAB_ALLOCATOR* slab_alloc,sh_pba_PAGE_BLOCK_ALLOCATOR *pba); +// Allocate a new slab, initialize it and put it into the allocator. If new slab isn't the first to be allocated, push it on the partial slab list +SH_STATUS sh_slab_generic_add_slab(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_generic_SLAB** out_slab); +// Obtain a pointer to the first partial slab. Does not scan the slabs. Allocate a new slab if necessary +SH_STATUS sh_slab_generic_get_partial_slab(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL* ptp,sh_slab_generic_SLAB** found_slab); +// Rescan all the slabs to rebuild partial list in case of doubt. Does not modify alloc->slab_count, any slab->free_bitmap or any slab>->nodes +SH_STATUS sh_slab_generic_scan_slabs(struct sh_slab_generic_SLAB_ALLOCATOR* alloc); +// Return a valid pointer to an empty object slot as well as the object index in the corresponding index. Slabs allocation is automated by sh_slab_generic_get_partial_slab +SH_STATUS sh_slab_generic_find_free_object(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,void** out,sh_slab_generic_OBJECT_INDEX_IN_SLAB* index_in_slab); +// Allocate one new object. Return a pointer to the struct of the new object. Since it call sh_slab_generic_find_free_object, it can allocate new slab +SH_STATUS sh_slab_generic_alloc(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,void** out_obj); +// Dellocate one radix node object provided as pointer. +SH_STATUS sh_slab_generic_dealloc(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,void *object_ptr); +#endif diff --git a/shelter/lib/include/memory/slabs/slab_radix_node.h b/shelter/lib/include/memory/slabs/slab_radix_node.h new file mode 100644 index 0000000..04165b5 --- /dev/null +++ b/shelter/lib/include/memory/slabs/slab_radix_node.h @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MPL-2.0 +// This is the implementation for the slab allocator for sh_radix_NODE +#ifndef SH_LIB_SLAB_RADIX_NODE_H +#define SH_LIB_SLAB_RADIX_NODE_H +#include "memory/page.h" +#include "std/status.h" +#include "std/type.h" +#include "memory/pez/radix.h" +#include "memory/pba.h" +#define SH_SLAB_RADIX_NODE_SLAB_DATA_PAGES (sh_uint64)32 +#define SH_SLAB_RADIX_NODE_SLAB_DATA_SIZE_BYTES (sh_uint64)(SH_SLAB_RADIX_NODE_SLAB_DATA_PAGES*4096) +#define SH_SLAB_RADIX_NODE_OBJECT_SIZE_BYTES (sh_uint64)128 +#define SH_SLAB_RADIX_NODE_OBJECTS_PER_SLAB (sh_uint64)1024 +#define SH_SLAB_RADIX_NODE_ACTUAL_OBJECTS_PER_SLAB (sh_uint64)1006 +#define SH_SLAB_RADIX_NODE_SLAB_BITMAP_SIZE_BYTES (sh_uint64)(SH_SLAB_RADIX_NODE_OBJECTS_PER_SLAB/8) +#define SH_SLAB_RADIX_NODE_NULL_REF (sh_uint64)0 +#define SH_SLAB_RADIX_NODE_SLAB_SIG {'S','h','S','l','R','a','N','o'} +#define SH_SLAB_RADIX_NODE_MAGIC 0x6F4E61526C536853ULL // little endian +// Radix node slab structure +#pragma pack(1) +typedef struct sh_slab_radix_node_SLAB { + sh_uint8 sig[8]; + sh_uint16 used_count; + sh_uint64 slab_index; + struct sh_slab_radix_node_SLAB* next_slab; + struct sh_slab_radix_node_SLAB* prev_slab; + struct sh_slab_radix_node_SLAB* next_partial; + struct sh_slab_radix_node_SLAB* prev_partial; + sh_uint8 padding[78]; + sh_uint64 free_bitmap[16]; // First 18 objects will be mark as unavailable due to being replaced by the header and bitmap + sh_uint16 node_bitmap[1024]; + sh_radix_NODE nodes[SH_SLAB_RADIX_NODE_ACTUAL_OBJECTS_PER_SLAB]; +} sh_slab_radix_node_SLAB; +#pragma pack() +// Radix node slab allocator structure +struct sh_slab_radix_node_SLAB_ALLOCATOR { + sh_slab_radix_node_SLAB* first_slab; + sh_slab_radix_node_SLAB* partial_head; + sh_uint64 slab_count; + sh_pba_PAGE_BLOCK_ALLOCATOR* pba; +}; +typedef sh_uint16 sh_slab_radix_node_NODE_INDEX_IN_SLAB; +// Initialize slab allocator structure. Does not allocate any slab +SH_STATUS sh_slab_radix_node_alloc_init(struct sh_slab_radix_node_SLAB_ALLOCATOR* slab_alloc,sh_pba_PAGE_BLOCK_ALLOCATOR *pba); +// Allocate a new slab, initialize it and put it into the allocator. If new slab isn't the first to be allocated, push it on the partial slab list +SH_STATUS sh_slab_radix_node_add_slab(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_radix_node_SLAB** out_slab); +// Obtain a pointer to the first partial slab. Does not scan the slabs. Allocate a new slab if necessary +SH_STATUS sh_slab_radix_node_get_partial_slab(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL* ptp,sh_slab_radix_node_SLAB** found_slab); +// Rescan all the slabs to set all metadatas in case of doubt. Does not modify alloc->slab_count or ->nodes +SH_STATUS sh_slab_radix_node_scan_slabs(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc); +// Return a valid pointer to an empty object slot as well as the object index in the corresponding index. Slabs allocation is automated by sh_slab_radix_node_get_partial_slab +SH_STATUS sh_slab_radix_node_find_free_object(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_radix_NODE** out,sh_slab_radix_node_NODE_INDEX_IN_SLAB* index_in_slab); +// Allocate one new radix node object. Return a pointer to the struct of the new object. Since it call sh_slab_reg_phys_find_free_object, it can allocate new slab +SH_STATUS sh_slab_radix_node_alloc(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_radix_NODE** out_obj); +// Dellocate one radix node object provided as pointer. +SH_STATUS sh_slab_radix_node_dealloc(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_radix_NODE *object_ptr); +// Return a pointer to the bitmap of a node +sh_uint16 *sh_slab_radix_node_get_node_bitmap(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_radix_NODE *object_ptr); +#endif diff --git a/shelter/lib/include/memory/slabs/slab_reg_phys.h b/shelter/lib/include/memory/slabs/slab_reg_phys.h new file mode 100644 index 0000000..2d520be --- /dev/null +++ b/shelter/lib/include/memory/slabs/slab_reg_phys.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MPL-2.0 +// This is the implementation for the slab allocator for sh_pez_REGION_PHYSICAL_OBJECT +#ifndef SH_LIB_SLAB_REG_PHYS_H +#define SH_LIB_SLAB_REG_PHYS_H +#include "std/type.h" +#include "std/status.h" +#include "memory/page.h" +#include "memory/vmem_layout.h" +#define SH_SLAB_REG_PHYS_DATA_VA SH_VMEM_LAYOUT_SLAB_REG_PHYS_VA +#define SH_SLAB_REG_PHYS_MAX_SLAB (sh_uint64)(16*1024) +#define SH_SLAB_REG_PHYS_SLAB_DATA_PAGES (sh_uint64)3 +#define SH_SLAB_REG_PHYS_SLAB_DATA_SIZE_BYTES (sh_uint64)(SH_SLAB_REG_PHYS_SLAB_DATA_PAGES*4096) +#define SH_SLAB_REG_PHYS_OBJECT_SIZE_BYTES (sh_uint64)12 +#define SH_SLAB_REG_PHYS_OBJECTS_PER_SLAB (sh_uint64)1024 +#define SH_SLAB_REG_PHYS_ACTUAL_OBJECTS_PER_SLAB (sh_uint64)1023 +#define SH_SLAB_REG_PHYS_SLAB_BITMAP_SIZE_BYTES (sh_uint64)(SH_SLAB_REG_PHYS_OBJECTS_PER_SLAB/8) +#define SH_SLAB_REG_PHYS_NULL_REF (sh_uint64)0 +// Physical region slab structure +typedef struct sh_slab_reg_phys_SLAB_STRUCT { + sh_uint16 used_count; + sh_uint16 slab_index; + struct sh_slab_reg_phys_SLAB_STRUCT* next; + struct sh_slab_reg_phys_SLAB_STRUCT* prev; + sh_uint64 free_bitmap[16]; +} sh_slab_reg_phys_SLAB_STRUCT; +#define SH_SLAB_REG_PHYS_HEADER_LIST_SIZE_BYTES SH_SLAB_REG_PHYS_MAX_SLAB*sizeof(sh_slab_reg_phys_SLAB_STRUCT) +// Physical region object slab allocator structure +typedef struct { + sh_slab_reg_phys_SLAB_STRUCT* slabs_header; + sh_uint8* slabs_data; + sh_uint16 max_slabs; + sh_uint16 slab_count; + sh_slab_reg_phys_SLAB_STRUCT* partial_head; +} sh_slab_reg_phys_SLAB_ALLOCATOR; +typedef sh_uint32 sh_slab_reg_phys_OBJECT_INDEX; +// Return slab index +#define SH_SLAB_REG_PHYS_REF_SLAB(ref) (sh_uint16)((ref)>>10) +// Return object index inside a slab +#define SH_SLAB_REG_PHYS_REF_OBJECT(ref) (sh_uint16)((ref) & 0x3FF) +// Make a valid object index from a slab index and an object index inside this slab +#define SH_SLAB_REG_PHYS_MAKE_REF(slab,obj) (((sh_slab_reg_phys_OBJECT_INDEX)(slab)<<10) | (sh_slab_reg_phys_OBJECT_INDEX)(obj)) +// Initialize slab allocator structure. Does not allocate any slabs +SH_STATUS sh_slab_reg_phys_alloc_init(sh_slab_reg_phys_SLAB_ALLOCATOR* slab_alloc,sh_page_PAGE_TABLE_POOL *ptp); +// Allocate a new slab, initialize it and put it into the allocator. If new slab isn't the first to be allocated, push it on the partial slab list +SH_STATUS sh_slab_reg_phys_add_slab(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_phys_SLAB_STRUCT** out_slab); +// Obtain a pointer to the first partial slab. Does not scan the slabs. Allocate a new slab if necessary +SH_STATUS sh_slab_reg_phys_get_partial_slab(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL* ptp,sh_slab_reg_phys_SLAB_STRUCT** found_slab); +// Rescan all the slabs to set all metadatas in case of doubt. Does not modify alloc->slab_count, alloc->slabs_data +SH_STATUS sh_slab_reg_phys_scan_slabs(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc); +// Return a valid 24 bits index to a empty object slot. Slabs allocation is automated by sh_slab_reg_phys_get_partial_slab +SH_STATUS sh_slab_reg_phys_find_free_object(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_phys_OBJECT_INDEX* out_ref); +// Return a pointer to referenced physical region object +void* sh_slab_reg_phys_ref_to_ptr(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_slab_reg_phys_OBJECT_INDEX ref); +// Allocate one new physical region object. Return an index to access and deallocate the object. Since it call sh_slab_reg_phys_find_free_object, it can allocate new slab +SH_STATUS sh_slab_reg_phys_alloc(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_phys_OBJECT_INDEX* out_index); +// Dellocate one physical region object provided as index. +SH_STATUS sh_slab_reg_phys_dealloc(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_slab_reg_phys_OBJECT_INDEX index); +#endif diff --git a/shelter/lib/include/memory/slabs/slab_reg_virt.h b/shelter/lib/include/memory/slabs/slab_reg_virt.h new file mode 100644 index 0000000..0796cf1 --- /dev/null +++ b/shelter/lib/include/memory/slabs/slab_reg_virt.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MPL-2.0 +// This is the implementation for the slab allocator for sh_pez_REGION_VIRTUAL_OBJECT +#ifndef SH_LIB_SLAB_REG_VIRT_H +#define SH_LIB_SLAB_REG_VIRT_H +#include "std/type.h" +#include "std/status.h" +#include "memory/page.h" +#include "memory/vmem_layout.h" +#define SH_SLAB_REG_VIRT_DATA_VA SH_VMEM_LAYOUT_SLAB_REG_VIRT_VA +#define SH_SLAB_REG_VIRT_MAX_SLAB (sh_uint64)(512*1024) +#define SH_SLAB_REG_VIRT_SLAB_DATA_PAGES (sh_uint64)3 +#define SH_SLAB_REG_VIRT_SLAB_DATA_SIZE_BYTES (sh_uint64)(SH_SLAB_REG_VIRT_SLAB_DATA_PAGES*4096) +#define SH_SLAB_REG_VIRT_OBJECT_SIZE_BYTES (sh_uint64)12 +#define SH_SLAB_REG_VIRT_OBJECTS_PER_SLAB (sh_uint64)1024 +#define SH_SLAB_REG_VIRT_ACTUAL_OBJECTS_PER_SLAB (sh_uint64)1023 +#define SH_SLAB_REG_VIRT_SLAB_BITMAP_SIZE_BYTES (sh_uint64)(SH_SLAB_REG_VIRT_OBJECTS_PER_SLAB/8) +#define SH_SLAB_REG_VIRT_NULL_REF (sh_uint64)0 +// Virtual region slab structure +typedef struct sh_slab_reg_virt_SLAB_STRUCT { + sh_uint16 used_count; + sh_uint32 slab_index; + struct sh_slab_reg_virt_SLAB_STRUCT* next; + struct sh_slab_reg_virt_SLAB_STRUCT* prev; + sh_uint64 free_bitmap[16]; +} sh_slab_reg_virt_SLAB_STRUCT; +#define SH_SLAB_REG_VIRT_HEADER_LIST_SIZE_BYTES SH_SLAB_REG_VIRT_MAX_SLAB*sizeof(sh_slab_reg_virt_SLAB_STRUCT) +// Virtual region object slab allocator structure +typedef struct { + sh_slab_reg_virt_SLAB_STRUCT* slabs_header; + sh_uint8* slabs_data; + sh_uint32 max_slabs; + sh_uint32 slab_count; + sh_slab_reg_virt_SLAB_STRUCT* partial_head; +} sh_slab_reg_virt_SLAB_ALLOCATOR; +typedef sh_uint32 sh_slab_reg_virt_OBJECT_INDEX; +// Return slab index +#define SH_SLAB_REG_VIRT_REF_SLAB(ref) (sh_uint32)((ref)>>10) +// Return object index inside a slab +#define SH_SLAB_REG_VIRT_REF_OBJECT(ref) (sh_uint16)((ref) & 0x3FF) +// Make a valid object index from a slab index and an object index inside this slab +#define SH_SLAB_REG_VIRT_MAKE_REF(slab,obj) (((sh_slab_reg_virt_OBJECT_INDEX)(slab)<<10) | (sh_slab_reg_virt_OBJECT_INDEX)(obj)) +// Initialize slab allocator structure. Does not allocate any slabs +SH_STATUS sh_slab_reg_virt_alloc_init(sh_slab_reg_virt_SLAB_ALLOCATOR* slab_alloc,sh_page_PAGE_TABLE_POOL *ptp); +// Allocate a new slab, initialize it and put it into the allocator. If new slab isn't the first to be allocated, push it on the partial slab list +SH_STATUS sh_slab_reg_virt_add_slab(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_virt_SLAB_STRUCT** out_slab); +// Obtain a pointer to the first partial slab. Does not scan the slabs. Allocate a new slab if necessary +SH_STATUS sh_slab_reg_virt_get_partial_slab(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL* ptp,sh_slab_reg_virt_SLAB_STRUCT** found_slab); +// Rescan all the slabs to set all metadatas in case of doubt. Does not modify alloc->slab_count, alloc->slabs_data +SH_STATUS sh_slab_reg_virt_scan_slabs(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc); +// Return a valid 29 bits index to a empty object slot. Slabs allocation is automated by sh_slab_reg_virt_get_partial_slab +SH_STATUS sh_slab_reg_virt_find_free_object(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_virt_OBJECT_INDEX* out_ref); +// Return a pointer to referenced virtual region object +void* sh_slab_reg_virt_ref_to_ptr(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_slab_reg_virt_OBJECT_INDEX ref); +// Allocate one new virtual region object. Return both a pointer to the struct of the new object and an index to access and deallocte the object. Since it call sh_slab_reg_virt_find_free_object, it can allocate new slab +SH_STATUS sh_slab_reg_virt_alloc(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_virt_OBJECT_INDEX* out_index); +// Dellocate one virtual region object provided as index. +SH_STATUS sh_slab_reg_virt_dealloc(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_slab_reg_virt_OBJECT_INDEX index); +#endif diff --git a/shelter/lib/include/memory/vmem_layout.h b/shelter/lib/include/memory/vmem_layout.h new file mode 100644 index 0000000..98e38bf --- /dev/null +++ b/shelter/lib/include/memory/vmem_layout.h @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_VMEM_LAYOUT_H +#define SH_LIB_VMEM_LAYOUT_H +// HOW TO USE THIS FILE +// This file contain the VA, size in bytes and sometimes the end of virtual memory regions +// that we are sure that the kernel will search into at boot time or allocate things at these +// places. +// This file is autocheck by an automated script to ensure there is no overlapping. +// Any macro ending with _VA will create a new virtual region for the script. It will look for the +// size of this virtual region in another macro that start with the same prefix and end with +// _SIZE_BYTES. +// If a macro ending with _VA doesn't have a corresponding macro ending with _SIZE_BYTES, the +// script will trigger an error and the kernel compilation will fail. +// If a macro ending with _SIZE_BYTES doesn't have a corresponding macro ending with _VA, the +// script will ignore it. +// The start of each virtual region must be aligned to 4096 bytes and the size must be provided +// in bytes. +// Any overlapping virtual region will trigger a compilation error. +// Consider using this file as the source of trust for everything related to static virtual +// regions. +// Any macro that doesn't end with _VA or _SIZE_BYTES or that doesn't correspong to the behaviour +// described above will be ignored. +// TEMPORARY STRUCTURE +// The base for the memory map +#define SH_VMEM_LAYOUT_BOOT_CONFIG_VA 0x00180000 +// The size for the memory map +#define SH_VMEM_LAYOUT_BOOT_CONFIG_SIZE_BYTES 4096 +// The base for the memory map +#define SH_VMEM_LAYOUT_MEMORY_MAP_VA 0x00190000 +// The size for the memory map +#define SH_VMEM_LAYOUT_MEMORY_MAP_SIZE_BYTES 16*4096 +// KERNEL HEAP +// The base for big allocations region +#define SH_VMEM_LAYOUT_HEAP_BIG_VA 16LL*1024LL*1024LL*1024LL*1024LL +// The end for big allocations regions +#define SH_VMEM_LAYOUT_HEAP_BIG_SIZE_BYTES (0xFFFFFFFELL)*4096LL +// The base for all generic slabs allocators for the heap, spaced by 12 TB each +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_0_VA 0x0000200000000000 +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_1_VA 0x00002C0000000000 +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_2_VA 0x0000380000000000 +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_3_VA 0x0000440000000000 +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_4_VA 0x0000500000000000 +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_5_VA 0x00005C0000000000 +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_6_VA 0x0000680000000000 +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_7_VA 0x0000740000000000 +// The size for all generic slabs_allocators for the heap +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_0_SIZE_BYTES 0xBFFFFFFFLL*4096LL +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_1_SIZE_BYTES 0xBFFFFFFFLL*4096LL +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_2_SIZE_BYTES 0xBFFFFFFFLL*4096LL +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_3_SIZE_BYTES 0xBFFFFFFFLL*4096LL +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_4_SIZE_BYTES 0xBFFFFFFFLL*4096LL +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_5_SIZE_BYTES 0xBFFFFFFFLL*4096LL +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_6_SIZE_BYTES 0xBFFFFFFFLL*4096LL +#define SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_7_SIZE_BYTES 0xBFFFFFFFLL*4096LL +// The spacing in bytes for each slab allocator base +#define SH_VMEM_LAYOUT_HEAP_SLAB_SPACING 0xC0000000000 +// KERNEL AND STACKS AREA +// The base for the kernel image area +#define SH_VMEM_LAYOUT_KERNEL_IMAGE_AREA_VA 0xFFFF800000000000ULL +// The size for the kernel image area +#define SH_VMEM_LAYOUT_KERNEL_IMAGE_AREA_SIZE_BYTES 0xFFFF900000000000ULL-1-0xFFFF800000000000ULL +// The base for the stack area +#define SH_VMEM_LAYOUT_STACK_AREA_VA 0xFFFFF00000000000ULL +// The size for the kernel image area +#define SH_VMEM_LAYOUT_STACK_AREA_SIZE_BYTES 0xFFFFFF8000000000ULL-0xFFFFF00000000000ULL +// KERNEL ALLOCATION SPACE +// The base for the kernel allocation space +#define SH_VMEM_LAYOUT_KERNEL_ALLOC_SPACE_VA 0xFFFF900000000000ULL +// The size for the kernel allocation space +#define SH_VMEM_LAYOUT_KERNEL_ALLOC_SPACE_SIZE_BYTES 0xFFFFEFFFFFFFFFFFULL-0xFFFF900000000000ULL +// The end for the kernel allocation space +#define SH_VMEM_LAYOUT_KERNEL_ALLOC_SPACE_VA_END 0xFFFFEFFFFFFFFFFFULL +// SLABS VIRTUAl REGIONS +// The base for the slabs for physical regions objects +#define SH_VMEM_LAYOUT_SLAB_REG_PHYS_VA 0xFFFFFF8000000000ULL+0x4000ULL +// The total size for the slabs for physical regions objects +#define SH_VMEM_LAYOUT_SLAB_REG_PHYS_SIZE_BYTES 192*1024*1024ULL +// The base for the slabs for virtual regions objects +#define SH_VMEM_LAYOUT_SLAB_REG_VIRT_VA 0xFFFFFF800C600000ULL +// The total size for the slabs for virtual regions objects +#define SH_VMEM_LAYOUT_SLAB_REG_VIRT_SIZE_BYTES (1ULL<<29)*12ULL +// The alignement for SH_VMEM_LAYOUT_SLAB_RADIX_NODE_VA +#define SH_VMEM_LAYOUT_PBA_RADIX_NODE_BLOCK_ALIGN (32ULL*4096ULL) +// The base for the PBA for the slabs for radix nodes +#define SH_VMEM_LAYOUT_SLAB_RADIX_NODE_VA ((SH_VMEM_LAYOUT_SLAB_REG_VIRT_VA+SH_VMEM_LAYOUT_SLAB_REG_VIRT_SIZE_BYTES+SH_VMEM_LAYOUT_PBA_RADIX_NODE_BLOCK_ALIGN)/SH_VMEM_LAYOUT_PBA_RADIX_NODE_BLOCK_ALIGN)*SH_VMEM_LAYOUT_PBA_RADIX_NODE_BLOCK_ALIGN +// The total size for the PBA for the slabs for radix nodes +#define SH_VMEM_LAYOUT_SLAB_RADIX_NODE_SIZE_BYTES 64ULL*1024ULL*1024ULL*1024ULL +// LOGGING RING BUFFER +// The base for the logging ring buffer +#define SH_VMEM_LAYOUT_LOGGING_RING_BUFFER_VA 0xFFFFFFFFF0000000ULL +// The max size for the logging ring buffer +#define SH_VMEM_LAYOUT_LOGGING_RING_BUFFER_SIZE_BYTES 0xFFFF*4096 +#endif diff --git a/shelter/lib/include/std/malloc.h b/shelter/lib/include/std/malloc.h new file mode 100644 index 0000000..8109f08 --- /dev/null +++ b/shelter/lib/include/std/malloc.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_MALLOC_H +#define SH_LIB_MALLOC_H +#include "std/status.h" +#include "std/type.h" +// Return last status +SH_STATUS sh_malloc_get_last_status(); +// Allocate memory +void* sh_malloc(sh_uint64 size); +// Free memory +void sh_free(void *ptr); +#endif diff --git a/shelter/lib/include/std/mem.h b/shelter/lib/include/std/mem.h new file mode 100644 index 0000000..b80ad82 --- /dev/null +++ b/shelter/lib/include/std/mem.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_MEM_H +#define SH_LIB_MEM_H +#include "std/type.h" +#include "std/status.h" +// Memory comparaison function +SH_STATUS sh_mem_compare(const void *a,const void *b,sh_uint64 size); +// Memory copy function +SH_STATUS sh_mem_copy(const void *destination,const void *source,sh_uint64 size); +// Memory set function +SH_STATUS sh_mem_set_8(sh_uint8 *ptr,const sh_uint8 byte,sh_uint64 count); +#endif diff --git a/shelter/lib/include/std/status.h b/shelter/lib/include/std/status.h new file mode 100644 index 0000000..3c4f4bd --- /dev/null +++ b/shelter/lib/include/std/status.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_STATUS_H +#define SH_LIB_STATUS_H +#include "std/type.h" +typedef sh_int64 SH_STATUS; +#define SH_STATUS_NEW_SLAB_ADDED -5LL +#define SH_STATUS_VA_FULLY_MAPPED -4LL +#define SH_STATUS_VA_PARTIALLY_MAPPED -3LL +#define SH_STATUS_VA_NOT_MAPPED -2LL +#define SH_STATUS_VA_MAPPED -1LL +#define SH_STATUS_SUCCESS 0LL +#define SH_STATUS_INVALID_PARAMETER 1LL +#define SH_STATUS_MEM_NOT_EQUAL 2LL +#define SH_STATUS_INVALID_SIGNATURE 3LL +#define SH_STATUS_MMAP_BUFFER_OVERFLOW 4LL +#define SH_STATUS_PMAP_NO_PAGES_SET 5LL +#define SH_STATUS_PT_POOL_NO_BITMAP_INIT 6LL +#define SH_STATUS_PT_POOL_NO_PAGE_SET 7LL +#define SH_STATUS_OUT_OF_MEMORY 8LL +#define SH_STATUS_INVALID_INTERNAL_PA 9LL +#define SH_STATUS_KERNEL_PANIC 10LL +#define SH_STATUS_PTP_FULL 11LL +#define SH_STATUS_ERROR_VA_FULLY_MAPPED 12LL +#define SH_STATUS_ERROR_VA_PARTIALLY_MAPPED 13LL +#define SH_STATUS_ERROR_VA_NOT_MAPPED 14LL +#define SH_STATUS_SLAB_ALLOCATOR_FULL 15LL +#define SH_STATUS_NOT_FOUND 16LL +#define SH_STATUS_RESCAN_NEEDED 17LL +#define SH_STATUS_ERROR_NULLPTR_RETURNED 18LL +#define SH_STATUS_ERROR_PEZ_INITIALIZED 19LL +#define SH_STATUS_SLAB_SLOT_ALREADY_FREE 20LL +#define SH_STATUS_TEST_FAILED 21LL +#define SH_STATUS_PBA_FULL 22LL +#define SH_STATUS_FOUND_CORRUPTED_SLAB 23LL +#define SH_STATUS_PEZ_CORRUPTED 24LL +#define SH_STATUS_PAGE_ALREADY_FREE 25LL +#define SH_STATUS_TOO_MUCH_DATA 26LL +#define SH_STATUS_HEAP_NOT_INITIALIZED 27LL +// Return SH_TRUE if provided status is an error +static inline sh_bool sh_status_error(SH_STATUS s) { + return s>=SH_STATUS_INVALID_PARAMETER; +} +#endif diff --git a/shelter/lib/include/std/stdlib.h b/shelter/lib/include/std/stdlib.h new file mode 100644 index 0000000..7add4a2 --- /dev/null +++ b/shelter/lib/include/std/stdlib.h @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MPL-2.0 +// This header is just here to include all standard header +// If you are looking for printing functions, take a look at kernel/log.h +#include "std/mem.h" +#include "std/malloc.h" +#include "std/status.h" +#include "std/type.h" diff --git a/shelter/lib/include/std/type.h b/shelter/lib/include/std/type.h new file mode 100644 index 0000000..0d2c42d --- /dev/null +++ b/shelter/lib/include/std/type.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MPL-2.0 +#ifndef SH_LIB_TYPE_H +#define SH_LIB_TYPE_H +typedef signed char sh_int8; +typedef unsigned char sh_uint8; +typedef signed short sh_int16; +typedef unsigned short sh_uint16; +typedef signed int sh_int32; +typedef unsigned int sh_uint32; +typedef signed long long sh_int64; +typedef unsigned long long sh_uint64; +#define SH_INT8_MAX 127 +#define SH_INT8_MIN (-128) +#define SH_UINT8_MAX 0xFF +#define SH_INT16_MAX 32767 +#define SH_INT16_MIN (-32768) +#define SH_UINT16_MAX 0xFFFF +#define SH_INT32_MAX 2147483647 +#define SH_INT32_MIN (-2147483647-1) +#define SH_UINT32_MAX 0xFFFFFFFFU +#define SH_INT64_MAX 9223372036854775807LL +#define SH_INT64_MIN (-9223372036854775807LL-1) +#define SH_UINT64_MAX 0xFFFFFFFFFFFFFFFFULL +#define SH_NULLPTR ((void*)0) +typedef sh_uint8 sh_bool; +#define SH_TRUE ((sh_bool)1) +#define SH_FALSE ((sh_bool)0) +#define SH_DEFVALUE ((sh_uint64)0x446546614C547674) +#define DEFAULT +typedef sh_uint64 sh_iter64; +typedef __builtin_va_list va_list; +#define va_start(v,l) __builtin_va_start(v,l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v,l) __builtin_va_arg(v,l) +#endif diff --git a/shelter/lib/src/cpu/serial.c b/shelter/lib/src/cpu/serial.c new file mode 100644 index 0000000..38e5b0d --- /dev/null +++ b/shelter/lib/src/cpu/serial.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "cpu/serial.h" +#include "cpu/asm.h" +sh_bool disable_serial_port=SH_FALSE; +void sh_serial_load_serial_port_setting(sh_bool is_disabled) { + disable_serial_port=is_disabled; + return; +} +void sh_serial_send_byte(sh_uint8 b) { + if (disable_serial_port) return; + while (!(sh_asm_inb(SH_SERIAL_PORT_COM1+5) & 0x20)); + sh_asm_outb(SH_SERIAL_PORT_COM1,b); + return; +} diff --git a/shelter/lib/src/cpu/tsc.c b/shelter/lib/src/cpu/tsc.c new file mode 100644 index 0000000..f8aa230 --- /dev/null +++ b/shelter/lib/src/cpu/tsc.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "cpu/tsc.h" +sh_tsc_TSC_VALUE kernel_start_tsc=0; +SH_STATUS sh_tsc_init_tsc() { + kernel_start_tsc=sh_tsc_read_tsc(); + return SH_STATUS_SUCCESS; +} +sh_tsc_TSC_VALUE sh_tsc_get_kernel_init_tsc() { + return kernel_start_tsc; +} +sh_tsc_TSC_VALUE sh_tsc_get_kernel_current_tsc() { + return sh_tsc_read_tsc()-kernel_start_tsc; +} diff --git a/shelter/lib/src/kernel/conf.c b/shelter/lib/src/kernel/conf.c new file mode 100644 index 0000000..8a909a5 --- /dev/null +++ b/shelter/lib/src/kernel/conf.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "kernel/conf.h" +#include "kernel/log.h" +#include "std/mem.h" +SH_STATUS sh_conf_get_boot_config(sh_conf_BOOT_CONFIG **config) { + if (config==SH_NULLPTR) { + return SH_STATUS_INVALID_PARAMETER; + } + *config=(sh_conf_BOOT_CONFIG *)SH_CONF_BOOT_CONFIG_VA; + static const sh_uint8 sig_start[8]={'S','h','C','f','g','B','e','g'}; + static const sh_uint8 sig_end[8]={'S','h','C','f','g','E','n','d'}; + if (sh_mem_compare((*config)->sig_start,sig_start,sizeof(sig_start))==SH_STATUS_MEM_NOT_EQUAL) { + return SH_STATUS_INVALID_SIGNATURE; + } + if (sh_mem_compare((*config)->sig_end,sig_end,sizeof(sig_end))==SH_STATUS_MEM_NOT_EQUAL) { + return SH_STATUS_INVALID_SIGNATURE; + } + return SH_STATUS_SUCCESS; +} diff --git a/shelter/lib/src/kernel/log.c b/shelter/lib/src/kernel/log.c new file mode 100644 index 0000000..6be1c8a --- /dev/null +++ b/shelter/lib/src/kernel/log.c @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "kernel/log.h" +#include "memory/page.h" +#include "memory/ring.h" +#include "cpu/serial.h" +sh_uint8 kernel_log_level=0; +sh_bool log_disable_serial_port=SH_FALSE; +sh_ring_RING_BUFFER_HEADER logging_ring={.head=0,.tail=0,.buffer_bytes_size=4*4096,.data_start=(sh_uint8*)SH_VMEM_LAYOUT_LOGGING_RING_BUFFER_VA,.total_bytes_written=0}; +void sh_log_load_serial_setting(sh_bool is_disabled) { + log_disable_serial_port=is_disabled; + return; +} +sh_bool sh_log_get_serial_setting() { + return log_disable_serial_port; +} +void sh_log_load_logging_ring_size(sh_uint16 pages_count) { + logging_ring.buffer_bytes_size=4096*pages_count; + return; +} +sh_uint32 sh_log_get_logging_ring_size() { + return logging_ring.buffer_bytes_size; +} +sh_uint64 sh_log_get_total_bytes_written() { + return logging_ring.total_bytes_written; +} +void sh_log_byte(sh_uint8 byte) { + if (log_disable_serial_port) return; + sh_serial_send_byte(byte); + sh_ring_write_byte(&logging_ring,(sh_uint8)byte); + return; +} +SH_STATUS sh_log_string(const char* str) { + while (*str) { + sh_log_byte((sh_uint8)*str++); + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_int8(sh_int8 n) { + return sh_log_int64((sh_int64)n); +} +SH_STATUS sh_log_int16(sh_int16 n) { + return sh_log_int64((sh_int64)n); +} +SH_STATUS sh_log_int32(sh_int32 n) { + return sh_log_int64((sh_int64)n); +} +SH_STATUS sh_log_int64(sh_int64 n) { + sh_uint64 absolute_val; + if (n<0) { + sh_log_byte('-'); + absolute_val=(sh_uint64)(-(n+1))+1; + } else { + absolute_val=(sh_uint64)n; + } + return sh_log_uint64(absolute_val); +} +SH_STATUS sh_log_uint8(sh_uint8 n) { + return sh_log_uint64((sh_uint64)n); +} +SH_STATUS sh_log_uint16(sh_uint16 n) { + return sh_log_uint64((sh_uint64)n); +} +SH_STATUS sh_log_uint32(sh_uint32 n) { + return sh_log_uint64((sh_uint64)n); +} +SH_STATUS sh_log_uint64(sh_uint64 n) { + char buf[20]; + sh_uint32 i=0; + if (n==0) { + sh_log_byte('0'); + return SH_STATUS_SUCCESS; + } + while (n>0) { + buf[i++]='0'+(sh_int8)(n%10); + n/=10; + } + while (i--) { + sh_log_byte((sh_uint8)buf[i]); + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_uint8_hex(sh_uint8 n) { + return sh_log_uint64_hex((sh_uint64)n); +} +SH_STATUS sh_log_uint16_hex(sh_uint16 n) { + return sh_log_uint64_hex((sh_uint64)n); +} +SH_STATUS sh_log_uint32_hex(sh_uint32 n) { + return sh_log_uint64_hex((sh_uint64)n); +} +SH_STATUS sh_log_uint64_hex(sh_uint64 n) { + char buf[16]; + sh_uint32 i=0; + const char hex_digits[]="0123456789ABCDEF"; + if (n==0) { + sh_log_byte('0'); + return SH_STATUS_SUCCESS; + } + while (n>0) { + buf[i++]=hex_digits[n & 0xF]; + n>>=4; + } + while (i--) { + sh_log_byte((sh_uint8)buf[i]); + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_uint64_hex_fixed(sh_uint64 n) { + const char hex_digits[]="0123456789ABCDEF"; + for (sh_int8 i=15;i>=0;i--) { + sh_uint8 nibble=(sh_uint8)((n>>(i*4))&0xF); + sh_log_byte((sh_uint8)hex_digits[nibble]); + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_double(double value) { + if (value<0) { + sh_log_byte('-'); + value=-value; + } + sh_uint64 integer_part=(sh_uint64)value; + double fractional_part=value-(double)integer_part; + SH_STATUS status=sh_log_uint64(integer_part); + if (status!=SH_STATUS_SUCCESS) return status; + sh_log_byte('.'); + for (sh_iter64 i=0;i<6;i++) { + fractional_part*=10.0; + sh_uint64 digit=(sh_uint64)fractional_part; + sh_log_byte('0'+(sh_uint8)digit); + fractional_part-=(double)digit; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_format(const char* format,va_list args) { + for (const char *p=format;*p!='\0';p++) { + if (*p!='%') { + sh_log_byte((sh_uint8)*p); + continue; + } + p++; + switch (*p) { + case 's': { + char *s=va_arg(args,char *); + sh_log_string(s); + break; + } + case 'd': { + double s=va_arg(args,double); + sh_log_double(s); + break; + } + case '1': { + if (p[1]=='u') { + sh_log_uint8((sh_uint8)va_arg(args,int)); + p++; + } else if (p[1]=='s') { + sh_log_int8((sh_int8)va_arg(args,int)); + p++; + } else if (p[1]=='U') { + sh_log_uint8_hex((sh_uint8)va_arg(args,int)); + p++; + } + break; + } + case '2': { + if (p[1]=='u') { + sh_log_uint16((sh_uint16)va_arg(args,int)); + p++; + } else if (p[1]=='s') { + sh_log_int16((sh_int16)va_arg(args,int)); + p++; + } else if (p[1]=='U') { + sh_log_uint16_hex((sh_uint16)va_arg(args,int)); + p++; + } + break; + } + case '4': { + if (p[1]=='u') { + sh_log_uint32((sh_uint32)va_arg(args,int)); + p++; + } else if (p[1]=='s') { + sh_log_int32((sh_int32)va_arg(args,int)); + p++; + } else if (p[1]=='U') { + sh_log_uint32_hex((sh_uint32)va_arg(args,int)); + p++; + } + break; + } + case '8': { + if (p[1]=='u') { + sh_log_uint64(va_arg(args,unsigned long long)); + p++; + } else if (p[1]=='s') { + sh_log_int64(va_arg(args,long long)); + p++; + } else if (p[1]=='U') { + sh_log_uint64_hex(va_arg(args,unsigned long long)); + p++; + } + break; + } + case 'x': { + sh_uint64 x=va_arg(args,sh_uint64); + sh_log_uint64_hex_fixed(x); + break; + } + case 'c': { + sh_log_byte((sh_uint8)va_arg(args,int)); + break; + } + case '%': { + sh_log_byte('%'); + break; + } + default: + sh_log_byte((sh_uint8)*p); + break; + } + } + va_end(args); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_payload(sh_log_OUTPUT_PAYLOAD *payload) { + if (payload==SH_NULLPTR) { + return SH_STATUS_INVALID_PARAMETER; + } + if (!(sh_log_output_type_valid(payload->output_type))) { + return SH_STATUS_INVALID_PARAMETER; + } + if (!(sh_log_output_source_valid(payload->output_source))) { + return SH_STATUS_INVALID_PARAMETER; + } + if (payload->output_source!=SH_LOG_SOURCE_TEST && payload->output_typeoutput_source==SH_LOG_SOURCE_MAIN) { + sh_log_string("Main@"); + } else if (payload->output_source==SH_LOG_SOURCE_CONF) { + sh_log_string("Conf@"); + } else if (payload->output_source==SH_LOG_SOURCE_PAGE) { + sh_log_string("Page@"); + } else if (payload->output_source==SH_LOG_SOURCE_SLAB) { + sh_log_string("Slab@"); + } else if (payload->output_source==SH_LOG_SOURCE_TEST) { + sh_log_string("Test@"); + } else if (payload->output_source==SH_LOG_SOURCE_PEZ) { + sh_log_string("Pez @"); + } else if (payload->output_source==SH_LOG_SOURCE_PBA) { + sh_log_string("PBA @"); + } else if (payload->output_source==SH_LOG_SOURCE_HEAP) { + sh_log_string("Heap@"); + } else if (payload->output_source==SH_LOG_SOURCE_STD) { + sh_log_string("STD @"); + } + if (payload->output_type==SH_LOG_DEBUG) { + sh_log_string("Debug] "); + } else if (payload->output_type==SH_LOG_LOG) { + sh_log_string("Log] "); + } else if (payload->output_type==SH_LOG_WARNING) { + sh_log_string("Warning] "); + } else if (payload->output_type==SH_LOG_ERROR) { + sh_log_string("Error] "); + } else if (payload->output_type==SH_LOG_CRITICAL) { + sh_log_string("Critical] "); + } else if (payload->output_type==SH_LOG_FATAL) { + sh_log_string("Fatal] "); + } else if (payload->output_type==SH_LOG_TEST) { + sh_log_string("Test] "); + } + sh_log_uint64(payload->tsc_value); + sh_log_string(" : "); + sh_log_string(payload->message_pointer); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_payload_format(sh_log_OUTPUT_PAYLOAD *payload,va_list args) { + if (payload==SH_NULLPTR) { + return SH_STATUS_INVALID_PARAMETER; + } + if (!(sh_log_output_type_valid(payload->output_type))) { + return SH_STATUS_INVALID_PARAMETER; + } + if (!(sh_log_output_source_valid(payload->output_source))) { + return SH_STATUS_INVALID_PARAMETER; + } + if (payload->output_source!=SH_LOG_SOURCE_TEST && payload->output_typeoutput_source==SH_LOG_SOURCE_MAIN) { + sh_log_string("Main@"); + } else if (payload->output_source==SH_LOG_SOURCE_CONF) { + sh_log_string("Conf@"); + } else if (payload->output_source==SH_LOG_SOURCE_PAGE) { + sh_log_string("Page@"); + } else if (payload->output_source==SH_LOG_SOURCE_SLAB) { + sh_log_string("Slab@"); + } else if (payload->output_source==SH_LOG_SOURCE_TEST) { + sh_log_string("Test@"); + } else if (payload->output_source==SH_LOG_SOURCE_PEZ) { + sh_log_string("Pez @"); + } else if (payload->output_source==SH_LOG_SOURCE_PBA) { + sh_log_string("PBA @"); + } else if (payload->output_source==SH_LOG_SOURCE_HEAP) { + sh_log_string("Heap@"); + } else if (payload->output_source==SH_LOG_SOURCE_STD) { + sh_log_string("STD @"); + } + if (payload->output_type==SH_LOG_DEBUG) { + sh_log_string("Debug] "); + } else if (payload->output_type==SH_LOG_LOG) { + sh_log_string("Log] "); + } else if (payload->output_type==SH_LOG_WARNING) { + sh_log_string("Warning] "); + } else if (payload->output_type==SH_LOG_ERROR) { + sh_log_string("Error] "); + } else if (payload->output_type==SH_LOG_CRITICAL) { + sh_log_string("Critical] "); + } else if (payload->output_type==SH_LOG_FATAL) { + sh_log_string("Fatal] "); + } else if (payload->output_type==SH_LOG_TEST) { + sh_log_string("Test] "); + } + sh_log_uint64(payload->tsc_value); + sh_log_string(" : "); + sh_log_format(payload->message_pointer,args); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_load_log_level(sh_uint8 log_level) { + kernel_log_level=log_level; + return SH_STATUS_SUCCESS; +} +sh_uint8 sh_log_get_log_level() { + return kernel_log_level; +} +SH_STATUS sh_log_test(const char* str) { + if (str==SH_NULLPTR) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_TEST, + .output_source=SH_LOG_SOURCE_TEST, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + sh_log_string("\n"); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_debug(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_DEBUG, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + sh_log_string("\n"); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_log(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_LOG, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + sh_log_string("\n"); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_warning(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_WARNING, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + sh_log_string("\n"); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_error(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_ERROR, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + sh_log_string("\n"); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_critical(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_CRITICAL, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + sh_log_string("\n"); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_fatal(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_FATAL, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + sh_log_string("\n"); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_ltest(const char* str) { + if (str==SH_NULLPTR) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_TEST, + .output_source=SH_LOG_SOURCE_TEST, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_ldebug(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_DEBUG, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_llog(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_LOG, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_lwarning(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_WARNING, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_lerror(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_ERROR, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_lcritical(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_CRITICAL, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_lfatal(const char* str,sh_log_OUTPUT_SOURCE source) { + if (str==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_FATAL, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=str + }; + SH_STATUS status=sh_log_payload(&payload); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_ftest(const char* format,...) { + if (format==SH_NULLPTR) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_TEST, + .output_source=SH_LOG_SOURCE_TEST, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=format + }; + va_list args; + va_start(args,format); + SH_STATUS status=sh_log_payload_format(&payload,args); + va_end(args); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_fdebug(const sh_log_OUTPUT_SOURCE source,const char* format,...) { + if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_DEBUG, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=format + }; + va_list args; + va_start(args,format); + SH_STATUS status=sh_log_payload_format(&payload,args); + va_end(args); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_flog(const sh_log_OUTPUT_SOURCE source,const char* format,...) { + if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_LOG, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=format + }; + va_list args; + va_start(args,format); + SH_STATUS status=sh_log_payload_format(&payload,args); + va_end(args); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_fwarning(const sh_log_OUTPUT_SOURCE source,const char* format,...) { + if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_WARNING, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=format + }; + va_list args; + va_start(args,format); + SH_STATUS status=sh_log_payload_format(&payload,args); + va_end(args); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_ferror(const sh_log_OUTPUT_SOURCE source,const char* format,...) { + if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_ERROR, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=format + }; + va_list args; + va_start(args,format); + SH_STATUS status=sh_log_payload_format(&payload,args); + va_end(args); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_fcritical(const sh_log_OUTPUT_SOURCE source,const char* format,...) { + if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_CRITICAL, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=format + }; + va_list args; + va_start(args,format); + SH_STATUS status=sh_log_payload_format(&payload,args); + va_end(args); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_ffatal(const sh_log_OUTPUT_SOURCE source,const char* format,...) { + if (format==SH_NULLPTR || !(sh_log_output_source_valid(source))) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_log_OUTPUT_PAYLOAD payload={ + .output_type=SH_LOG_FATAL, + .output_source=source, + .tsc_value=sh_tsc_get_kernel_current_tsc(), + .message_pointer=format + }; + va_list args; + va_start(args,format); + SH_STATUS status=sh_log_payload_format(&payload,args); + va_end(args); + if (sh_status_error(status)) { + return status; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_log_mem_stats(sh_log_OUTPUT_SOURCE source) { + sh_page_MEM_STATS mem_stats; + sh_page_get_memory_stats(&mem_stats); + sh_log_flog(source,"Total physical memory (bytes) : %8u / 0x%8U\n",mem_stats.memory_total_bytes,mem_stats.memory_total_bytes); + sh_log_flog(source,"Total physical memory (pages) : %8u / 0x%8U\n",mem_stats.memory_total_pages,mem_stats.memory_total_pages); + sh_log_flog(source,"Total installed memory (bytes) : %8u / 0x%8U\n",mem_stats.memory_installed_bytes,mem_stats.memory_installed_bytes); + sh_log_flog(source,"Total installed memory (pages) : %8u / 0x%8U\n",mem_stats.memory_installed_pages,mem_stats.memory_installed_pages); + sh_log_flog(source,"Free memory : %d%%\n",mem_stats.free_ratio*100); + sh_log_flog(source,"Used memory : %d%%\n",mem_stats.used_ratio*100); + sh_log_flog(source,"Free pages : %8u\n",mem_stats.free_pages); + sh_log_flog(source,"Used pages : %8u\n",mem_stats.used_pages); + sh_log_flog(source,"Largest free block (pages) : %8u\n",mem_stats.largest_free_block); + sh_log_flog(source,"Largest used block (pages) : %8u\n",mem_stats.largest_used_block); + sh_log_flog(source,"Free block count : %8u\n",mem_stats.free_blocks_count); + sh_log_flog(source,"Used block count : %8u\n",mem_stats.used_blocks_count); + sh_log_flog(source,"Total memory taken by physical bitmap (bytes) : %8u / 0x%8U\n",mem_stats.physical_bitmap_size_bytes,mem_stats.physical_bitmap_size_bytes); + sh_log_flog(source,"Total memory taken by physical bitmap (pages) : %8u / 0x%8U\n",mem_stats.physical_bitmap_size_pages,mem_stats.physical_bitmap_size_pages); + return SH_STATUS_SUCCESS; +} diff --git a/shelter/lib/src/kernel/tests/test_malloc.c b/shelter/lib/src/kernel/tests/test_malloc.c new file mode 100644 index 0000000..9fdb961 --- /dev/null +++ b/shelter/lib/src/kernel/tests/test_malloc.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "kernel/tests/test_malloc.h" +#include "kernel/tests/test_utils.h" +#include "kernel/tests/payloads/test_malloc_payload.h" +#include "kernel/log.h" +#include "memory/page.h" +#include "memory/heap.h" +#include "std/mem.h" +#include "std/malloc.h" +static sh_uint64 iter_num=10000; +__attribute__((section(".bss"))) +static sh_uint64 address_list[10000]; +SH_STATUS sh_test_malloc_benchmark() { + sh_tsc_TSC_VALUE *tsc_value=sh_test_get_tsc_values_buffer_ptr(); + sh_mem_set_8((sh_uint8*)tsc_value,0x00,10000*sizeof(sh_tsc_TSC_VALUE)); + sh_mem_set_8((sh_uint8*)address_list,0x00,sizeof(address_list)); + sh_log_test("Benchmarking Malloc subsystem..."); + sh_tsc_TSC_VALUE start=0; + sh_tsc_TSC_VALUE end=0; + for (sh_iter64 i=0;i0 && array[j-1]>key) { + array[j]=array[j-1]; + j--; + } + array[j]=key; + } +} +static sh_tsc_TSC_VALUE percentile(sh_tsc_TSC_VALUE *array,sh_uint64 n,sh_uint8 p) { + sh_uint64 idx=(n*p)/100; + if (idx>=n) idx=n-1; + return array[idx]; +} +SH_STATUS sh_test_compute_print_stats(char* benchname,sh_tsc_TSC_VALUE *tsc_value_array,sh_uint64 array_size) { + if (tsc_value_array==SH_NULLPTR || array_size==0) return SH_STATUS_INVALID_PARAMETER; + sort(tsc_value_array,array_size); + sh_uint64 min=tsc_value_array[0]; + sh_uint64 max=tsc_value_array[array_size-1]; + sh_uint64 median=tsc_value_array[array_size/2]; + sh_uint64 sum=0; + for(sh_iter64 i=0;iphys_plane=phys_plane; + kernel_heap->virt_plane=virt_plane; + kernel_heap->kernel_ptp=kernel_ptp; + SH_STATUS status=sh_radix_tree_init(phys_plane->slab_radix_node,kernel_ptp,&kernel_heap->alloc_size_tree,8); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; +} +static void sh_heap_internal_crash(const char *str,SH_STATUS status) { + sh_log_ffatal(SH_LOG_SOURCE_HEAP,str,status); + sh_log_mem_stats(SH_LOG_SOURCE_HEAP); + while (SH_TRUE) {}; + return; +} +SH_STATUS sh_heap_allocate_pages(sh_uint32 pages_count,sh_page_VIRTUAL_ADDRESS *address) { + if (pages_count==0 || address==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (default_heap==SH_NULLPTR) return SH_STATUS_HEAP_NOT_INITIALIZED; + sh_page_PHYSICAL_ADDRESS pa; + SH_STATUS status=sh_pez_alloc_physical_pages(default_heap->phys_plane,pages_count,&pa); + if (sh_status_error(status)) return status; + sh_page_PHYSICAL_ADDRESS va; + status=sh_pez_alloc_virtual_pages(default_heap->virt_plane,pages_count,&va); + if (sh_status_error(status)) { + status=sh_pez_free_physical_pages(default_heap->phys_plane,&pa,pages_count); + if (sh_status_error(status)) sh_heap_internal_crash("Heap alloc error: couldn't free in emergency physical pages after virtual alloc error. Status: %8s\n",status); + return status; + } + if (pa%SH_PAGE_SIZE!=0 || va%SH_PAGE_SIZE!=0) sh_heap_internal_crash("Heap alloc error: physical address or virtual address isn't page aligned. Status: %8s\n",SH_STATUS_PEZ_CORRUPTED); + status=sh_page_map_contiguous_pages_range_ptp(default_heap->kernel_ptp,va,pa,SH_PAGE_PRESENT | SH_PAGE_RW | SH_PAGE_NX,pages_count*SH_PAGE_SIZE); + if (status!=SH_STATUS_SUCCESS) sh_heap_internal_crash("Heap alloc error: couldn't map all pages. Status: %8s",status); + sh_uint32 page_index=(sh_uint32)((va/SH_PAGE_SIZE)-SH_VMEM_LAYOUT_HEAP_BIG_VA); + status=sh_radix_tree_insert_value(default_heap->phys_plane->slab_radix_node,default_heap->kernel_ptp,&default_heap->alloc_size_tree,page_index,pages_count); + if (status!=SH_STATUS_SUCCESS) sh_heap_internal_crash("Heap alloc error: couldn't insert alloc record inside alloc_size_tree. Status: %8s\n",status); + *address=va; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_heap_free_pages(sh_page_VIRTUAL_ADDRESS va) { + if (default_heap==SH_NULLPTR) return SH_STATUS_HEAP_NOT_INITIALIZED; + if (va%SH_PAGE_SIZE!=0) return SH_STATUS_INVALID_PARAMETER; + sh_uint32 page_index=(sh_uint32)((va/SH_PAGE_SIZE)-SH_VMEM_LAYOUT_HEAP_BIG_VA); + sh_page_VIRTUAL_ADDRESS pages_count_raw=0; + SH_STATUS status=sh_radix_tree_get_value(&default_heap->alloc_size_tree,page_index,&pages_count_raw); + if (status!=SH_STATUS_SUCCESS) { + // sh_log_flog(SH_LOG_SOURCE_HEAP,"va=0x%x\n",va); + sh_heap_internal_crash("Heap free error: alloc record not found in alloc_size_tree. Status: %8s\n",status); + } + sh_uint32 pages_count=(sh_uint32)pages_count_raw; + sh_page_PHYSICAL_ADDRESS pa; + status=sh_page_ptp_va_to_pa(default_heap->kernel_ptp,va,&pa); + if (sh_status_error(status)) sh_heap_internal_crash("Heap free error: couldn't find associated physical address. Status: %8s\n",status); + status=sh_page_unmap_contiguous_pages_range_ptp(default_heap->kernel_ptp,va,pages_count*SH_PAGE_SIZE); + if (sh_status_error(status)) sh_heap_internal_crash("Heap free error: unmap failed. Status: %8s\n",status); + // sh_log_flog(SH_LOG_SOURCE_HEAP,"freeed va=0x%x\n",va); + status=sh_pez_free_virtual_pages(default_heap->virt_plane,&va,pages_count); + if (sh_status_error(status)) sh_heap_internal_crash("Heap free error: virt_plane free failed. Status: %8s\n",status); + status=sh_pez_free_physical_pages(default_heap->phys_plane,&pa,pages_count); + if (sh_status_error(status)) sh_heap_internal_crash("Heap free error: phys_plane free failed. Status: %8s\n",status); + status=sh_radix_tree_delete_value(default_heap->phys_plane->slab_radix_node,&default_heap->alloc_size_tree,page_index); + if (sh_status_error(status)) sh_heap_internal_crash("Heap free error: record deletion failed. Status: %8s\n",status); + return SH_STATUS_SUCCESS; +} +static inline sh_uint32 sh_heap_internal_get_index(sh_uint32 n) { + if (n<=8) return 0; + return (sh_uint32)31-(sh_uint32)__builtin_clz(n-1)-(sh_uint32)2; +} +SH_STATUS sh_heap_allocate_object(sh_uint32 size_bytes,sh_page_VIRTUAL_ADDRESS *address) { + if (default_heap==SH_NULLPTR) return SH_STATUS_HEAP_NOT_INITIALIZED; + if (size_bytes==0 || address==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (size_bytes>1024) return SH_STATUS_INVALID_PARAMETER; + sh_uint32 slab_alloc_index=sh_heap_internal_get_index(size_bytes); + void *ptr; + SH_STATUS status=sh_slab_generic_alloc(&default_heap->slabs_allocator[slab_alloc_index],default_heap->kernel_ptp,&ptr); + if (sh_status_error(status)) return status; + sh_page_VIRTUAL_ADDRESS va=(sh_uint64)ptr; + *address=va; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_heap_free_object(sh_page_VIRTUAL_ADDRESS va) { + if (default_heap==SH_NULLPTR) return SH_STATUS_HEAP_NOT_INITIALIZED; + sh_uint64 slab_alloc_index=(va-SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_0_VA)/0x00000C0000000000; + if (slab_alloc_index>7) return SH_STATUS_INVALID_PARAMETER; + return sh_slab_generic_dealloc(&default_heap->slabs_allocator[slab_alloc_index],(void*)va); +} diff --git a/shelter/lib/src/memory/page.c b/shelter/lib/src/memory/page.c new file mode 100644 index 0000000..010c419 --- /dev/null +++ b/shelter/lib/src/memory/page.c @@ -0,0 +1,669 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "memory/page.h" +#include "kernel/log.h" +#include "std/mem.h" +__attribute__((section(".bss"))) +static sh_uint8 memory_map_buffer[64*1024]; +static sh_uint8 *physical_bitmap; +__attribute__((section(".bss"))) +static sh_uint64 physical_memory_pages_count=0; +__attribute__((section(".bss"))) +static sh_uint64 physical_memory_bytes_count=0; +__attribute__((section(".bss"))) +static sh_uint64 physical_bitmap_size_bytes=0; +__attribute__((section(".bss"))) +static sh_uint64 physical_bitmap_size_pages=0; +__attribute__((section(".bss"))) +static sh_uint64 physical_memory_installed=0; +static sh_page_VIRTUAL_ADDRESS page_table_pool_va_ptr=SH_PAGE_NULL_VA; +SH_STATUS sh_page_load_boot_ptp_va(sh_page_VIRTUAL_ADDRESS pt_pool_va) { + page_table_pool_va_ptr=pt_pool_va; + sh_log_fdebug(SH_LOG_SOURCE_PAGE,"Page table pool VA: 0x%8U\n",(sh_uint64)page_table_pool_va_ptr); + volatile sh_uint8 first_byte=*(sh_uint8*)(page_table_pool_va_ptr); + (void)first_byte; + sh_log_debug("If you can see this message, no fault happened.",SH_LOG_SOURCE_PAGE); + return SH_STATUS_SUCCESS; +} +sh_page_VIRTUAL_ADDRESS sh_page_get_boot_ptp_va() { + return page_table_pool_va_ptr; +} +SH_STATUS sh_page_copy_memory_map() { + return sh_mem_copy(memory_map_buffer,(void*)SH_PAGE_MEMORY_MAP_VA,sizeof(memory_map_buffer)); +} +SH_STATUS sh_page_check_memory_map() { + static const sh_uint8 memory_map_sig[8]={'S','h','e','M','m','a','p','B'}; + if (sh_mem_compare(memory_map_sig,memory_map_buffer,sizeof(memory_map_sig))==SH_STATUS_MEM_NOT_EQUAL) { + sh_log_critical("Memory map doesn't have signature on.",SH_LOG_SOURCE_PAGE); + return SH_STATUS_INVALID_SIGNATURE; + } + sh_page_MEMORY_MAP_HEADER *memory_map_header=(sh_page_MEMORY_MAP_HEADER *)memory_map_buffer; + sh_log_fdebug(SH_LOG_SOURCE_PAGE,"Memory map entry count: %8u\n",(sh_uint64)memory_map_header->entry_count); + sh_log_fdebug(SH_LOG_SOURCE_PAGE,"Memory map entry size: %8u\n",(sh_uint64)memory_map_header->entry_size); + sh_log_fdebug(SH_LOG_SOURCE_PAGE,"Memory map syntax version: %1u\n",memory_map_header->mmap_syntax_version); + if (memory_map_header->entry_count*memory_map_header->entry_size+sizeof(sh_page_MEMORY_MAP_HEADER)>sizeof(memory_map_buffer)) { + sh_log_error("Memory map overflow allocated buffer.",SH_LOG_SOURCE_PAGE); + return SH_STATUS_MMAP_BUFFER_OVERFLOW; + } + return SH_STATUS_SUCCESS; +} +sh_uint64 sh_page_get_physical_memory_amount_pages() { + return physical_memory_pages_count; +} +sh_uint64 sh_page_get_physical_memory_amount_bytes() { + return physical_memory_bytes_count; +} +sh_uint64 sh_page_get_one_page_na() { + sh_uint64 page_count=physical_memory_pages_count; + sh_uint64 bitmap_word_count=(page_count+63)/64; + for (sh_iter64 word=0;word=page_count) { + return 0; + } + if ((value & (1ULL<page_count_in_bitmap) { + return SH_STATUS_INVALID_PARAMETER; + } + for (sh_iter64 i=0;ipage_table_pa=ptp_pa; + page_table_pool->page_table_va=ptp_va; + page_table_pool->ptp_pages_count=SH_PAGE_PTP_ALLOCATOR_PAGES_COUNT; + page_table_pool->ptp_alloc_bitmap_uint64_count=SH_PAGE_PTP_ALLOCATOR_BITMAP_UINT64; + SH_STATUS status=sh_mem_set_8((sh_uint8*)page_table_pool->ptp_alloc_bitmap,SH_FALSE,sizeof(page_table_pool->ptp_alloc_bitmap)); + if (sh_status_error(status)) { + sh_log_error("Error: couldn't initialize page table pool bitmap.",SH_LOG_SOURCE_PAGE); + return SH_STATUS_PT_POOL_NO_BITMAP_INIT; + } + if (initial_fill_level!=0) { + status=sh_page_set_pages_range_bitmap((sh_uint8*)page_table_pool->ptp_alloc_bitmap,page_table_pool->ptp_pages_count,0,initial_fill_level,SH_TRUE); + if (sh_status_error(status)) { + sh_log_error("Error: couldn't initialize pages tables already alocated.",SH_LOG_SOURCE_PAGE); + return SH_STATUS_PT_POOL_NO_PAGE_SET; + } + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_page_dump_ptp_bitmap(sh_page_PAGE_TABLE_POOL *ptp) { + for (sh_iter64 i=0;iptp_alloc_bitmap_uint64_count;++i) { + sh_log_fdebug(SH_LOG_SOURCE_PAGE," 0x%8U\n",ptp->ptp_alloc_bitmap[i]); + } + return SH_STATUS_SUCCESS; +} +sh_page_PHYSICAL_ADDRESS sh_page_ptp_alloc_one_page(sh_page_PAGE_TABLE_POOL *pt_pool) { + if (pt_pool==SH_NULLPTR) { + return SH_STATUS_INVALID_PARAMETER; + } + sh_uint64 page_count=pt_pool->ptp_pages_count; + sh_uint64 bitmap_word_count=(page_count+63)/64; + for (sh_iter64 word=0;wordptp_alloc_bitmap[word]; + if (value==0xFFFFFFFFFFFFFFFFULL) { + continue; + } + for (sh_iter64 bit=0;bit<64;bit++) { + sh_uint64 page_index=(word*64)+bit; + if (page_index>=page_count) { + return 0; + } + if ((value & (1ULL<ptp_alloc_bitmap[word]|=(1ULL<page_table_pa+page_index*SH_PAGE_SIZE; + return pa; + } + } + } + return 0; +} +SH_STATUS sh_page_map_one_page_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_page_PHYSICAL_ADDRESS pa,sh_uint64 flags) { + if (ptp==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (va%SH_PAGE_SIZE!=0 || pa%SH_PAGE_SIZE!=0) return SH_STATUS_INVALID_PARAMETER; + sh_uint64 pml4_i=(va>>39) & 0x1FF; + sh_uint64 pdpt_i=(va>>30) & 0x1FF; + sh_uint64 pd_i=(va>>21) & 0x1FF; + sh_uint64 pt_i=(va>>12) & 0x1FF; + sh_uint64 *pdpt; + sh_uint64 *pd; + sh_uint64 *pt; + sh_uint64 *pml4=(sh_uint64*)ptp->page_table_va; + if (!(pml4[pml4_i] & SH_PAGE_PRESENT)) { + sh_page_PHYSICAL_ADDRESS pdpt_pa=sh_page_ptp_alloc_one_page(ptp); + if (!pdpt_pa) return SH_STATUS_OUT_OF_MEMORY; + pdpt=sh_page_ptp_pa_to_va(ptp,pdpt_pa); + if (!pdpt) return SH_STATUS_INVALID_INTERNAL_PA; + sh_mem_set_8((sh_uint8*)pdpt,0,SH_PAGE_SIZE); + pml4[pml4_i]=pdpt_pa | SH_PAGE_TABLE_FLAGS | SH_PAGE_PRESENT; + } else { + pdpt=sh_page_ptp_pa_to_va(ptp,(pml4[pml4_i] & ~0xFFFULL)); + if (!pdpt) return SH_STATUS_INVALID_INTERNAL_PA; + } + if (!(pdpt[pdpt_i] & SH_PAGE_PRESENT)) { + sh_page_PHYSICAL_ADDRESS pd_pa=sh_page_ptp_alloc_one_page(ptp); + if (!pd_pa) return SH_STATUS_OUT_OF_MEMORY; + pd=sh_page_ptp_pa_to_va(ptp,pd_pa); + if (!pd) return SH_STATUS_INVALID_INTERNAL_PA; + sh_mem_set_8((sh_uint8*)pd,0,SH_PAGE_SIZE); + pdpt[pdpt_i]=pd_pa | SH_PAGE_TABLE_FLAGS | SH_PAGE_PRESENT; + } else { + pd=sh_page_ptp_pa_to_va(ptp,(pdpt[pdpt_i] & ~0xFFFULL)); + if (!pd) return SH_STATUS_INVALID_INTERNAL_PA; + } + if (!(pd[pd_i] & SH_PAGE_PRESENT)) { + sh_page_PHYSICAL_ADDRESS pt_pa=sh_page_ptp_alloc_one_page(ptp); + if (!pt_pa) return SH_STATUS_OUT_OF_MEMORY; + pt=sh_page_ptp_pa_to_va(ptp,pt_pa); + if (!pt) return SH_STATUS_INVALID_INTERNAL_PA; + sh_mem_set_8((sh_uint8*)pt,0,SH_PAGE_SIZE); + pd[pd_i]=pt_pa | SH_PAGE_TABLE_FLAGS | SH_PAGE_PRESENT; + } else { + pt=sh_page_ptp_pa_to_va(ptp,pd[pd_i] & ~0xFFFULL); + if (!pt) return SH_STATUS_INVALID_INTERNAL_PA; + } + pt[pt_i]=(pa & ~0xFFFULL) | flags | SH_PAGE_PRESENT; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_page_is_va_mapped_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va) { + if (ptp==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_uint64 pml4_i=(va>>39) & 0x1FF; + sh_uint64 pdpt_i=(va>>30) & 0x1FF; + sh_uint64 pd_i=(va>>21) & 0x1FF; + sh_uint64 pt_i=(va>>12) & 0x1FF; + sh_uint64 *pdpt; + sh_uint64 *pd; + sh_uint64 *pt; + sh_uint64 *pml4=(sh_uint64*)ptp->page_table_va; + if (!(pml4[pml4_i] & SH_PAGE_PRESENT)) { + return SH_STATUS_VA_NOT_MAPPED; + } else { + pdpt=sh_page_ptp_pa_to_va(ptp,(pml4[pml4_i] & ~0xFFFULL)); + if (pdpt==0) return SH_STATUS_INVALID_INTERNAL_PA; + } + if (!(pdpt[pdpt_i] & SH_PAGE_PRESENT)) { + return SH_STATUS_VA_NOT_MAPPED; + } else { + pd=sh_page_ptp_pa_to_va(ptp,(pdpt[pdpt_i] & ~0xFFFULL)); + if (pd==0) return SH_STATUS_INVALID_INTERNAL_PA; + } + if (!(pd[pd_i] & SH_PAGE_PRESENT)) { + return SH_STATUS_VA_NOT_MAPPED; + } else { + pt=sh_page_ptp_pa_to_va(ptp,(pd[pd_i] & ~0xFFFULL)); + if (pt==0) return SH_STATUS_INVALID_INTERNAL_PA; + } + if (!(pt[pt_i] & SH_PAGE_PRESENT)) { + return SH_STATUS_VA_NOT_MAPPED; + } else { + return SH_STATUS_VA_MAPPED; + } +} +SH_STATUS sh_page_is_va_range_mapped_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes) { + if (ptp==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (va%SH_PAGE_SIZE!=0 || size_bytes%SH_PAGE_SIZE!=0 || size_bytes==0) return SH_STATUS_INVALID_PARAMETER; + sh_uint64 counter=0; + for (sh_iter64 i=0;i>39) & 0x1FF; + sh_uint64 pdpt_i=(va>>30) & 0x1FF; + sh_uint64 pd_i=(va>>21) & 0x1FF; + sh_uint64 pt_i=(va>>12) & 0x1FF; + sh_uint64 *pdpt; + sh_uint64 *pd; + sh_uint64 *pt; + sh_uint64 *pml4=(sh_uint64*)ptp->page_table_va; + if (!(pml4[pml4_i] & SH_PAGE_PRESENT)) { + return SH_STATUS_ERROR_VA_NOT_MAPPED; + } else { + pdpt=sh_page_ptp_pa_to_va(ptp,(pml4[pml4_i] & ~0xFFFULL)); + if (!pdpt) return SH_STATUS_INVALID_INTERNAL_PA; + } + if (!(pdpt[pdpt_i] & SH_PAGE_PRESENT)) { + return SH_STATUS_ERROR_VA_NOT_MAPPED; + } else { + pd=sh_page_ptp_pa_to_va(ptp,(pdpt[pdpt_i] & ~0xFFFULL)); + if (!pd) return SH_STATUS_INVALID_INTERNAL_PA; + } + if (!(pd[pd_i] & SH_PAGE_PRESENT)) { + return SH_STATUS_ERROR_VA_NOT_MAPPED; + } else { + pt=sh_page_ptp_pa_to_va(ptp,pd[pd_i] & ~0xFFFULL); + if (!pt) return SH_STATUS_INVALID_INTERNAL_PA; + } + pt[pt_i]=0x0ULL; + sh_asm_invlpg((void*)va); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_page_unmap_contiguous_pages_range_ptp(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes) { + if (ptp==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (va%SH_PAGE_SIZE!=0 || size_bytes==0 || size_bytes%SH_PAGE_SIZE!=0) return SH_STATUS_INVALID_PARAMETER; + sh_uint64 pages=size_bytes/SH_PAGE_SIZE; + SH_STATUS status=sh_page_is_va_range_mapped_ptp(ptp,va,size_bytes); + if (status==SH_STATUS_VA_PARTIALLY_MAPPED) return SH_STATUS_ERROR_VA_PARTIALLY_MAPPED; + if (status==SH_STATUS_VA_NOT_MAPPED) return SH_STATUS_ERROR_VA_NOT_MAPPED; + for (sh_iter64 i=0;i>39) & 0x1FF; + sh_uint64 pdpt_i=(va>>30) & 0x1FF; + sh_uint64 pd_i=(va>>21) & 0x1FF; + sh_uint64 pt_i=(va>>12) & 0x1FF; + sh_uint64 *pdpt; + sh_uint64 *pd; + sh_uint64 *pt; + sh_uint64 *pml4=(sh_uint64*)ptp->page_table_va; + if (!(pml4[pml4_i] & SH_PAGE_PRESENT)) { + return SH_STATUS_ERROR_VA_NOT_MAPPED; + } else { + pdpt=sh_page_ptp_pa_to_va(ptp,(pml4[pml4_i] & ~0xFFFULL)); + if (!pdpt) return SH_STATUS_INVALID_INTERNAL_PA; + } + if (!(pdpt[pdpt_i] & SH_PAGE_PRESENT)) { + return SH_STATUS_ERROR_VA_NOT_MAPPED; + } else { + pd=sh_page_ptp_pa_to_va(ptp,(pdpt[pdpt_i] & ~0xFFFULL)); + if (!pd) return SH_STATUS_INVALID_INTERNAL_PA; + } + if (!(pd[pd_i] & SH_PAGE_PRESENT)) { + return SH_STATUS_ERROR_VA_NOT_MAPPED; + } else { + pt=sh_page_ptp_pa_to_va(ptp,pd[pd_i] & ~0xFFFULL); + if (!pt) return SH_STATUS_INVALID_INTERNAL_PA; + } + if (!(pt[pt_i] & SH_PAGE_PRESENT)) return SH_STATUS_ERROR_VA_NOT_MAPPED; + *pa=pt[pt_i] & 0x000FFFFFFFFFF000; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_page_unalloc_one_page(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va) { + if (ptp==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (va%SH_PAGE_SIZE!=0) return SH_STATUS_INVALID_PARAMETER; + sh_page_PHYSICAL_ADDRESS equivalent_pa; + SH_STATUS status=sh_page_ptp_va_to_pa(ptp,va,&equivalent_pa); + if (status!=SH_STATUS_SUCCESS) return status; + status=sh_page_unmap_one_page_ptp(ptp,va); // If this call return SH_STATUS_ERROR_VA_NOT_MAPPED, there is a severe bug that should cause kernel panic because sh_page_ptp_va_to_pa should already have returned exact same error code. + if (status!=SH_STATUS_SUCCESS) return status; + status=sh_page_set_pages_range_bitmap(physical_bitmap,physical_memory_pages_count,(sh_uint64)(equivalent_pa/SH_PAGE_SIZE),1,SH_FALSE); + if (status!=SH_STATUS_SUCCESS) return status; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_page_unalloc_contiguous(sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS va,sh_uint64 size_bytes) { + if (ptp==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (va%SH_PAGE_SIZE!=0 || size_bytes==0 || size_bytes%SH_PAGE_SIZE!=0) return SH_STATUS_INVALID_PARAMETER; + SH_STATUS status=sh_page_is_va_range_mapped_ptp(ptp,va,size_bytes); + if (status==SH_STATUS_VA_NOT_MAPPED) return SH_STATUS_ERROR_VA_NOT_MAPPED; + if (status==SH_STATUS_VA_PARTIALLY_MAPPED) return SH_STATUS_ERROR_VA_PARTIALLY_MAPPED; + sh_uint64 pages=size_bytes/SH_PAGE_SIZE; + for (sh_iter64 i=0;ientry_count;i++) { + sh_uint64 start_page=memory_map_cursor[i].physical_start/4096; + sh_uint64 end_page=start_page+memory_map_cursor[i].pages_count; + if (memory_map_cursor[i].type==SH_PAGE_CONVENTIONAL_MEMORY && memory_map_cursor[i].pages_count>biggest_segment_pages) { + biggest_segment_pages=memory_map_cursor[i].pages_count; + biggest_segment_index=i; + } + if (memory_map_cursor[i].type==SH_PAGE_CONVENTIONAL_MEMORY) { + sh_log_fdebug(SH_LOG_SOURCE_PAGE,"Found memory map segment #%8u: [0x%8U - 0x%8U] Memory type : %4u --> usable\n",i,start_page*4096,end_page*4096,memory_map_cursor[i].type); + } else { + sh_log_fdebug(SH_LOG_SOURCE_PAGE,"Found memory map segment #%8u: [0x%8U - 0x%8U] Memory type : %4u --> not usable\n",i,start_page*4096,end_page*4096,memory_map_cursor[i].type); + } + if (!(end_page<=SH_PAGE_MAX_PAGES_COUNT)) { + sh_log_fwarning(SH_LOG_SOURCE_PAGE,"Memory map segment #%8u isn't usable because it overflow over max page count. Enable debug log channel to see more.\n",i); + } + if (memory_map_cursor[i].type==SH_PAGE_CONVENTIONAL_MEMORY && end_page<=SH_PAGE_MAX_PAGES_COUNT) { + physical_memory_installed+=memory_map_cursor[i].pages_count*SH_PAGE_SIZE; + if (end_page>highest_usable_page) { + highest_usable_segment=i; + highest_usable_page=end_page; + } + } + } + physical_memory_pages_count=highest_usable_page; + physical_memory_bytes_count=physical_memory_pages_count*SH_PAGE_SIZE; + sh_log_fdebug(SH_LOG_SOURCE_PAGE,"Total memory was given by memory map segment #%8u\n",highest_usable_segment); + sh_log_fdebug(SH_LOG_SOURCE_PAGE,"Total memory (pages): %8u\n",physical_memory_pages_count); + sh_log_fdebug(SH_LOG_SOURCE_PAGE,"Total memory (bytes) : %8u\n",physical_memory_bytes_count); + if (biggest_segment_pages==0) { + sh_log_error("No suitable conventional memory segment found.",SH_LOG_SOURCE_PAGE); + return SH_STATUS_OUT_OF_MEMORY; + } + if (memory_map_cursor[biggest_segment_index].pages_count<(physical_memory_pages_count/8)) { + sh_log_error("Memory is too low or too fragmented to allocate physical bitmap.",SH_LOG_SOURCE_PAGE); + return SH_STATUS_OUT_OF_MEMORY; + } + sh_page_PHYSICAL_ADDRESS pa=memory_map_cursor[biggest_segment_index].physical_start; + sh_page_VIRTUAL_ADDRESS va; + physical_bitmap_size_bytes=physical_memory_pages_count/8; + if (physical_memory_pages_count%8!=0) physical_bitmap_size_bytes++; + physical_bitmap_size_pages=physical_bitmap_size_bytes/SH_PAGE_SIZE; + if (physical_bitmap_size_bytes%SH_PAGE_SIZE!=0) physical_bitmap_size_pages++; + SH_STATUS status=sh_page_search_available_va_range(ptp,SH_PAGE_KERNEL_PERM_VA_BASE,(SH_PAGE_KERNEL_PERM_VA_END-SH_PAGE_KERNEL_PERM_VA_BASE+1-0x1000),physical_bitmap_size_pages*SH_PAGE_SIZE,&va); + if (status!=SH_STATUS_SUCCESS) { + sh_log_error("Memory is too low or too fragmented to allocate physical bitmap.",SH_LOG_SOURCE_PAGE); + return status; + } + status=sh_page_map_contiguous_pages_range_ptp(ptp,va,pa,SH_PAGE_PRESENT | SH_PAGE_NX | SH_PAGE_RW,physical_bitmap_size_pages*SH_PAGE_SIZE); + if (status==SH_STATUS_OUT_OF_MEMORY) { + sh_log_error("Memory is too low or too fragmented to allocate physical bitmap.",SH_LOG_SOURCE_PAGE); + return status; + } else if (status!=SH_STATUS_SUCCESS) { + sh_log_error("An unknow error happened during physical bitmap pages mapping. See error below",SH_LOG_SOURCE_PAGE); + return status; + } + physical_bitmap=(sh_uint8*)va; + status=sh_mem_set_8(physical_bitmap,0xFF,physical_bitmap_size_bytes); + if (sh_status_error(status)) { + sh_log_error("An unknow error happened during physical bitmap filling with 0xFF. See error below.",SH_LOG_SOURCE_PAGE); + return status; + } + // second loop : actually set all free regions into physical bitmap + for (sh_iter64 i=0;ientry_count;i++) { + sh_uint64 start_page=memory_map_cursor[i].physical_start/4096; + sh_uint64 end_page=start_page+memory_map_cursor[i].pages_count; + if (end_page<=SH_PAGE_MAX_PAGES_COUNT) { + if (memory_map_cursor[i].type==SH_PAGE_CONVENTIONAL_MEMORY) { + status=sh_page_set_pages_range_bitmap(physical_bitmap,physical_memory_pages_count,memory_map_cursor[i].physical_start/SH_PAGE_SIZE,memory_map_cursor[i].pages_count,SH_FALSE); + if (sh_status_error(status)) { + sh_log_error("Couldn't set this memory map segment to usable.",SH_LOG_SOURCE_PAGE); + return SH_STATUS_PMAP_NO_PAGES_SET; + } + } + } + } + sh_page_set_pages_range_bitmap(physical_bitmap,physical_memory_pages_count,pa/SH_PAGE_SIZE,physical_bitmap_size_pages,SH_TRUE); + return SH_STATUS_SUCCESS; +} +sh_page_VIRTUAL_ADDRESS sh_page_get_physical_bitmap_ptr() { + return (sh_page_VIRTUAL_ADDRESS)physical_bitmap; +} +static sh_uint64 popcount64(sh_uint64 x) { + x=x-((x>>1) & 0x5555555555555555ULL); + x=(x & 0x3333333333333333ULL)+((x>>2) & 0x3333333333333333ULL); + x=(x+(x>>4)) & 0x0F0F0F0F0F0F0F0FULL; + x=x+(x>>8); + x=x+(x>>16); + x=x+(x>>32); + return x & 0x7F; +} +SH_STATUS sh_page_get_memory_stats(sh_page_MEM_STATS *mem_stats) { + if (mem_stats==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + mem_stats->memory_total_pages=physical_memory_pages_count; + mem_stats->memory_total_bytes=physical_memory_bytes_count; + sh_uint64 free_pages=0; + sh_uint64 used_pages=0; + sh_uint64 largest_free_block=0; + sh_uint64 largest_used_block=0; + sh_uint64 free_blocks_count=0; + sh_uint64 used_blocks_count=0; + sh_uint64 current_free_block=0; + sh_uint64 current_used_block=0; + sh_uint64 full_uint64_count=physical_memory_pages_count/64; + sh_uint64 remaining_bits=physical_memory_pages_count%64; + sh_uint64 *bitmap64=(sh_uint64*)physical_bitmap; + for (sh_iter64 i=0;i>b) & 1; + if (bit_set) { + current_used_block++; + if (current_free_block) { + free_blocks_count++; + if (current_free_block>largest_free_block) { + largest_free_block=current_free_block; + } + current_free_block=0; + } + } else { + current_free_block++; + if (current_used_block) { + used_blocks_count++; + if (current_used_block>largest_used_block) { + largest_used_block=current_used_block; + } + current_used_block=0; + } + } + } + } + if (remaining_bits) { + sh_uint64 val=bitmap64[full_uint64_count] & ((1ULL<>b) & 1; + if (bit_set) { + current_used_block++; + if (current_free_block) { + free_blocks_count++; + if (current_free_block>largest_free_block) { + largest_free_block=current_free_block; + } + current_free_block=0; + } + } else { + current_free_block++; + if (current_used_block) { + used_blocks_count++; + if (current_used_block>largest_used_block) { + largest_used_block=current_used_block; + } + current_used_block=0; + } + } + } + } + if (current_free_block) { + free_blocks_count++; + if (current_free_block>largest_free_block) { + largest_free_block=current_free_block; + } + } + if (current_used_block) { + used_blocks_count++; + if (current_used_block>largest_used_block) { + largest_used_block=current_used_block; + } + } + mem_stats->free_pages=free_pages; + mem_stats->used_pages=used_pages; + mem_stats->free_ratio=(double)free_pages/(double)physical_memory_pages_count; + mem_stats->used_ratio=(double)used_pages/(double)physical_memory_pages_count; + mem_stats->largest_free_block=largest_free_block; + mem_stats->largest_used_block=largest_used_block; + mem_stats->free_blocks_count=free_blocks_count; + mem_stats->used_blocks_count=used_blocks_count; + mem_stats->physical_bitmap_size_bytes=(physical_memory_pages_count+7)/8; + mem_stats->physical_bitmap_size_pages=(mem_stats->physical_bitmap_size_bytes+4095)/4096; + mem_stats->memory_installed_pages=physical_memory_installed/SH_PAGE_SIZE; + mem_stats->memory_installed_bytes=physical_memory_installed; + return SH_STATUS_SUCCESS; +} diff --git a/shelter/lib/src/memory/pba.c b/shelter/lib/src/memory/pba.c new file mode 100644 index 0000000..98578ac --- /dev/null +++ b/shelter/lib/src/memory/pba.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "memory/pba.h" +#include "memory/pez/pez.h" +#include "kernel/log.h" +SH_STATUS sh_pba_init(sh_pba_PAGE_BLOCK_ALLOCATOR *pba,sh_page_VIRTUAL_ADDRESS start_va,sh_uint64 area_pages_amount,sh_uint64 block_pages) { + if (pba==SH_NULLPTR) { + return SH_STATUS_INVALID_PARAMETER; + } + if (area_pages_amount%block_pages!=0) { + return SH_STATUS_INVALID_PARAMETER; + } + if (start_va%(SH_PAGE_SIZE*block_pages)!=0) { + return SH_STATUS_INVALID_PARAMETER; + } + pba->start_va=start_va; + pba->block_pages=block_pages; + pba->block_count=0; + pba->total_pages=area_pages_amount; + pba->max_blocks=area_pages_amount/block_pages; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_pba_alloc(sh_pba_PAGE_BLOCK_ALLOCATOR *pba,sh_page_PAGE_TABLE_POOL *ptp,sh_page_VIRTUAL_ADDRESS *ptr) { + if (pba==SH_NULLPTR || ptr==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (pba->block_count==pba->max_blocks) return SH_STATUS_PBA_FULL; + if (!sh_pez_is_available()) { + sh_page_PHYSICAL_ADDRESS block_pa; + SH_STATUS status=sh_page_search_physical_contiguous_block_na(pba->block_pages,&block_pa); + if (sh_status_error(status)) return status; + sh_page_VIRTUAL_ADDRESS temp_ptr=pba->start_va+pba->block_count*pba->block_pages*SH_PAGE_SIZE; + status=sh_page_is_va_range_mapped_ptp(ptp,temp_ptr,pba->block_pages*SH_PAGE_SIZE); + if (status==SH_STATUS_VA_FULLY_MAPPED || status==SH_STATUS_VA_PARTIALLY_MAPPED) { + return status; + } else if (sh_status_error(status)) { + return status; + } else if (status!=SH_STATUS_VA_NOT_MAPPED) { + return status; + } + status=sh_page_set_pages_range_bitmap((sh_uint8*)sh_page_get_physical_bitmap_ptr(),sh_page_get_physical_memory_amount_pages(),block_pa/SH_PAGE_SIZE,pba->block_pages,SH_TRUE); + if (sh_status_error(status)) return status; + status=sh_page_map_contiguous_pages_range_ptp(ptp,temp_ptr,block_pa,SH_PAGE_RW | SH_PAGE_NX | SH_PAGE_PRESENT,pba->block_pages*SH_PAGE_SIZE); + if (sh_status_error(status)) { + SH_STATUS status2=sh_page_set_pages_range_bitmap((sh_uint8*)sh_page_get_physical_bitmap_ptr(),sh_page_get_physical_memory_amount_pages(),block_pa/SH_PAGE_SIZE,pba->block_pages,SH_FALSE); + if (sh_status_error(status2)) return status2; + return status; + } + *ptr=temp_ptr; + } else { + if (sh_pez_get_reference_phys_plane()==SH_NULLPTR) return SH_STATUS_ERROR_PEZ_INITIALIZED; + sh_page_PHYSICAL_ADDRESS block_pa; + SH_STATUS status=sh_pez_alloc_physical_pages(sh_pez_get_reference_phys_plane(),(sh_uint32)pba->block_pages,&block_pa); + if (sh_status_error(status)) return status; + sh_page_VIRTUAL_ADDRESS temp_ptr=pba->start_va+pba->block_count*pba->block_pages*SH_PAGE_SIZE; + status=sh_page_is_va_range_mapped_ptp(ptp,temp_ptr,pba->block_pages*SH_PAGE_SIZE); + if (status==SH_STATUS_VA_FULLY_MAPPED || status==SH_STATUS_VA_PARTIALLY_MAPPED) { + return status; + } else if (sh_status_error(status)) { + return status; + } else if (status!=SH_STATUS_VA_NOT_MAPPED) { + return status; + } + status=sh_page_map_contiguous_pages_range_ptp(ptp,temp_ptr,block_pa,SH_PAGE_RW | SH_PAGE_NX | SH_PAGE_PRESENT,pba->block_pages*SH_PAGE_SIZE); + if (sh_status_error(status)) { + SH_STATUS status2=sh_page_set_pages_range_bitmap((sh_uint8*)sh_page_get_physical_bitmap_ptr(),sh_page_get_physical_memory_amount_pages(),block_pa/SH_PAGE_SIZE,pba->block_pages,SH_FALSE); + if (sh_status_error(status2)) return status2; + return status; + } + *ptr=temp_ptr; + } + pba->block_count++; + return SH_STATUS_SUCCESS; +} diff --git a/shelter/lib/src/memory/pez/pez.c b/shelter/lib/src/memory/pez/pez.c new file mode 100644 index 0000000..b4f996c --- /dev/null +++ b/shelter/lib/src/memory/pez/pez.c @@ -0,0 +1,860 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "memory/pez/pez.h" +#include "memory/pez/pez_debug.h" +#include "kernel/log.h" +sh_bool sh_pez_is_pez_available=SH_FALSE; +sh_pez_PHYSICAL_PLANE *reference_phys_plane=SH_NULLPTR; +void sh_pez_set_available() { + sh_pez_is_pez_available=SH_TRUE; +} +sh_bool sh_pez_is_available() { + return sh_pez_is_pez_available; +} +static void sh_pez_set_reference_phys_plane(sh_pez_PHYSICAL_PLANE *phys_plane) { + reference_phys_plane=phys_plane; +} +sh_pez_PHYSICAL_PLANE* sh_pez_get_reference_phys_plane() { + return reference_phys_plane; +} +static inline void sh_pez_internal_pack_index(sh_uint8 *dest,sh_uint32 index) { + dest[0]=(sh_uint8)(index & 0xFF); + dest[1]=(sh_uint8)((index>>8)&0xFF); + dest[2]=(sh_uint8)((index>>16)&0xFF); + return; +} +static inline sh_uint32 sh_pez_internal_unpack_index(sh_uint8 *src) { + return (sh_uint32)src[0] | ((sh_uint32)src[1]<<8) | ((sh_uint32)src[2]<<16); +} +typedef struct { + sh_uint32 region_index; + sh_uint32 previous_index; +} sh_pez_internal_BOUNDARY_ENTRY; +static inline sh_page_VIRTUAL_ADDRESS sh_pez_internal_pack_boundary(sh_uint32 region,sh_uint32 prev) { + return (sh_page_VIRTUAL_ADDRESS)(((sh_uint64)prev<<32) | region); +} +static inline sh_pez_internal_BOUNDARY_ENTRY sh_pez_internal_unpack_boundary(sh_page_VIRTUAL_ADDRESS value) { + sh_pez_internal_BOUNDARY_ENTRY entry; + entry.region_index=(sh_uint32)(value & 0xFFFFFFFF); + entry.previous_index=(sh_uint32)(value>>32); + return entry; +} +static SH_STATUS sh_pez_internal_scan_physical_bitmap(sh_pez_PHYSICAL_PLANE *phys_plane) { + if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_log_fdebug(SH_LOG_SOURCE_PEZ,"Scanning physical bitmap:\n"); + sh_uint64 *bitmap64=(sh_uint64*)phys_plane->physical_bitmap; + sh_uint32 current_page=0; + sh_uint64 free_region_count=0; + sh_pez_debug_BITMAP bitmap_struct; + while (current_pagephysical_page_count) { + if ((current_page%64)==0) { + while (current_page+64<=phys_plane->physical_page_count && bitmap64[current_page/64]==0xFFFFFFFFFFFFFFFF) { + current_page+=64; + } + } + if (current_pagephysical_page_count && (sh_page_is_allocated(phys_plane->physical_bitmap,current_page)==0)) { + sh_uint32 start=current_page; + while (current_pagephysical_page_count && sh_page_is_allocated(phys_plane->physical_bitmap,current_page)==0) { + current_page++; + } + sh_uint32 size=current_page-start; + bitmap_struct.regions[free_region_count].start=start; + bitmap_struct.regions[free_region_count].size=size; + free_region_count++; + if (free_region_count==512) { + return SH_STATUS_TOO_MUCH_DATA; + } + } else if (current_pagephysical_page_count) { + current_page++; + } + } + bitmap_struct.count=(sh_uint32)free_region_count; + sh_pez_debug_send_bitmap(&bitmap_struct); + return SH_STATUS_SUCCESS; +} +static SH_STATUS sh_pez_internal_scan_boundary_radix_physical(sh_pez_PHYSICAL_PLANE *phys_plane) { + if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_log_fdebug(SH_LOG_SOURCE_PEZ,"Scanning boundary radix:\n"); + sh_uint64 free_region_count=0; + sh_uint64 stop=sh_page_get_physical_memory_amount_pages(); + sh_pez_debug_BOUNDARY_RADIX boundary_radix; + sh_bool is_in_region=SH_FALSE; + for (sh_iter64 i=0;iboundary_radix_tree,i,&boundary); + if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; + if (status==SH_STATUS_NOT_FOUND) continue; + if (is_in_region==SH_FALSE) { + boundary_radix.regions[free_region_count].pos_start=(sh_uint32)i; + sh_pez_internal_BOUNDARY_ENTRY boundary_unpack=sh_pez_internal_unpack_boundary(boundary); + boundary_radix.regions[free_region_count].idx_start=boundary_unpack.region_index; + boundary_radix.regions[free_region_count].prev_start=boundary_unpack.previous_index; + is_in_region=SH_TRUE; + continue; + } else { + sh_pez_internal_BOUNDARY_ENTRY boundary_unpack=sh_pez_internal_unpack_boundary(boundary); + // check to see if region is one page wide or more + if (boundary_unpack.region_index==boundary_radix.regions[free_region_count].idx_start) { + boundary_radix.regions[free_region_count].pos_end=(sh_uint32)i; + boundary_radix.regions[free_region_count].idx_end=boundary_unpack.region_index; + boundary_radix.regions[free_region_count].prev_end=boundary_unpack.previous_index; + is_in_region=SH_FALSE; + free_region_count++; + if (free_region_count==512) { + return SH_STATUS_TOO_MUCH_DATA; + } + continue; + } else { + boundary_radix.regions[free_region_count].pos_end=boundary_radix.regions[free_region_count].pos_start; + boundary_radix.regions[free_region_count].idx_end=boundary_radix.regions[free_region_count].idx_start; + boundary_radix.regions[free_region_count].prev_end=boundary_radix.regions[free_region_count].prev_start; + is_in_region=SH_FALSE; + free_region_count++; + if (free_region_count==512) { + return SH_STATUS_TOO_MUCH_DATA; + } + continue; + } + } + } + boundary_radix.count=(sh_uint32)free_region_count; + sh_pez_debug_send_boundary_radix(&boundary_radix); + return SH_STATUS_SUCCESS; +} +static SH_STATUS sh_pez_internal_check_physical_radix(sh_pez_PHYSICAL_PLANE *phys_plane) { + if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_page_MEM_STATS mem_stats; + SH_STATUS status=sh_page_get_memory_stats(&mem_stats); + for (sh_iter64 i=1;iregion_radix_tree,i,&index); + if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; + sh_slab_reg_phys_OBJECT_INDEX idx=(sh_uint32)index; + if (status==SH_STATUS_NOT_FOUND) continue; + while (SH_TRUE) { + reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,idx); + if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + if (reg->region_size_pages==0 || reg->start_page_index==0) { + sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that have either his address or his size starting at 0 (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i); + return SH_STATUS_PEZ_CORRUPTED; + } + if (reg->region_size_pages!=i) { + sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that doesn't have his placement (#%8u in size list) and size matching : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i); + return SH_STATUS_PEZ_CORRUPTED; + } + for (sh_uint64 y=reg->start_page_index;y<(sh_uint64)(reg->start_page_index+reg->region_size_pages);y++) { + if (sh_page_is_allocated(phys_plane->physical_bitmap,y)) { + sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that have a page allocated (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages; Concerned page: 0x%x\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i,((sh_uint64)y)*SH_PAGE_SIZE); + return SH_STATUS_PEZ_CORRUPTED; + } + } + if (!sh_page_is_allocated(phys_plane->physical_bitmap,reg->start_page_index-1)) { + sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that doesn't fully extend at left, found a page free at left (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages; Concerned page: 0x%x\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i,((sh_uint64)reg->start_page_index-1)*SH_PAGE_SIZE); + return SH_STATUS_PEZ_CORRUPTED; + } + if (!sh_page_is_allocated(phys_plane->physical_bitmap,reg->start_page_index+reg->region_size_pages) && reg->start_page_index+reg->region_size_pages!=sh_page_get_physical_memory_amount_pages()) { + sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that doesn't fully extend at right, found a page free at right (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages; Concerned page: 0x%x\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i,((sh_uint64)(reg->start_page_index+reg->region_size_pages))*SH_PAGE_SIZE); + return SH_STATUS_PEZ_CORRUPTED; + } + if (sh_pez_internal_unpack_index(reg->next_region_index)==0) { + break; + } else { + idx=sh_pez_internal_unpack_index(reg->next_region_index); + pos++; + } + } + } + return SH_STATUS_SUCCESS; +} +static SH_STATUS sh_pez_internal_scan_size_list_physical(sh_pez_PHYSICAL_PLANE *phys_plane) { + if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_page_MEM_STATS mem_stats; + SH_STATUS status=sh_page_get_memory_stats(&mem_stats); + sh_log_fdebug(SH_LOG_SOURCE_PEZ,"Scanning size list radix:\n"); + sh_uint64 free_region_count=0; + sh_log_byte(SH_PEZ_DEBUG_SIZE_LIST_BLOCK_HEADER); + sh_log_byte('\n'); + for (sh_iter64 i=1;iregion_radix_tree,i,&index); + if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; + sh_slab_reg_phys_OBJECT_INDEX idx=(sh_uint32)index; + if (status==SH_STATUS_NOT_FOUND) continue; + sh_uint64 size_list_region_count=0; + sh_pez_debug_SIZE_LIST size_list; + size_list.size=(sh_uint32)i; + while (SH_TRUE) { + reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,idx); + if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + size_list.regions[size_list_region_count].start=reg->start_page_index; + size_list.regions[size_list_region_count].size=reg->region_size_pages; + size_list.regions[size_list_region_count].idx=idx; + size_list.regions[size_list_region_count].next_idx=sh_pez_internal_unpack_index(reg->next_region_index); + free_region_count++; + size_list_region_count++; + if (size_list_region_count==512) { + return SH_STATUS_TOO_MUCH_DATA; + } + if (sh_pez_internal_unpack_index(reg->next_region_index)==0) { + break; + } else { + idx=sh_pez_internal_unpack_index(reg->next_region_index); + } + } + size_list.count=(sh_uint32)size_list_region_count; + sh_pez_debug_send_size_list(&size_list); + } + return SH_STATUS_SUCCESS; +} +static SH_STATUS sh_pez_internal_create_free_region_physical(sh_pez_PHYSICAL_PLANE *phys_plane,sh_uint32 region_start,sh_uint32 region_size,sh_pez_REGION_PHYSICAL_OBJECT **region) { + if (phys_plane==SH_NULLPTR || region_size==0 || region==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_reg_phys_OBJECT_INDEX reg_idx; + SH_STATUS status=sh_slab_reg_phys_alloc(phys_plane->slab_reg_phys,phys_plane->kernel_ptp,®_idx); + if (sh_status_error(status)) return status; + sh_pez_REGION_PHYSICAL_OBJECT *reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,reg_idx); + if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + reg->start_page_index=region_start; + reg->region_size_pages=region_size; + sh_page_VIRTUAL_ADDRESS head_index=0; + status=sh_radix_tree_get_value(&phys_plane->region_radix_tree,(sh_uint64)region_size,&head_index); + if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; + sh_page_VIRTUAL_ADDRESS new_boundary_val=sh_pez_internal_pack_boundary(reg_idx,0); + status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)region_start,new_boundary_val); + if (sh_status_error(status)) return status; + status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)region_start+region_size-1,new_boundary_val); + if (sh_status_error(status)) return status; + if (head_index!=0) { + sh_pez_REGION_PHYSICAL_OBJECT *old_head_ptr=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,(sh_uint32)head_index); + if (old_head_ptr==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + sh_page_VIRTUAL_ADDRESS updated_old_head_boundary=sh_pez_internal_pack_boundary((sh_uint32)head_index,reg_idx); + status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index,updated_old_head_boundary); + if (sh_status_error(status)) return status; + sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index+old_head_ptr->region_size_pages-1,updated_old_head_boundary); + if (sh_status_error(status)) return status; + sh_pez_internal_pack_index(reg->next_region_index,(sh_uint32)head_index); + } else { + sh_pez_internal_pack_index(reg->next_region_index,0); + } + status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->region_radix_tree,(sh_uint64)region_size,(sh_page_VIRTUAL_ADDRESS)reg_idx); + if (sh_status_error(status)) return status; + *region=reg; + return SH_STATUS_SUCCESS; +} +static SH_STATUS sh_pez_internal_check_region_integrity_physical(sh_pez_PHYSICAL_PLANE *phys_plane,sh_pez_REGION_PHYSICAL_OBJECT *reg,sh_bool *optional_is_first,sh_slab_reg_phys_OBJECT_INDEX *optional_reg_idx,sh_slab_reg_phys_OBJECT_INDEX *optional_prev_idx) { + if (phys_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_page_VIRTUAL_ADDRESS boundary_left; + sh_page_VIRTUAL_ADDRESS boundary_right; + SH_STATUS status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,reg->start_page_index,&boundary_left); + if (sh_status_error(status)) return status; + status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1,&boundary_right); + if (sh_status_error(status)) return status; + if (boundary_left!=boundary_right) return SH_STATUS_PEZ_CORRUPTED; + sh_pez_internal_BOUNDARY_ENTRY boundary=sh_pez_internal_unpack_boundary(boundary_left); + sh_bool should_be_first; + if (boundary.previous_index==0) { + should_be_first=SH_TRUE; + } else { + should_be_first=SH_FALSE; + } + sh_page_VIRTUAL_ADDRESS reg_idx; + status=sh_radix_tree_get_value(&phys_plane->region_radix_tree,reg->region_size_pages,®_idx); + if (sh_status_error(status)) return status; + sh_pez_REGION_PHYSICAL_OBJECT *region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,(sh_uint32)reg_idx); + if (region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + if ((!should_be_first && region==reg) || (should_be_first && region!=reg)) { + return SH_STATUS_PEZ_CORRUPTED; + } + if (optional_is_first!=SH_NULLPTR) { + *optional_is_first=should_be_first; + } + if (optional_reg_idx!=SH_NULLPTR) { + *optional_reg_idx=boundary.region_index; + } + if (optional_prev_idx!=SH_NULLPTR) { + *optional_prev_idx=boundary.previous_index; + } + return SH_STATUS_SUCCESS; +} +static SH_STATUS sh_pez_internal_delete_free_region_physical(sh_pez_PHYSICAL_PLANE *phys_plane,sh_pez_REGION_PHYSICAL_OBJECT *reg) { + if (phys_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_bool is_first; + sh_slab_reg_phys_OBJECT_INDEX reg_idx; + sh_slab_reg_phys_OBJECT_INDEX prev_idx; + SH_STATUS status=sh_pez_internal_check_region_integrity_physical(phys_plane,reg,&is_first,®_idx,&prev_idx); + if (sh_status_error(status)) return status; + if (is_first) { + // if region is the first of the list + if (sh_pez_internal_unpack_index(reg->next_region_index)==0) { + // if region was alone in his list + // delete radix entry + status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->region_radix_tree,reg->region_size_pages); + if (sh_status_error(status)) return status; + // delete left boundary + status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index); + if (sh_status_error(status)) return status; + // delete right boundary + if (reg->region_size_pages!=1) { + status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); + if (sh_status_error(status)) return status; + } + // delete region object + status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; + } else { + // if region isn't alone in his list + // obtain next region informations + sh_slab_reg_phys_OBJECT_INDEX next_reg_idx=sh_pez_internal_unpack_index(reg->next_region_index); + sh_pez_REGION_PHYSICAL_OBJECT *next_region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,next_reg_idx); + if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + // insert next region as first inside size list + status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->region_radix_tree,reg->region_size_pages,next_reg_idx); + if (sh_status_error(status)) return status; + // update left boundary of next region + sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,0); + status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region); + if (sh_status_error(status)) return status; + // update right boundary of next region + status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region); + if (sh_status_error(status)) return status; + // delete left boundary + status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index); + if (sh_status_error(status)) return status; + // delete right boundary + if (reg->region_size_pages!=1) { + status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); + if (sh_status_error(status)) return status; + } + // delete region object + status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; + } + } else { + // if region isn't the first of his list + // obtain information about prev region + sh_pez_REGION_PHYSICAL_OBJECT *prev_region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,prev_idx); + if (prev_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + if (sh_pez_internal_unpack_index(reg->next_region_index)==0) { + // if region is at the end of the size list + // put next_idx of prev region to 0 + sh_pez_internal_pack_index(prev_region->next_region_index,0); + // delete left boundary + status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index); + if (sh_status_error(status)) return status; + // delete right boundary + if (reg->region_size_pages!=1) { + status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); + if (sh_status_error(status)) return status; + } + // delete region object + status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; + } else { + // if region is in the middle of the size list + // obtain information about next region + sh_slab_reg_phys_OBJECT_INDEX next_reg_idx=sh_pez_internal_unpack_index(reg->next_region_index); + sh_pez_REGION_PHYSICAL_OBJECT *next_region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,next_reg_idx); + if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + // put next_idx of prev region to next_region_idx + sh_pez_internal_pack_index(prev_region->next_region_index,next_reg_idx); + // update left boundary of next region + sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,prev_idx); + status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region); + if (sh_status_error(status)) return status; + // update right boundary of next region + status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region); + if (sh_status_error(status)) return status; + // delete left boundary + status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index); + if (sh_status_error(status)) return status; + // delete right boundary + if (reg->region_size_pages!=1) { + status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); + if (sh_status_error(status)) return status; + } + // delete region object + status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; + } + } +} +SH_STATUS sh_pez_init_physical_plane(sh_uint8 *physical_bitmap,sh_uint64 physical_page_count,sh_slab_reg_phys_SLAB_ALLOCATOR *slab_reg_phys,struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_pez_PHYSICAL_PLANE *phys_plane) { + if (physical_bitmap==SH_NULLPTR || physical_page_count==0 || slab_reg_phys==SH_NULLPTR || slab_radix_node==SH_NULLPTR || kernel_ptp==SH_NULLPTR || phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_radix_tree_init(slab_radix_node,kernel_ptp,&phys_plane->region_radix_tree,8); + sh_radix_tree_init(slab_radix_node,kernel_ptp,&phys_plane->boundary_radix_tree,8); + phys_plane->free_pages=0; + phys_plane->used_pages=0; + phys_plane->physical_bitmap=physical_bitmap; + phys_plane->physical_page_count=physical_page_count; + phys_plane->slab_reg_phys=slab_reg_phys; + phys_plane->slab_radix_node=slab_radix_node; + phys_plane->kernel_ptp=kernel_ptp; + sh_uint64 *bitmap64=(sh_uint64*)physical_bitmap; + sh_uint32 current_page=0; + while (current_pagefree_pages=(sh_uint32)mem_stats.free_pages; + phys_plane->used_pages=(sh_uint32)mem_stats.used_pages; + sh_pez_set_reference_phys_plane(phys_plane); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_pez_alloc_physical_pages(sh_pez_PHYSICAL_PLANE *phys_plane,sh_uint32 pages_count,sh_page_PHYSICAL_ADDRESS *address) { + if (phys_plane==SH_NULLPTR || pages_count==0 || address==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_page_VIRTUAL_ADDRESS reg_idx; + SH_STATUS status=sh_radix_tree_search_smallest_min_bound(phys_plane->slab_radix_node,&phys_plane->region_radix_tree,pages_count,®_idx); + if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; + if (status==SH_STATUS_NOT_FOUND) return SH_STATUS_OUT_OF_MEMORY; + sh_pez_REGION_PHYSICAL_OBJECT *reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,(sh_uint32)reg_idx); + if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + if (reg->region_size_pagesregion_size_pages==pages_count) { + // exact fit case + // save metadata for later modifications + sh_uint32 start=reg->start_page_index; + // delete free region + status=sh_pez_internal_delete_free_region_physical(phys_plane,reg); + if (sh_status_error(status)) return status; + // mark pages as occupied inside physical bitmap + status=sh_page_set_pages_range_bitmap(phys_plane->physical_bitmap,phys_plane->physical_page_count,start,pages_count,SH_TRUE); + if (sh_status_error(status)) return status; + // give the address + *address=((sh_page_PHYSICAL_ADDRESS)start)*SH_PAGE_SIZE; + } else if (reg->region_size_pages>pages_count) { + // best fit case + // save metadata for later modifications + sh_uint32 start=reg->start_page_index; + // compute new size and index + sh_uint32 new_start=reg->start_page_index+pages_count; + if (new_start==0) return SH_STATUS_PEZ_CORRUPTED; + sh_uint32 new_size=reg->region_size_pages-pages_count; + if (new_size==0) return SH_STATUS_PEZ_CORRUPTED; + // delete free region + status=sh_pez_internal_delete_free_region_physical(phys_plane,reg); + if (sh_status_error(status)) return status; + // mark pages as occupied inside physical bitmap + status=sh_page_set_pages_range_bitmap(phys_plane->physical_bitmap,phys_plane->physical_page_count,start,pages_count,SH_TRUE); + if (sh_status_error(status)) return status; + // create new free region with splitted part + sh_pez_REGION_PHYSICAL_OBJECT *new_reg; + status=sh_pez_internal_create_free_region_physical(phys_plane,new_start,new_size,&new_reg); + if (sh_status_error(status)) return status; + // give the address + *address=((sh_page_PHYSICAL_ADDRESS)start)*SH_PAGE_SIZE; + } + phys_plane->free_pages=phys_plane->free_pages-pages_count; + phys_plane->used_pages=phys_plane->used_pages+pages_count; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_pez_free_physical_pages(sh_pez_PHYSICAL_PLANE *phys_plane,sh_page_PHYSICAL_ADDRESS *address,sh_uint32 pages_count) { + if (phys_plane==SH_NULLPTR || address==SH_NULLPTR || pages_count==0) return SH_STATUS_INVALID_PARAMETER; + // save freeed region data + sh_uint32 start_reg=(sh_uint32)((*address)/SH_PAGE_SIZE); + if (start_reg==0) return SH_STATUS_INVALID_PARAMETER; + sh_uint32 size_reg=pages_count; + // checking if region is entirely used + for (sh_iter64 i=0;iphysical_bitmap,start_reg+i)) { + return SH_STATUS_PAGE_ALREADY_FREE; + } + } + // marking pages as free + SH_STATUS status=sh_page_set_pages_range_bitmap(phys_plane->physical_bitmap,phys_plane->physical_page_count,start_reg,size_reg,SH_FALSE); + if (sh_status_error(status)) return status; + // identifying neighbours + // left + sh_page_VIRTUAL_ADDRESS left_neighbour; + status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,start_reg-1,&left_neighbour); + if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; + sh_bool left_fusion; + if (status==SH_STATUS_NOT_FOUND) { + left_fusion=SH_FALSE; + } else { + left_fusion=SH_TRUE; + } + // right + sh_page_VIRTUAL_ADDRESS right_neighbour; + status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,start_reg+size_reg,&right_neighbour); + if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; + sh_bool right_fusion; + if (status==SH_STATUS_NOT_FOUND) { + right_fusion=SH_FALSE; + } else { + right_fusion=SH_TRUE; + } + // preparing fusion + sh_uint32 new_start=start_reg; + sh_uint32 new_size=size_reg; + // fusionning left region + if (left_fusion) { + // obtaining information about left region + sh_pez_internal_BOUNDARY_ENTRY left_boundary=sh_pez_internal_unpack_boundary(left_neighbour); + sh_slab_reg_phys_OBJECT_INDEX left_reg_idx=left_boundary.region_index; + sh_pez_REGION_PHYSICAL_OBJECT *left_reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,left_reg_idx); + if (left_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + // updating new region informations + new_start=left_reg->start_page_index; + if (new_start==0 || new_start>sh_page_get_physical_memory_amount_pages()) return SH_STATUS_PEZ_CORRUPTED; + new_size=new_size+left_reg->region_size_pages; + if (new_size==0 || new_size>sh_page_get_physical_memory_amount_pages()) return SH_STATUS_PEZ_CORRUPTED; + // deleting left region + status=sh_pez_internal_delete_free_region_physical(phys_plane,left_reg); + if (sh_status_error(status)) return status; + } + // fusionning right region + if (right_fusion) { + // obtaining information about right region + sh_pez_internal_BOUNDARY_ENTRY right_boundary=sh_pez_internal_unpack_boundary(right_neighbour); + sh_slab_reg_phys_OBJECT_INDEX right_reg_idx=right_boundary.region_index; + sh_pez_REGION_PHYSICAL_OBJECT *right_reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,right_reg_idx); + if (right_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + // updating new region informations + new_size=new_size+right_reg->region_size_pages; + if (new_size==0 || new_size>sh_page_get_physical_memory_amount_pages()) return SH_STATUS_PEZ_CORRUPTED; + // deleting right region + status=sh_pez_internal_delete_free_region_physical(phys_plane,right_reg); + if (sh_status_error(status)) return status; + } + // inserting new region + sh_pez_REGION_PHYSICAL_OBJECT *new_reg; + status=sh_pez_internal_create_free_region_physical(phys_plane,new_start,new_size,&new_reg); + if (sh_status_error(status)) return status; + phys_plane->free_pages=phys_plane->free_pages+pages_count; + phys_plane->used_pages=phys_plane->used_pages-pages_count; + *address=0; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_pez_debug_physical(sh_pez_PHYSICAL_PLANE *phys_plane) { + sh_pez_internal_check_physical_radix(phys_plane); + sh_pez_internal_scan_physical_bitmap(phys_plane); + sh_pez_internal_scan_size_list_physical(phys_plane); + sh_pez_internal_scan_boundary_radix_physical(phys_plane); + return SH_STATUS_SUCCESS; +} +static SH_STATUS sh_pez_internal_create_free_region_virtual(sh_pez_VIRTUAL_PLANE *virt_plane,sh_uint32 region_start,sh_uint32 region_size,sh_pez_REGION_VIRTUAL_OBJECT **region) { + if (virt_plane==SH_NULLPTR || region_size==0 || region==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_reg_virt_OBJECT_INDEX reg_idx; + SH_STATUS status=sh_slab_reg_virt_alloc(virt_plane->slab_reg_virt,virt_plane->kernel_ptp,®_idx); + if (sh_status_error(status)) return status; + sh_pez_REGION_VIRTUAL_OBJECT *reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,reg_idx); + if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + reg->start_page_index=region_start; + reg->region_size_pages=region_size; + sh_page_VIRTUAL_ADDRESS head_index=0; + status=sh_radix_tree_get_value(&virt_plane->region_radix_tree,(sh_uint64)region_size,&head_index); + if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; + sh_page_VIRTUAL_ADDRESS new_boundary_val=sh_pez_internal_pack_boundary(reg_idx,0); + status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)region_start,new_boundary_val); + if (sh_status_error(status)) return status; + status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)region_start+region_size-1,new_boundary_val); + if (sh_status_error(status)) return status; + if (head_index!=0) { + sh_pez_REGION_PHYSICAL_OBJECT *old_head_ptr=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,(sh_uint32)head_index); + if (old_head_ptr==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + sh_page_VIRTUAL_ADDRESS updated_old_head_boundary=sh_pez_internal_pack_boundary((sh_uint32)head_index,reg_idx); + status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index,updated_old_head_boundary); + if (sh_status_error(status)) return status; + sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index+old_head_ptr->region_size_pages-1,updated_old_head_boundary); + if (sh_status_error(status)) return status; + reg->next_region_index=(sh_uint32)head_index; + } else { + reg->next_region_index=0; + } + status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->region_radix_tree,(sh_uint64)region_size,(sh_page_VIRTUAL_ADDRESS)reg_idx); + if (sh_status_error(status)) return status; + *region=reg; + return SH_STATUS_SUCCESS; +} +static SH_STATUS sh_pez_internal_check_region_integrity_virtual(sh_pez_VIRTUAL_PLANE *virt_plane,sh_pez_REGION_VIRTUAL_OBJECT *reg,sh_bool *optional_is_first,sh_slab_reg_virt_OBJECT_INDEX *optional_reg_idx,sh_slab_reg_virt_OBJECT_INDEX *optional_prev_idx) { + if (virt_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_page_VIRTUAL_ADDRESS boundary_left; + sh_page_VIRTUAL_ADDRESS boundary_right; + SH_STATUS status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,reg->start_page_index,&boundary_left); + if (sh_status_error(status)) return status; + status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1,&boundary_right); + if (sh_status_error(status)) return status; + if (boundary_left!=boundary_right) return SH_STATUS_PEZ_CORRUPTED; + sh_pez_internal_BOUNDARY_ENTRY boundary=sh_pez_internal_unpack_boundary(boundary_left); + sh_bool should_be_first; + if (boundary.previous_index==0) { + should_be_first=SH_TRUE; + } else { + should_be_first=SH_FALSE; + } + sh_page_VIRTUAL_ADDRESS reg_idx; + status=sh_radix_tree_get_value(&virt_plane->region_radix_tree,reg->region_size_pages,®_idx); + if (sh_status_error(status)) return status; + sh_pez_REGION_VIRTUAL_OBJECT *region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,(sh_uint32)reg_idx); + if (region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + if ((!should_be_first && region==reg) || (should_be_first && region!=reg)) { + return SH_STATUS_PEZ_CORRUPTED; + } + if (optional_is_first!=SH_NULLPTR) { + *optional_is_first=should_be_first; + } + if (optional_reg_idx!=SH_NULLPTR) { + *optional_reg_idx=boundary.region_index; + } + if (optional_prev_idx!=SH_NULLPTR) { + *optional_prev_idx=boundary.previous_index; + } + return SH_STATUS_SUCCESS; +} +static SH_STATUS sh_pez_internal_delete_free_region_virtual(sh_pez_VIRTUAL_PLANE *virt_plane,sh_pez_REGION_VIRTUAL_OBJECT *reg) { + if (virt_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_bool is_first; + sh_slab_reg_virt_OBJECT_INDEX reg_idx; + sh_slab_reg_virt_OBJECT_INDEX prev_idx; + SH_STATUS status=sh_pez_internal_check_region_integrity_virtual(virt_plane,reg,&is_first,®_idx,&prev_idx); + if (sh_status_error(status)) return status; + if (is_first) { + // if region is the first of the list + if (reg->next_region_index==0) { + // if region was alone in his list + // delete radix entry + status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->region_radix_tree,reg->region_size_pages); + if (sh_status_error(status)) return status; + // delete left boundary + status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index); + if (sh_status_error(status)) return status; + // delete right boundary + if (reg->region_size_pages!=1) { + status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); + if (sh_status_error(status)) return status; + } + // delete region object + status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; + } else { + // if region isn't alone in his list + // obtain next region informations + sh_slab_reg_virt_OBJECT_INDEX next_reg_idx=reg->next_region_index; + sh_pez_REGION_VIRTUAL_OBJECT *next_region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,next_reg_idx); + if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + // insert next region as first inside size list + status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->region_radix_tree,reg->region_size_pages,next_reg_idx); + if (sh_status_error(status)) return status; + // update left boundary of next region + sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,0); + status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region); + if (sh_status_error(status)) return status; + // update right boundary of next region + status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region); + if (sh_status_error(status)) return status; + // delete left boundary + status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index); + if (sh_status_error(status)) return status; + // delete right boundary + if (reg->region_size_pages!=1) { + status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); + if (sh_status_error(status)) return status; + } + // delete region object + status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; + } + } else { + // if region isn't the first of his list + // obtain information about prev region + sh_pez_REGION_VIRTUAL_OBJECT *prev_region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,prev_idx); + if (prev_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + if (reg->next_region_index==0) { + // if region is at the end of the size list + // put next_idx of prev region to 0 + prev_region->next_region_index=0; + // delete left boundary + status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index); + if (sh_status_error(status)) return status; + // delete right boundary + if (reg->region_size_pages!=1) { + status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); + if (sh_status_error(status)) return status; + } + // delete region object + status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; + } else { + // if region is in the middle of the size list + // obtain information about next region + sh_slab_reg_virt_OBJECT_INDEX next_reg_idx=reg->next_region_index; + sh_pez_REGION_VIRTUAL_OBJECT *next_region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,next_reg_idx); + if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + // put next_idx of prev region to next_region_idx + prev_region->next_region_index=next_reg_idx; + // update left boundary of next region + sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,prev_idx); + status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region); + if (sh_status_error(status)) return status; + // update right boundary of next region + status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region); + if (sh_status_error(status)) return status; + // delete left boundary + status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index); + if (sh_status_error(status)) return status; + // delete right boundary + if (reg->region_size_pages!=1) { + status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1); + if (sh_status_error(status)) return status; + } + // delete region object + status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; + } + } +} +SH_STATUS sh_pez_init_virtual_plane(sh_page_VIRTUAL_ADDRESS plane_offset,sh_slab_reg_virt_SLAB_ALLOCATOR *slab_reg_virt,struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_page_PAGE_TABLE_POOL *reference_ptp,sh_pez_VIRTUAL_PLANE *virt_plane) { + if (slab_reg_virt==SH_NULLPTR || slab_radix_node==SH_NULLPTR || kernel_ptp==SH_NULLPTR || reference_ptp==SH_NULLPTR || virt_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_radix_tree_init(slab_radix_node,kernel_ptp,&virt_plane->region_radix_tree,8); + sh_radix_tree_init(slab_radix_node,kernel_ptp,&virt_plane->boundary_radix_tree,8); + virt_plane->free_pages=0xFFFFFFFF-1; + virt_plane->used_pages=0; + virt_plane->slab_reg_virt=slab_reg_virt; + virt_plane->slab_radix_node=slab_radix_node; + virt_plane->kernel_ptp=kernel_ptp; + virt_plane->reference_ptp=reference_ptp; + virt_plane->plane_offset=plane_offset; + sh_pez_REGION_VIRTUAL_OBJECT *reg; + SH_STATUS status=sh_pez_internal_create_free_region_virtual(virt_plane,0,0xFFFFFFFF-1,®); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_pez_alloc_virtual_pages(sh_pez_VIRTUAL_PLANE *virt_plane,sh_uint32 pages_count,sh_page_VIRTUAL_ADDRESS *address) { + if (virt_plane==SH_NULLPTR || pages_count==0 || address==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_page_VIRTUAL_ADDRESS reg_idx; + SH_STATUS status=sh_radix_tree_search_smallest_min_bound(virt_plane->slab_radix_node,&virt_plane->region_radix_tree,pages_count,®_idx); + if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; + if (status==SH_STATUS_NOT_FOUND) return SH_STATUS_OUT_OF_MEMORY; + sh_pez_REGION_VIRTUAL_OBJECT *reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,(sh_uint32)reg_idx); + if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + if (reg->region_size_pagesregion_size_pages==pages_count) { + // exact fit case + // save metadata for later modifications + sh_uint32 start=reg->start_page_index; + // delete free region + status=sh_pez_internal_delete_free_region_virtual(virt_plane,reg); + if (sh_status_error(status)) return status; + // give the address + *address=((sh_page_VIRTUAL_ADDRESS)start)*SH_PAGE_SIZE+virt_plane->plane_offset; + } else if (reg->region_size_pages>pages_count) { + // best fit case + // save metadata for later modifications + sh_uint32 start=reg->start_page_index; + // compute new size and index + sh_uint32 new_start=reg->start_page_index+pages_count; + if (new_start==0) return SH_STATUS_PEZ_CORRUPTED; + sh_uint32 new_size=reg->region_size_pages-pages_count; + if (new_size==0) return SH_STATUS_PEZ_CORRUPTED; + // delete free region + status=sh_pez_internal_delete_free_region_virtual(virt_plane,reg); + if (sh_status_error(status)) return status; + // create new free region with splitted part + sh_pez_REGION_VIRTUAL_OBJECT *new_reg; + status=sh_pez_internal_create_free_region_virtual(virt_plane,new_start,new_size,&new_reg); + if (sh_status_error(status)) return status; + // give the address + *address=((sh_page_VIRTUAL_ADDRESS)start)*SH_PAGE_SIZE+virt_plane->plane_offset; + } + virt_plane->free_pages=virt_plane->free_pages-pages_count; + virt_plane->used_pages=virt_plane->used_pages+pages_count; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_pez_free_virtual_pages(sh_pez_VIRTUAL_PLANE *virt_plane,sh_page_VIRTUAL_ADDRESS *address,sh_uint32 pages_count) { + if (virt_plane==SH_NULLPTR || address==SH_NULLPTR || pages_count==0) return SH_STATUS_INVALID_PARAMETER; + // save freeed region data + sh_uint32 start_reg=(sh_uint32)((*address)/SH_PAGE_SIZE-virt_plane->plane_offset); + sh_uint32 size_reg=pages_count; + // identifying neighbours + // left + sh_page_VIRTUAL_ADDRESS left_neighbour; + SH_STATUS status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,start_reg-1,&left_neighbour); + if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; + sh_bool left_fusion; + if (status==SH_STATUS_NOT_FOUND) { + left_fusion=SH_FALSE; + } else { + left_fusion=SH_TRUE; + } + // right + sh_page_VIRTUAL_ADDRESS right_neighbour; + status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,start_reg+size_reg,&right_neighbour); + if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status; + sh_bool right_fusion; + if (status==SH_STATUS_NOT_FOUND) { + right_fusion=SH_FALSE; + } else { + right_fusion=SH_TRUE; + } + // preparing fusion + sh_uint32 new_start=start_reg; + sh_uint32 new_size=size_reg; + // fusionning left region + if (left_fusion) { + // obtaining information about left region + sh_pez_internal_BOUNDARY_ENTRY left_boundary=sh_pez_internal_unpack_boundary(left_neighbour); + sh_slab_reg_virt_OBJECT_INDEX left_reg_idx=left_boundary.region_index; + sh_pez_REGION_VIRTUAL_OBJECT *left_reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,left_reg_idx); + if (left_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + // updating new region informations + new_start=left_reg->start_page_index; + new_size=new_size+left_reg->region_size_pages; + if (new_size==0) return SH_STATUS_PEZ_CORRUPTED; + // deleting left region + status=sh_pez_internal_delete_free_region_virtual(virt_plane,left_reg); + if (sh_status_error(status)) return status; + } + // fusionning right region + if (right_fusion) { + // obtaining information about right region + sh_pez_internal_BOUNDARY_ENTRY right_boundary=sh_pez_internal_unpack_boundary(right_neighbour); + sh_slab_reg_virt_OBJECT_INDEX right_reg_idx=right_boundary.region_index; + sh_pez_REGION_VIRTUAL_OBJECT *right_reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,right_reg_idx); + if (right_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + // updating new region informations + new_size=new_size+right_reg->region_size_pages; + if (new_size==0) return SH_STATUS_PEZ_CORRUPTED; + // deleting right region + status=sh_pez_internal_delete_free_region_virtual(virt_plane,right_reg); + if (sh_status_error(status)) return status; + } + // inserting new region + sh_pez_REGION_VIRTUAL_OBJECT *new_reg; + status=sh_pez_internal_create_free_region_virtual(virt_plane,new_start,new_size,&new_reg); + if (sh_status_error(status)) return status; + virt_plane->free_pages=virt_plane->free_pages+pages_count; + virt_plane->used_pages=virt_plane->used_pages-pages_count; + *address=0; + return SH_STATUS_SUCCESS; +} diff --git a/shelter/lib/src/memory/pez/pez_debug.c b/shelter/lib/src/memory/pez/pez_debug.c new file mode 100644 index 0000000..318846f --- /dev/null +++ b/shelter/lib/src/memory/pez/pez_debug.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "memory/pez/pez_debug.h" +#include "kernel/log.h" +SH_STATUS sh_pez_debug_send_alloc(sh_pez_debug_ALLOC *alloc) { + sh_log_uint8_hex(SH_PEZ_DEBUG_ALLOC_HEADER); + sh_log_byte('a'); + sh_log_uint32_hex(alloc->size_asked); + sh_log_byte('a'); + sh_log_uint32_hex(alloc->found_region_start_index); + sh_log_byte('a'); + sh_log_uint32_hex(alloc->found_region_size); + sh_log_byte('a'); + sh_log_uint8_hex(alloc->is_exact_fit); + sh_log_byte('a'); + sh_log_uint32_hex(alloc->remaining_size); + sh_log_byte('a'); + sh_log_uint32_hex(alloc->new_start); + sh_log_byte('a'); + sh_log_uint32_hex(alloc->reg_idx); + sh_log_byte('\n'); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_pez_debug_send_free(sh_pez_debug_FREE *free) { + sh_log_uint8_hex(SH_PEZ_DEBUG_FREE_HEADER); + sh_log_byte('a'); + sh_log_uint32_hex(free->region_start_freeed); + sh_log_byte('a'); + sh_log_uint32_hex(free->size_freeed); + sh_log_byte('a'); + sh_log_uint32_hex(free->left_region_idx); + sh_log_byte('a'); + sh_log_uint32_hex(free->right_region_idx); + sh_log_byte('a'); + sh_log_uint32_hex(free->left_region_size); + sh_log_byte('a'); + sh_log_uint32_hex(free->right_region_size); + sh_log_byte('a'); + sh_log_uint32_hex(free->left_region_start); + sh_log_byte('a'); + sh_log_uint32_hex(free->right_region_start); + sh_log_byte('a'); + sh_log_uint32_hex(free->final_inserted_region_idx); + sh_log_byte('a'); + sh_log_uint32_hex(free->final_inserted_region_start); + sh_log_byte('a'); + sh_log_uint32_hex(free->final_inserted_region_size); + sh_log_byte('a'); + sh_log_uint8_hex(free->was_right_region_object_freeed); + sh_log_byte('a'); + sh_log_uint8_hex(free->was_new_region_object_allocated); + sh_log_byte('a'); + sh_log_uint32_hex(free->new_reg_obj_start); + sh_log_byte('a'); + sh_log_uint32_hex(free->new_reg_obj_size); + sh_log_byte('\n'); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_pez_debug_send_size_list(sh_pez_debug_SIZE_LIST *size_list) { + sh_log_uint8_hex(SH_PEZ_DEBUG_SIZE_LIST_HEADER); + sh_log_byte('a'); + sh_log_uint32_hex(size_list->size); + sh_log_byte('a'); + sh_log_uint32_hex(size_list->count); + sh_log_byte('\n'); + for (sh_iter64 i=0;icount;i++) { + sh_log_uint32_hex(size_list->regions[i].start); + sh_log_byte('a'); + sh_log_uint32_hex(size_list->regions[i].size); + sh_log_byte('a'); + sh_log_uint32_hex(size_list->regions[i].idx); + sh_log_byte('a'); + sh_log_uint32_hex(size_list->regions[i].next_idx); + sh_log_byte('\n'); + } + sh_log_byte('\n'); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_pez_debug_send_boundary_radix(sh_pez_debug_BOUNDARY_RADIX *boundary_radix) { + sh_log_uint8_hex(SH_PEZ_DEBUG_BOUNDARY_RADIX_HEADER); + sh_log_byte('a'); + sh_log_uint32_hex(boundary_radix->count); + sh_log_byte('\n'); + for (sh_iter64 i=0;icount;i++) { + sh_log_uint32_hex(boundary_radix->regions[i].pos_start); + sh_log_byte('a'); + sh_log_uint32_hex(boundary_radix->regions[i].idx_start); + sh_log_byte('a'); + sh_log_uint32_hex(boundary_radix->regions[i].prev_start); + sh_log_byte('a'); + sh_log_uint32_hex(boundary_radix->regions[i].pos_end); + sh_log_byte('a'); + sh_log_uint32_hex(boundary_radix->regions[i].idx_end); + sh_log_byte('a'); + sh_log_uint32_hex(boundary_radix->regions[i].prev_end); + sh_log_byte('\n'); + } + sh_log_byte('\n'); + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_pez_debug_send_bitmap(sh_pez_debug_BITMAP *bitmap) { + sh_log_uint8_hex(SH_PEZ_DEBUG_BITMAP_REGIONS_HEADER); + sh_log_byte('a'); + sh_log_uint32_hex(bitmap->count); + sh_log_byte('\n'); + for (sh_iter64 i=0;icount;i++) { + sh_log_uint32_hex(bitmap->regions[i].start); + sh_log_byte('a'); + sh_log_uint32_hex(bitmap->regions[i].size); + sh_log_byte('\n'); + } + sh_log_byte('\n'); + return SH_STATUS_SUCCESS; +} diff --git a/shelter/lib/src/memory/pez/radix.c b/shelter/lib/src/memory/pez/radix.c new file mode 100644 index 0000000..2138c32 --- /dev/null +++ b/shelter/lib/src/memory/pez/radix.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "memory/pez/radix.h" +#include "memory/slabs/slab_radix_node.h" +SH_STATUS sh_radix_node_read_value(sh_radix_NODE *node,sh_uint8 index,sh_page_VIRTUAL_ADDRESS *value) { + if (node==SH_NULLPTR || index>15 || value==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_page_VIRTUAL_ADDRESS val=node->ptr[index]; + if (val==0) return SH_STATUS_NOT_FOUND; + *value=val; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_radix_node_set_value(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_radix_NODE *node,sh_uint8 index,sh_page_VIRTUAL_ADDRESS value) { + if (node==SH_NULLPTR || alloc==SH_NULLPTR|| index>15) return SH_STATUS_INVALID_PARAMETER; + sh_uint16 *bitmap_ptr=sh_slab_radix_node_get_node_bitmap(alloc,node); + if (bitmap_ptr==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + if (value!=0) { + *bitmap_ptr|=(1<ptr[index]=value; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_radix_tree_init(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_radix_TREE *tree, sh_uint8 depth) { + if (tree==SH_NULLPTR || alloc==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (depth>16) return SH_STATUS_INVALID_PARAMETER; + tree->depth=depth; + sh_radix_NODE *root_node; + SH_STATUS status=sh_slab_radix_node_alloc(alloc,ptp,&root_node); + if (sh_status_error(status)) return status; + for (sh_uint8 i=0;i<16;i++) root_node->ptr[i]=0; + tree->root_node=root_node; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_radix_tree_get_value(sh_radix_TREE *tree,sh_uint64 key,sh_page_VIRTUAL_ADDRESS *value) { + if (tree==SH_NULLPTR || tree->root_node==SH_NULLPTR || value==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_radix_NODE *current_node=tree->root_node; + sh_page_VIRTUAL_ADDRESS current_val=0; + SH_STATUS status; + for (sh_uint8 level=0;leveldepth;level++) { + sh_uint8 shift=(sh_uint8)((tree->depth-1-level)*4); + sh_uint8 index=(sh_uint8)((key>>shift)&0x0F); + status=sh_radix_node_read_value(current_node,index,¤t_val); + if (sh_status_error(status)) { + return status; + } + if (level<(tree->depth-1)) { + current_node=(sh_radix_NODE *)current_val; + } + } + *value=current_val; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_radix_tree_insert_value(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_radix_TREE *tree,sh_uint64 key,sh_page_VIRTUAL_ADDRESS value) { + if (tree==SH_NULLPTR || tree->root_node==SH_NULLPTR || alloc==SH_NULLPTR || ptp==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_radix_NODE *current_node=tree->root_node; + SH_STATUS status; + for (sh_uint8 level=0;leveldepth;level++) { + sh_uint8 shift=(sh_uint8)((tree->depth-1-level)*4); + sh_uint8 index=(sh_uint8)((key>>shift)&0x0F); + if (level==(tree->depth-1)) { + return sh_radix_node_set_value(alloc,current_node,index,value); + } + sh_page_VIRTUAL_ADDRESS next_node_va; + status=sh_radix_node_read_value(current_node,index,&next_node_va); + if (status==SH_STATUS_NOT_FOUND) { + sh_radix_NODE *new_node; + status=sh_slab_radix_node_alloc(alloc,ptp,&new_node); + if (sh_status_error(status)) return status; + sh_uint8 i=0; + while(i<16) { + new_node->ptr[i]=0; + i++; + } + status=sh_radix_node_set_value(alloc,current_node,index,(sh_page_VIRTUAL_ADDRESS)new_node); + if (sh_status_error(status)) return status; + current_node=new_node; + } else if (status==SH_STATUS_SUCCESS) { + current_node=(sh_radix_NODE *)next_node_va; + } else { + return status; + } + } + return SH_STATUS_SUCCESS; +} +static SH_STATUS sh_radix_tree_delete_recursive(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_radix_NODE *current_node,sh_uint64 key,sh_uint8 depth,sh_uint8 current_level,sh_bool *is_empty_out) { + sh_uint8 shift=(sh_uint8)((depth-1-current_level)*4); + sh_uint8 index=(sh_uint8)((key>>shift)&0x0F); + SH_STATUS status; + if (current_level==(depth-1)) { + status=sh_radix_node_set_value(alloc,current_node,index,0); + if (sh_status_error(status)) return status; + } else { + sh_page_VIRTUAL_ADDRESS next_node_va=current_node->ptr[index]; + if (next_node_va==0) return SH_STATUS_NOT_FOUND; + sh_radix_NODE *next_node=(sh_radix_NODE *)next_node_va; + sh_bool child_is_empty=SH_FALSE; + status=sh_radix_tree_delete_recursive(alloc,next_node,key,depth,current_level+1,&child_is_empty); + if (sh_status_error(status)) return status; + if (child_is_empty) { + status=sh_slab_radix_node_dealloc(alloc,next_node); + if (sh_status_error(status)) return status; + status=sh_radix_node_set_value(alloc,current_node,index,0); + if (sh_status_error(status)) return status; + } + } + sh_bool empty=SH_TRUE; + for (sh_uint8 i=0;i<16;i++) { + if (current_node->ptr[i]!=0) { + empty=SH_FALSE; + break; + } + } + if (is_empty_out) *is_empty_out=empty; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_radix_tree_delete_value(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_radix_TREE *tree,sh_uint64 key) { + if (tree==SH_NULLPTR || tree->root_node==SH_NULLPTR || alloc==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_bool root_is_empty=SH_FALSE; + return sh_radix_tree_delete_recursive(alloc,tree->root_node,key,tree->depth,0,&root_is_empty); +} +SH_STATUS sh_radix_tree_search_smallest_min_bound(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_radix_TREE *tree,sh_uint64 lower_bound_key,sh_page_VIRTUAL_ADDRESS *value){ + if (tree==SH_NULLPTR || tree->root_node==SH_NULLPTR || alloc==SH_NULLPTR || value==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_uint8 depth=tree->depth; + sh_radix_NODE *node_stack[16]; + sh_uint8 idx_stack[16]; + sh_uint8 level=0; + node_stack[0]=tree->root_node; + while (level>shift)&0x0F); + idx_stack[level]=index; + if (level==depth-1) { + sh_radix_NODE *node=node_stack[level]; + sh_uint16 *bmp=sh_slab_radix_node_get_node_bitmap(alloc,node); + sh_uint16 mask=(sh_uint16)(0xFFFF<ptr[best]; + return SH_STATUS_SUCCESS; + } + goto backtrack; + } + sh_radix_NODE *node=node_stack[level]; + sh_page_VIRTUAL_ADDRESS child_va=node->ptr[index]; + if (child_va!=0) { + node_stack[level+1]=(sh_radix_NODE *)child_va; + level++; + continue; + } + goto try_next_sibling; + try_next_sibling:; + sh_uint16 *bmp=sh_slab_radix_node_get_node_bitmap(alloc,node); + sh_uint8 next_index=index+1; + if (next_index>15) goto backtrack; + sh_uint16 mask=(sh_uint16)(0xFFFF<ptr[best]; + idx_stack[level]=best; + level++; + while (levelptr[min_idx]; + return SH_STATUS_SUCCESS; + } + node_stack[level+1]=(sh_radix_NODE *)cur->ptr[min_idx]; + level++; + } + backtrack: + if (level==0) return SH_STATUS_NOT_FOUND; + level--; + node=node_stack[level]; + index=idx_stack[level]; + goto try_next_sibling; + } + return SH_STATUS_NOT_FOUND; +} diff --git a/shelter/lib/src/memory/ring.c b/shelter/lib/src/memory/ring.c new file mode 100644 index 0000000..21734f8 --- /dev/null +++ b/shelter/lib/src/memory/ring.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "memory/ring.h" +SH_STATUS sh_ring_write_byte(sh_ring_RING_BUFFER_HEADER *ring_buffer,sh_uint8 byte) { + if (ring_buffer==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (ring_buffer->buffer_bytes_size==0 || ring_buffer->data_start==SH_NULLPTR) return SH_STATUS_SUCCESS; + sh_uint32 next_head=(ring_buffer->head+1)%ring_buffer->buffer_bytes_size; + if (next_head==ring_buffer->tail) { + ring_buffer->tail=(ring_buffer->tail+1)%ring_buffer->buffer_bytes_size; + } + ring_buffer->data_start[ring_buffer->head]=byte; + ring_buffer->head=next_head; + ring_buffer->total_bytes_written++; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_ring_write_string(sh_ring_RING_BUFFER_HEADER *ring_buffer,char *string) { + if (ring_buffer==SH_NULLPTR || string==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (ring_buffer->buffer_bytes_size==0 || ring_buffer->data_start==SH_NULLPTR) return SH_STATUS_SUCCESS; + for (sh_uint32 i=0;string[i]!='\0';i++) { + sh_uint32 next_head=(ring_buffer->head+1)%ring_buffer->buffer_bytes_size; + if (next_head==ring_buffer->tail) { + ring_buffer->tail=(ring_buffer->tail+1)%ring_buffer->buffer_bytes_size; + } + ring_buffer->data_start[ring_buffer->head]=(sh_uint8)string[i]; + ring_buffer->head=next_head; + } + return SH_STATUS_SUCCESS; +} diff --git a/shelter/lib/src/memory/slabs/slab_generic.c b/shelter/lib/src/memory/slabs/slab_generic.c new file mode 100644 index 0000000..5856b31 --- /dev/null +++ b/shelter/lib/src/memory/slabs/slab_generic.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "memory/slabs/slab_generic.h" +#include "std/mem.h" +#include "kernel/log.h" +static sh_uint8 sig[8]=SH_SLAB_GENERIC_SLAB_SIG; +static sh_uint64 object_size_bytes[8]=SH_SLAB_GENERIC_OBJECT_SIZE_BYTES; +static sh_uint64 actual_objects_per_slab[8]=SH_SLAB_GENERIC_ACTUAL_OBJECTS_PER_SLAB; +static sh_uint64 bitmap_init[8]=SH_SLAB_GENERIC_SLAB_BITMAP_INIT; +static sh_uint64 slab_data_pages[8]=SH_SLAB_GENERIC_SLAB_DATA_PAGES; +SH_STATUS sh_slab_generic_alloc_init(sh_uint8 level,struct sh_slab_generic_SLAB_ALLOCATOR* slab_alloc,sh_pba_PAGE_BLOCK_ALLOCATOR *pba) { + if (slab_alloc==SH_NULLPTR || pba==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (level>7) return SH_STATUS_INVALID_PARAMETER; + slab_alloc->first_slab=SH_NULLPTR; + slab_alloc->slab_count=0; + slab_alloc->partial_head=SH_NULLPTR; + slab_alloc->pba=pba; + slab_alloc->level=level; + slab_alloc->object_size_bytes=object_size_bytes[level]; + slab_alloc->object_per_slab=SH_SLAB_GENERIC_OBJECTS_PER_SLAB; + slab_alloc->actual_object_per_slab=actual_objects_per_slab[level]; + slab_alloc->bitmap_init=bitmap_init[level]; + slab_alloc->slab_pages_count=slab_data_pages[level]; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_generic_add_slab(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_generic_SLAB** out_slab) { + if (alloc==SH_NULLPTR || out_slab==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; // phys_plane isn't checked because it can be SH_NULLPTR + sh_page_VIRTUAL_ADDRESS slab_va; + SH_STATUS status=sh_pba_alloc(alloc->pba,ptp,&slab_va); + if (sh_status_error(status)) return status; + if (alloc->slab_count==0) { + sh_slab_generic_SLAB *new_slab=(sh_slab_generic_SLAB*)slab_va; + for (sh_iter64 i=0;isig[i]=sig[i]; + new_slab->next_slab=SH_NULLPTR; + new_slab->prev_slab=SH_NULLPTR; + new_slab->next_partial=SH_NULLPTR; + new_slab->prev_partial=SH_NULLPTR; + new_slab->slab_index=alloc->slab_count; + new_slab->used_count=0; + for (sh_iter64 i=0;i<8;i++) new_slab->free_bitmap[i]=0; + new_slab->free_bitmap[0]|=alloc->bitmap_init; + alloc->first_slab=new_slab; + alloc->partial_head=new_slab; + alloc->slab_count++; + *out_slab=new_slab; + } else { + sh_slab_generic_SLAB *new_slab=(sh_slab_generic_SLAB*)slab_va; + for (sh_iter64 i=0;isig[i]=sig[i]; + new_slab->next_slab=alloc->first_slab; + new_slab->prev_slab=SH_NULLPTR; + alloc->first_slab->prev_slab=new_slab; + alloc->first_slab=new_slab; + new_slab->next_partial=alloc->partial_head; + new_slab->prev_partial=SH_NULLPTR; + alloc->partial_head->prev_partial=new_slab; + alloc->partial_head=new_slab; + new_slab->slab_index=alloc->slab_count; + new_slab->used_count=0; + for (sh_iter64 i=0;i<8;i++) new_slab->free_bitmap[i]=0; + new_slab->free_bitmap[0]|=alloc->bitmap_init; + alloc->slab_count++; + *out_slab=new_slab; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_generic_get_partial_slab(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL* ptp,sh_slab_generic_SLAB** found_slab) { + if (alloc==SH_NULLPTR ||ptp==SH_NULLPTR || found_slab==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (alloc->partial_head==SH_NULLPTR) { + *found_slab=SH_NULLPTR; + return SH_STATUS_NOT_FOUND; + } + sh_slab_generic_SLAB *slab=alloc->partial_head; + if (slab->used_count>=alloc->actual_object_per_slab) { + if (slab->next_partial!=SH_NULLPTR && slab->next_partial->used_countactual_object_per_slab) { + alloc->partial_head=slab->next_partial; + alloc->partial_head->prev_partial=SH_NULLPTR; + slab->next_partial=SH_NULLPTR; + slab->prev_partial=SH_NULLPTR; + *found_slab=alloc->partial_head; + return SH_STATUS_SUCCESS; + } else { + sh_slab_generic_SLAB *new_slab; + SH_STATUS status=sh_slab_generic_add_slab(alloc,ptp,&new_slab); + if (sh_status_error(status)) return status; + *found_slab=new_slab; + return SH_STATUS_NEW_SLAB_ADDED; + } + } + *found_slab=slab; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_generic_scan_slabs(struct sh_slab_generic_SLAB_ALLOCATOR* alloc) { + if (alloc==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_generic_SLAB* current=alloc->first_slab; + sh_slab_generic_SLAB* last_partial=SH_NULLPTR; + alloc->partial_head=SH_NULLPTR; + sh_log_debug("Slabs rescan triggered.",SH_LOG_SOURCE_SLAB); + while (current!=SH_NULLPTR) { + if (*(sh_uint64*)current->sig!=SH_SLAB_GENERIC_MAGIC) { + return SH_STATUS_FOUND_CORRUPTED_SLAB; + } + current->next_partial=SH_NULLPTR; + current->prev_partial=SH_NULLPTR; + if (current->used_countactual_object_per_slab) { + if (alloc->partial_head==SH_NULLPTR) { + alloc->partial_head=current; + last_partial=current; + } else { + last_partial->next_partial=current; + current->prev_partial=last_partial; + last_partial=current; + } + } + current=current->next_slab; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_generic_find_free_object(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,void** out,sh_slab_generic_OBJECT_INDEX_IN_SLAB* index_in_slab) { + if (alloc==SH_NULLPTR || ptp==SH_NULLPTR || out==SH_NULLPTR || index_in_slab==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_generic_SLAB *partial_slab; + sh_slab_generic_SLAB *new_slab; + SH_STATUS status=sh_slab_generic_get_partial_slab(alloc,ptp,&partial_slab); + if (status==SH_STATUS_RESCAN_NEEDED) { + status=sh_slab_generic_scan_slabs(alloc); + if (sh_status_error(status)) { + return status; + } + status=sh_slab_generic_get_partial_slab(alloc,ptp,&partial_slab); + } + if (status==SH_STATUS_NOT_FOUND) { + status=sh_slab_generic_add_slab(alloc,ptp,&new_slab); + if (sh_status_error(status)) { + return status; + } + status=sh_slab_generic_get_partial_slab(alloc,ptp,&partial_slab); + if (sh_status_error(status)) { + return status; + } + } else if (sh_status_error(status) && status!=SH_STATUS_NEW_SLAB_ADDED) { + return status; + } + sh_uint16 found_index=0; + sh_bool found=SH_FALSE; + for (sh_iter64 w=0;w<8;w++) { + sh_uint64 word=partial_slab->free_bitmap[w]; + sh_uint64 free_bits=(~word); + if (free_bits) { + sh_uint64 bit=(sh_uint64)__builtin_ctzll(free_bits); + found_index=(sh_uint16)((w<<6)+bit); + found=SH_TRUE; + break; + } + } + if (!found) { + return SH_STATUS_NOT_FOUND; + } + sh_uint8 *start_data=(sh_uint8*)partial_slab; + *out=start_data+found_index*alloc->object_size_bytes; + *index_in_slab=found_index; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_generic_alloc(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,void** out_obj) { + if (alloc==SH_NULLPTR || ptp==SH_NULLPTR || out_obj==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_uint8* object_ptr; + sh_slab_generic_OBJECT_INDEX_IN_SLAB index_in_slab; + SH_STATUS status=sh_slab_generic_find_free_object(alloc,ptp,(void*)&object_ptr,&index_in_slab); + if (sh_status_error(status)) return status; + if (object_ptr==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + for (sh_iter64 i=0;iobject_size_bytes;i++) object_ptr[i]=0; + sh_slab_generic_SLAB* slab=(sh_slab_generic_SLAB*)((sh_uint64)object_ptr & ~(0xFFFULL<level)); + if (*(sh_uint64*)slab->sig!=SH_SLAB_GENERIC_MAGIC) { + return SH_STATUS_FOUND_CORRUPTED_SLAB; + } + sh_uint16 word_idx=index_in_slab>>6; + sh_uint16 bit_idx=index_in_slab&63; + slab->free_bitmap[word_idx]|=(1ULL<used_count++; + *out_obj=object_ptr; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_generic_dealloc(struct sh_slab_generic_SLAB_ALLOCATOR* alloc,void *object_ptr) { + if (alloc==SH_NULLPTR || object_ptr==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if ((sh_uint64)object_ptr%alloc->object_size_bytes!=0) return SH_STATUS_INVALID_PARAMETER; + sh_slab_generic_SLAB* slab=(sh_slab_generic_SLAB*)((sh_uint64)object_ptr & ~(0xFFFULL<level)); + if (*(sh_uint64*)slab->sig!=SH_SLAB_GENERIC_MAGIC) { + return SH_STATUS_FOUND_CORRUPTED_SLAB; + } + sh_uint16 object_idx=(sh_uint16)(((sh_uint64)object_ptr & (0xFFFULL<level))/alloc->object_size_bytes); + sh_uint16 min_idx=(sh_uint16)(alloc->object_per_slab-alloc->actual_object_per_slab); + if (object_idx>6; + sh_uint16 bit=object_idx&63; + if (!(slab->free_bitmap[word] & (1ULL<free_bitmap[word]&= ~(1ULL<used_count==alloc->actual_object_per_slab) { + slab->prev_partial=SH_NULLPTR; + slab->next_partial=alloc->partial_head; + if (alloc->partial_head!=SH_NULLPTR) { + alloc->partial_head->prev_partial=slab; + } + alloc->partial_head=slab; + } + slab->used_count--; + for (sh_iter64 i=0;iobject_size_bytes;i++) ((sh_uint8*)object_ptr)[i]=0; + return SH_STATUS_SUCCESS; +} diff --git a/shelter/lib/src/memory/slabs/slab_radix_node.c b/shelter/lib/src/memory/slabs/slab_radix_node.c new file mode 100644 index 0000000..e222d70 --- /dev/null +++ b/shelter/lib/src/memory/slabs/slab_radix_node.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "memory/slabs/slab_radix_node.h" +#include "std/mem.h" +#include "kernel/log.h" +#include "memory/pez/pez.h" +static sh_uint8 sig[8]=SH_SLAB_RADIX_NODE_SLAB_SIG; +SH_STATUS sh_slab_radix_node_alloc_init(struct sh_slab_radix_node_SLAB_ALLOCATOR* slab_alloc,sh_pba_PAGE_BLOCK_ALLOCATOR *pba) { + if (slab_alloc==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + slab_alloc->first_slab=SH_NULLPTR; + slab_alloc->slab_count=0; + slab_alloc->partial_head=SH_NULLPTR; + slab_alloc->pba=pba; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_radix_node_add_slab(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_radix_node_SLAB** out_slab) { + if (alloc==SH_NULLPTR || out_slab==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_page_VIRTUAL_ADDRESS slab_va; + SH_STATUS status=sh_pba_alloc(alloc->pba,ptp,&slab_va); + if (sh_status_error(status)) return status; + if (alloc->slab_count==0) { + sh_slab_radix_node_SLAB *new_slab=(sh_slab_radix_node_SLAB*)slab_va; + for (sh_iter64 i=0;isig[i]=sig[i]; + new_slab->next_slab=SH_NULLPTR; + new_slab->prev_slab=SH_NULLPTR; + new_slab->next_partial=SH_NULLPTR; + new_slab->prev_partial=SH_NULLPTR; + new_slab->slab_index=alloc->slab_count; + new_slab->used_count=0; + for (sh_iter64 i=0;i<16;i++) new_slab->free_bitmap[i]=0; + new_slab->free_bitmap[0]|=0x3FFFFULL; + sh_mem_set_8(new_slab->padding,0x00,sizeof(new_slab->padding)); + sh_mem_set_8((sh_uint8*)new_slab->node_bitmap,0x00,sizeof(new_slab->node_bitmap)); + sh_mem_set_8((sh_uint8*)new_slab->nodes,0x00,sizeof(new_slab->nodes)); + alloc->first_slab=new_slab; + alloc->partial_head=new_slab; + alloc->slab_count++; + *out_slab=new_slab; + } else { + sh_slab_radix_node_SLAB *new_slab=(sh_slab_radix_node_SLAB*)slab_va; + for (sh_iter64 i=0;isig[i]=sig[i]; + new_slab->next_slab=alloc->first_slab; + new_slab->prev_slab=SH_NULLPTR; + alloc->first_slab->prev_slab=new_slab; + alloc->first_slab=new_slab; + new_slab->next_partial=alloc->partial_head; + new_slab->prev_partial=SH_NULLPTR; + alloc->partial_head->prev_partial=new_slab; + alloc->partial_head=new_slab; + new_slab->slab_index=alloc->slab_count; + new_slab->used_count=0; + for (sh_iter64 i=0;i<16;i++) new_slab->free_bitmap[i]=0; + new_slab->free_bitmap[0]|=0x3FFFFULL; + sh_mem_set_8(new_slab->padding,0x00,sizeof(new_slab->padding)); + sh_mem_set_8((sh_uint8*)new_slab->node_bitmap,0x00,sizeof(new_slab->node_bitmap)); + sh_mem_set_8((sh_uint8*)new_slab->nodes,0x00,sizeof(new_slab->nodes)); + alloc->slab_count++; + *out_slab=new_slab; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_radix_node_get_partial_slab(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL* ptp,sh_slab_radix_node_SLAB** found_slab) { + if (alloc==SH_NULLPTR ||ptp==SH_NULLPTR || found_slab==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (alloc->partial_head==SH_NULLPTR) { + *found_slab=SH_NULLPTR; + return SH_STATUS_NOT_FOUND; + } + sh_slab_radix_node_SLAB *slab=alloc->partial_head; + if (slab->used_count>=SH_SLAB_RADIX_NODE_ACTUAL_OBJECTS_PER_SLAB) { + if (slab->next_partial!=SH_NULLPTR && slab->next_partial->used_countpartial_head=slab->next_partial; + alloc->partial_head->prev_partial=SH_NULLPTR; + slab->next_partial=SH_NULLPTR; + slab->prev_partial=SH_NULLPTR; + *found_slab=alloc->partial_head; + return SH_STATUS_SUCCESS; + } else { + sh_slab_radix_node_SLAB *new_slab; + SH_STATUS status=sh_slab_radix_node_add_slab(alloc,ptp,&new_slab); + if (sh_status_error(status)) return status; + *found_slab=new_slab; + return SH_STATUS_NEW_SLAB_ADDED; + } + } + *found_slab=slab; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_radix_node_scan_slabs(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc) { + if (alloc==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_radix_node_SLAB* current=alloc->first_slab; + sh_slab_radix_node_SLAB* last_partial=SH_NULLPTR; + alloc->partial_head=SH_NULLPTR; + sh_log_debug("Slabs rescan triggered.",SH_LOG_SOURCE_SLAB); + while (current!=SH_NULLPTR) { + if (*(sh_uint64*)current->sig!=SH_SLAB_RADIX_NODE_MAGIC) { + return SH_STATUS_FOUND_CORRUPTED_SLAB; + } + current->used_count=0; + for (sh_iter64 w=0;w<16;w++) current->free_bitmap[w]=0; + current->free_bitmap[0]|=0x3FFFFULL; + for (sh_uint16 obj_idx=18;obj_idxnodes[obj_idx-18]; + sh_bool is_free=SH_TRUE; + sh_uint64* check_ptr=(sh_uint64*)node_ptr; + for (sh_uint8 b=0;b<(SH_SLAB_RADIX_NODE_OBJECT_SIZE_BYTES/8);b++) { + if (check_ptr[b]!=0) { + is_free=SH_FALSE; + break; + } + } + sh_uint16 word=obj_idx>>6; + sh_uint16 bit=obj_idx&63; + if (!is_free) { + current->free_bitmap[word]|=(1ULL<used_count++; + } + } + current->next_partial=SH_NULLPTR; + current->prev_partial=SH_NULLPTR; + if (current->used_countpartial_head==SH_NULLPTR) { + alloc->partial_head=current; + last_partial=current; + } else { + last_partial->next_partial=current; + current->prev_partial=last_partial; + last_partial=current; + } + } + current=current->next_slab; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_radix_node_find_free_object(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_radix_NODE** out,sh_slab_radix_node_NODE_INDEX_IN_SLAB* index_in_slab) { + if (alloc==SH_NULLPTR || ptp==SH_NULLPTR || out==SH_NULLPTR || index_in_slab==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_radix_node_SLAB *partial_slab; + sh_slab_radix_node_SLAB *new_slab; + SH_STATUS status=sh_slab_radix_node_get_partial_slab(alloc,ptp,&partial_slab); + if (status==SH_STATUS_RESCAN_NEEDED) { + status=sh_slab_radix_node_scan_slabs(alloc); + if (sh_status_error(status)) return status; + status=sh_slab_radix_node_get_partial_slab(alloc,ptp,&partial_slab); + } + if (status==SH_STATUS_NOT_FOUND) { + status=sh_slab_radix_node_add_slab(alloc,ptp,&new_slab); + if (sh_status_error(status)) return status; + status=sh_slab_radix_node_get_partial_slab(alloc,ptp,&partial_slab); + if (sh_status_error(status)) return status; + } else if (sh_status_error(status) && status!=SH_STATUS_NEW_SLAB_ADDED) { + return status; + } + sh_uint16 found_idx=0; + sh_bool bit_found=SH_FALSE; + for (sh_uint8 w=0;w<16;w++) { + if (partial_slab->free_bitmap[w]!=0xFFFFFFFFFFFFFFFFULL) { + for (sh_uint8 b=0;b<64;b++) { + if (!(partial_slab->free_bitmap[w] & (1ULL<nodes[found_idx-18]; + *index_in_slab=found_idx; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_radix_node_alloc(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_radix_NODE** out_obj) { + if (alloc==SH_NULLPTR || ptp==SH_NULLPTR || out_obj==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_radix_NODE *object_ptr; + sh_slab_radix_node_NODE_INDEX_IN_SLAB index_in_slab; + SH_STATUS status=sh_slab_radix_node_find_free_object(alloc,ptp,&object_ptr,&index_in_slab); + if (sh_status_error(status)) return status; + if (object_ptr==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + for (sh_iter64 i=0;i<16;i++) object_ptr->ptr[i]=0x0; + sh_slab_radix_node_SLAB* slab=(sh_slab_radix_node_SLAB*)((sh_uint64)object_ptr & ~0x1FFFFULL); + if (*(sh_uint64*)slab->sig!=SH_SLAB_RADIX_NODE_MAGIC) { + return SH_STATUS_FOUND_CORRUPTED_SLAB; + } + sh_uint16 word_idx=index_in_slab>>6; + sh_uint16 bit_idx=index_in_slab&63; + slab->free_bitmap[word_idx]|=(1ULL<node_bitmap[index_in_slab]=0; + slab->used_count++; + *out_obj=object_ptr; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_radix_node_dealloc(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_radix_NODE *object_ptr) { + if (alloc==SH_NULLPTR || object_ptr==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if ((sh_uint64)object_ptr%SH_SLAB_RADIX_NODE_OBJECT_SIZE_BYTES!=0) return SH_STATUS_INVALID_PARAMETER; + sh_slab_radix_node_SLAB* slab=(sh_slab_radix_node_SLAB*)((sh_uint64)object_ptr & ~0x1FFFFULL); + if (*(sh_uint64*)slab->sig!=SH_SLAB_RADIX_NODE_MAGIC) { + return SH_STATUS_FOUND_CORRUPTED_SLAB; + } + sh_uint16 object_idx=(sh_uint16)(((sh_uint64)object_ptr & 0x1FFFFULL)/SH_SLAB_RADIX_NODE_OBJECT_SIZE_BYTES); + if (object_idx<18) return SH_STATUS_INVALID_PARAMETER; + sh_uint16 word=object_idx>>6; + sh_uint16 bit=object_idx&63; + if (!(slab->free_bitmap[word] & (1ULL<free_bitmap[word]&= ~(1ULL<used_count==SH_SLAB_RADIX_NODE_ACTUAL_OBJECTS_PER_SLAB) { + slab->prev_partial=SH_NULLPTR; + slab->next_partial=alloc->partial_head; + if (alloc->partial_head!=SH_NULLPTR) { + alloc->partial_head->prev_partial=slab; + } + alloc->partial_head=slab; + } + slab->used_count--; + for (sh_iter64 i=0;i<16;i++) object_ptr->ptr[i]=0x0; + slab->node_bitmap[object_idx]=0; + return SH_STATUS_SUCCESS; +} +sh_uint16 *sh_slab_radix_node_get_node_bitmap(struct sh_slab_radix_node_SLAB_ALLOCATOR* alloc,sh_radix_NODE *object_ptr) { + if (alloc==SH_NULLPTR || object_ptr==SH_NULLPTR) return SH_NULLPTR; + if ((sh_uint64)object_ptr%SH_SLAB_RADIX_NODE_OBJECT_SIZE_BYTES!=0) return SH_NULLPTR; + sh_slab_radix_node_SLAB* slab=(sh_slab_radix_node_SLAB*)((sh_uint64)object_ptr & ~0x1FFFFULL); + if (*(sh_uint64*)slab->sig!=SH_SLAB_RADIX_NODE_MAGIC) { + return SH_NULLPTR; + } + sh_uint16 object_idx=(sh_uint16)(((sh_uint64)object_ptr & 0x1FFFFULL)/SH_SLAB_RADIX_NODE_OBJECT_SIZE_BYTES); + if (object_idx<18) return SH_NULLPTR; + return &slab->node_bitmap[object_idx]; +} diff --git a/shelter/lib/src/memory/slabs/slab_reg_phys.c b/shelter/lib/src/memory/slabs/slab_reg_phys.c new file mode 100644 index 0000000..b1778a2 --- /dev/null +++ b/shelter/lib/src/memory/slabs/slab_reg_phys.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "memory/slabs/slab_reg_phys.h" +#include "std/mem.h" +#include "kernel/log.h" +#include "memory/pez/pez.h" +SH_STATUS sh_slab_reg_phys_alloc_init(sh_slab_reg_phys_SLAB_ALLOCATOR* slab_alloc,sh_page_PAGE_TABLE_POOL *ptp) { + if (slab_alloc==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + slab_alloc->max_slabs=SH_SLAB_REG_PHYS_MAX_SLAB; + slab_alloc->slab_count=0; + slab_alloc->partial_head=SH_NULLPTR; + slab_alloc->slabs_data=SH_NULLPTR; + sh_page_VIRTUAL_ADDRESS header_va; + SH_STATUS status=sh_page_alloc_contiguous_extended(ptp,SH_SLAB_REG_PHYS_HEADER_LIST_SIZE_BYTES,&header_va,SH_PAGE_RW | SH_PAGE_PRESENT | SH_PAGE_NX,SH_PAGE_KERNEL_PERM_VA_BASE,(SH_PAGE_KERNEL_PERM_VA_END-SH_PAGE_KERNEL_PERM_VA_BASE+1-0x1000)); + if (sh_status_error(status)) return status; + slab_alloc->slabs_header=(sh_slab_reg_phys_SLAB_STRUCT*)header_va; + status=sh_mem_set_8((sh_uint8*)(slab_alloc->slabs_header),0x00,SH_SLAB_REG_PHYS_HEADER_LIST_SIZE_BYTES); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_reg_phys_add_slab(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_phys_SLAB_STRUCT** out_slab) { + if (alloc==SH_NULLPTR || out_slab==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (alloc->slab_count>=alloc->max_slabs) return SH_STATUS_SLAB_ALLOCATOR_FULL; + sh_page_PHYSICAL_ADDRESS slab_pa; + SH_STATUS status; + if (sh_pez_is_available()) { + sh_pez_PHYSICAL_PLANE *phys_plane=sh_pez_get_reference_phys_plane(); + status=sh_pez_alloc_physical_pages(phys_plane,SH_SLAB_REG_PHYS_SLAB_DATA_PAGES,&slab_pa); + if (sh_status_error(status)) return status; + } else { + status=sh_page_search_physical_contiguous_block_na(SH_SLAB_REG_PHYS_SLAB_DATA_PAGES,&slab_pa); + if (sh_status_error(status)) return status; + } + if (alloc->slabs_data==SH_NULLPTR && alloc->slab_count==0) { + sh_page_VIRTUAL_ADDRESS alloc_slab_data_va_base=SH_SLAB_REG_PHYS_DATA_VA; + status=sh_page_is_va_range_mapped_ptp(ptp,alloc_slab_data_va_base,SH_SLAB_REG_PHYS_MAX_SLAB*SH_SLAB_REG_PHYS_SLAB_DATA_SIZE_BYTES); + if (status!=SH_STATUS_VA_NOT_MAPPED) return status; + status=sh_page_map_contiguous_pages_range_ptp(ptp,alloc_slab_data_va_base,slab_pa,SH_PAGE_PRESENT | SH_PAGE_NX | SH_PAGE_RW,SH_SLAB_REG_PHYS_SLAB_DATA_SIZE_BYTES); + if (sh_status_error(status)) return status; + alloc->slabs_data=(sh_uint8*)alloc_slab_data_va_base; + sh_slab_reg_phys_SLAB_STRUCT *new_slab_header=alloc->slabs_header; + new_slab_header->slab_index=alloc->slab_count; + new_slab_header->used_count=0; + new_slab_header->prev=SH_NULLPTR; + new_slab_header->next=SH_NULLPTR; + for (sh_iter64 i=0;i<16;i++) new_slab_header->free_bitmap[i]=0; + new_slab_header->free_bitmap[0]|=1ULL; + new_slab_header->used_count=0; + status=sh_mem_set_8(alloc->slabs_data,0x00,SH_SLAB_REG_PHYS_SLAB_DATA_SIZE_BYTES); + if (sh_status_error(status)) return status; + if (!sh_pez_is_available()) { + sh_page_MEM_STATS mem_stats; + status=sh_page_get_memory_stats(&mem_stats); + if (sh_status_error(status)) return status; + status=sh_page_set_pages_range_bitmap((sh_uint8*)sh_page_get_physical_bitmap_ptr(),mem_stats.memory_total_pages,slab_pa/SH_PAGE_SIZE,SH_SLAB_REG_PHYS_SLAB_DATA_PAGES,SH_TRUE); + if (sh_status_error(status)) return status; + } + alloc->partial_head=alloc->slabs_header; + *out_slab=new_slab_header; + alloc->slab_count++; + } else if (alloc->slabs_data!=SH_NULLPTR && alloc->slab_count!=0) { + sh_page_VIRTUAL_ADDRESS alloc_slab_data_va=SH_SLAB_REG_PHYS_DATA_VA+alloc->slab_count*SH_SLAB_REG_PHYS_SLAB_DATA_SIZE_BYTES; + status=sh_page_is_va_range_mapped_ptp(ptp,alloc_slab_data_va,SH_SLAB_REG_PHYS_SLAB_DATA_SIZE_BYTES); + if (status!=SH_STATUS_VA_NOT_MAPPED) return status; + status=sh_page_map_contiguous_pages_range_ptp(ptp,alloc_slab_data_va,slab_pa,SH_PAGE_PRESENT | SH_PAGE_NX | SH_PAGE_RW,SH_SLAB_REG_PHYS_SLAB_DATA_SIZE_BYTES); + if (sh_status_error(status)) return status; + sh_slab_reg_phys_SLAB_STRUCT *new_slab_header=alloc->slabs_header+alloc->slab_count; + new_slab_header->slab_index=alloc->slab_count; + new_slab_header->used_count=0; + new_slab_header->prev=SH_NULLPTR; + new_slab_header->next=alloc->partial_head; + sh_slab_reg_phys_SLAB_STRUCT *next_slab_header=new_slab_header->next; + next_slab_header->prev=new_slab_header; + alloc->partial_head=new_slab_header; + for (sh_iter64 i=0;i<16;i++) new_slab_header->free_bitmap[i]=0; + new_slab_header->free_bitmap[0]|=1ULL; + new_slab_header->used_count=0; + status=sh_mem_set_8(alloc->slabs_data+alloc->slab_count*SH_SLAB_REG_PHYS_SLAB_DATA_SIZE_BYTES,0x00,SH_SLAB_REG_PHYS_SLAB_DATA_SIZE_BYTES); + if (sh_status_error(status)) return status; + if (!sh_pez_is_available()) { + sh_page_MEM_STATS mem_stats; + status=sh_page_get_memory_stats(&mem_stats); + if (sh_status_error(status)) return status; + status=sh_page_set_pages_range_bitmap((sh_uint8*)sh_page_get_physical_bitmap_ptr(),mem_stats.memory_total_pages,slab_pa/SH_PAGE_SIZE,SH_SLAB_REG_PHYS_SLAB_DATA_PAGES,SH_TRUE); + if (sh_status_error(status)) return status; + } + *out_slab=new_slab_header; + alloc->slab_count++; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_reg_phys_get_partial_slab(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL* ptp,sh_slab_reg_phys_SLAB_STRUCT** found_slab) { + if (alloc==SH_NULLPTR || ptp==SH_NULLPTR || found_slab==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (alloc->partial_head==SH_NULLPTR) { + *found_slab=SH_NULLPTR; + return SH_STATUS_NOT_FOUND; + } + sh_slab_reg_phys_SLAB_STRUCT* slab=alloc->partial_head; + if (slab->used_count>=SH_SLAB_REG_PHYS_ACTUAL_OBJECTS_PER_SLAB) { + if (slab->next!=SH_NULLPTR && slab->next->used_countpartial_head=slab->next; + alloc->partial_head->prev=SH_NULLPTR; + slab->next=SH_NULLPTR; + slab->prev=SH_NULLPTR; + *found_slab=alloc->partial_head; + return SH_STATUS_SUCCESS; + } else { + sh_slab_reg_phys_SLAB_STRUCT* new_slab; + SH_STATUS status=sh_slab_reg_phys_add_slab(alloc,ptp,&new_slab); + if (sh_status_error(status)) return status; + *found_slab=new_slab; + return SH_STATUS_NEW_SLAB_ADDED; + } + } + *found_slab=slab; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_reg_phys_scan_slabs(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc) { + if (alloc==SH_NULLPTR || alloc->slabs_data==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_reg_phys_SLAB_STRUCT* first_partial=SH_NULLPTR; + sh_slab_reg_phys_SLAB_STRUCT* last_partial=SH_NULLPTR; + sh_log_debug("Slabs rescan triggered.",SH_LOG_SOURCE_SLAB); + for (sh_uint16 i=0;islab_count;i++) { + sh_slab_reg_phys_SLAB_STRUCT* slab=alloc->slabs_header+i; + sh_uint8* slab_base=alloc->slabs_data+i*SH_SLAB_REG_PHYS_SLAB_DATA_SIZE_BYTES; + slab->used_count=0; + for (sh_iter64 w=0;w<16;w++) slab->free_bitmap[w]=0; + slab->free_bitmap[0]|=1ULL; + for (sh_uint16 obj_idx=1;obj_idx>6; + sh_uint16 bit=obj_idx&63; + slab->free_bitmap[word]&= ~(1ULL<>6; + sh_uint16 bit=obj_idx&63; + slab->free_bitmap[word]|=(1ULL<used_count++; + } + } + sh_log_fdebug(SH_LOG_SOURCE_SLAB,"Slab index: %2u; used count: %2u\n",slab->slab_index,slab->used_count); + slab->prev=SH_NULLPTR; + slab->next=SH_NULLPTR; + if (slab->used_count>0 && slab->used_countprev=last_partial; + last_partial->next=slab; + last_partial=slab; + } + } + } + alloc->partial_head=first_partial; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_reg_phys_find_free_object(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_phys_OBJECT_INDEX* out_ref) { + if (alloc==SH_NULLPTR || ptp==SH_NULLPTR || out_ref==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_reg_phys_SLAB_STRUCT *partial_slab; + sh_slab_reg_phys_SLAB_STRUCT *new_slab; + SH_STATUS status=sh_slab_reg_phys_get_partial_slab(alloc,ptp,&partial_slab); + if (status==SH_STATUS_RESCAN_NEEDED) { + status=sh_slab_reg_phys_scan_slabs(alloc); + if (sh_status_error(status)) return status; + status=sh_slab_reg_phys_get_partial_slab(alloc,ptp,&partial_slab); + } + if (status==SH_STATUS_NOT_FOUND) { + status=sh_slab_reg_phys_add_slab(alloc,ptp,&new_slab); + if (sh_status_error(status)) return status; + status=sh_slab_reg_phys_get_partial_slab(alloc,ptp,&partial_slab); + if (sh_status_error(status)) return status; + } else if (sh_status_error(status) && status!=SH_STATUS_NEW_SLAB_ADDED) { + return status; + } + sh_uint64 obj; + sh_bool obj_found=SH_FALSE; + for (sh_uint8 w=0;w<16;w++) { + sh_uint64 word=partial_slab->free_bitmap[w]; + if (word!=SH_UINT64_MAX) { + if (w==0) { + for (sh_uint8 bit=0;bit<64;bit++) { + if (!(word & (1ULL<slab_index,obj); + return SH_STATUS_SUCCESS; +} +void* sh_slab_reg_phys_ref_to_ptr(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_slab_reg_phys_OBJECT_INDEX ref) { + if (alloc==SH_NULLPTR) { + return SH_NULLPTR; + } + if (SH_SLAB_REG_PHYS_REF_SLAB(ref)>=alloc->slab_count || SH_SLAB_REG_PHYS_REF_OBJECT(ref)==0) { + return SH_NULLPTR; + } + sh_uint16 slab=SH_SLAB_REG_PHYS_REF_SLAB(ref); + sh_uint16 object=SH_SLAB_REG_PHYS_REF_OBJECT(ref); + if (object==0) { + return SH_NULLPTR; + } + return alloc->slabs_data+slab*SH_SLAB_REG_PHYS_SLAB_DATA_SIZE_BYTES+object*SH_SLAB_REG_PHYS_OBJECT_SIZE_BYTES; +} +SH_STATUS sh_slab_reg_phys_alloc(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_phys_OBJECT_INDEX* out_index) { + if (alloc==SH_NULLPTR || ptp==SH_NULLPTR || out_index==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_reg_phys_OBJECT_INDEX ref; + SH_STATUS status=sh_slab_reg_phys_find_free_object(alloc,ptp,&ref); + if (sh_status_error(status)) return status; + sh_uint16 slab_idx=SH_SLAB_REG_PHYS_REF_SLAB(ref); + sh_uint16 obj_idx=SH_SLAB_REG_PHYS_REF_OBJECT(ref); + sh_slab_reg_phys_SLAB_STRUCT* slab=alloc->slabs_header+slab_idx; + sh_uint16 word=obj_idx>>6; + sh_uint16 bit=obj_idx&63; + slab->free_bitmap[word]|=(1ULL<start_page_index=0; + obj->region_size_pages=0; + obj->next_region_index[0]=obj->next_region_index[1]=obj->next_region_index[2]=0; + obj->flags=0; + slab->used_count++; + *out_index=ref; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_reg_phys_dealloc(sh_slab_reg_phys_SLAB_ALLOCATOR* alloc,sh_slab_reg_phys_OBJECT_INDEX index) { + if (alloc==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (SH_SLAB_REG_PHYS_REF_OBJECT(index)==0) return SH_STATUS_INVALID_PARAMETER; + if (SH_SLAB_REG_PHYS_REF_SLAB(index)>=alloc->slab_count) return SH_STATUS_INVALID_PARAMETER; + sh_uint16 obj_idx=SH_SLAB_REG_PHYS_REF_OBJECT(index); + sh_uint16 word=obj_idx>>6; + sh_uint16 bit=obj_idx&63; + sh_slab_reg_phys_SLAB_STRUCT* slab_header=alloc->slabs_header+SH_SLAB_REG_PHYS_REF_SLAB(index); + if (!(slab_header->free_bitmap[word] & (1ULL<free_bitmap[word]&= ~(1ULL<used_count==SH_SLAB_REG_PHYS_ACTUAL_OBJECTS_PER_SLAB) { + slab_header->prev=SH_NULLPTR; + slab_header->next=alloc->partial_head; + if (alloc->partial_head!=SH_NULLPTR) { + alloc->partial_head->prev=slab_header; + } + alloc->partial_head=slab_header; + } + slab_header->used_count--; + sh_uint8* obj_ptr=sh_slab_reg_phys_ref_to_ptr(alloc,index); + if (obj_ptr==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + sh_mem_set_8(obj_ptr,0x00,SH_SLAB_REG_PHYS_OBJECT_SIZE_BYTES); + return SH_STATUS_SUCCESS; +} diff --git a/shelter/lib/src/memory/slabs/slab_reg_virt.c b/shelter/lib/src/memory/slabs/slab_reg_virt.c new file mode 100644 index 0000000..f4e2aef --- /dev/null +++ b/shelter/lib/src/memory/slabs/slab_reg_virt.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "memory/slabs/slab_reg_virt.h" +#include "std/mem.h" +#include "kernel/log.h" +#include "memory/pez/pez.h" +SH_STATUS sh_slab_reg_virt_alloc_init(sh_slab_reg_virt_SLAB_ALLOCATOR* slab_alloc,sh_page_PAGE_TABLE_POOL *ptp) { + if (slab_alloc==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + slab_alloc->max_slabs=SH_SLAB_REG_VIRT_MAX_SLAB; + slab_alloc->slab_count=0; + slab_alloc->partial_head=SH_NULLPTR; + slab_alloc->slabs_data=SH_NULLPTR; + sh_page_VIRTUAL_ADDRESS header_va; + SH_STATUS status=sh_page_alloc_contiguous_extended(ptp,SH_SLAB_REG_VIRT_HEADER_LIST_SIZE_BYTES,&header_va,SH_PAGE_RW | SH_PAGE_PRESENT | SH_PAGE_NX,SH_PAGE_KERNEL_PERM_VA_BASE,(SH_PAGE_KERNEL_PERM_VA_END-SH_PAGE_KERNEL_PERM_VA_BASE+1-0x1000)); + if (sh_status_error(status)) return status; + slab_alloc->slabs_header=(sh_slab_reg_virt_SLAB_STRUCT*)header_va; + status=sh_mem_set_8((sh_uint8*)(slab_alloc->slabs_header),0x00,SH_SLAB_REG_VIRT_HEADER_LIST_SIZE_BYTES); + if (sh_status_error(status)) return status; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_reg_virt_add_slab(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_virt_SLAB_STRUCT** out_slab) { + if (alloc==SH_NULLPTR || out_slab==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (alloc->slab_count>=alloc->max_slabs) return SH_STATUS_SLAB_ALLOCATOR_FULL; + sh_page_VIRTUAL_ADDRESS slab_pa; + SH_STATUS status; + if (sh_pez_is_available()) { + sh_pez_PHYSICAL_PLANE *phys_plane=sh_pez_get_reference_phys_plane(); + status=sh_pez_alloc_physical_pages(phys_plane,SH_SLAB_REG_PHYS_SLAB_DATA_PAGES,&slab_pa); + if (sh_status_error(status)) return status; + } else { + status=sh_page_search_physical_contiguous_block_na(SH_SLAB_REG_PHYS_SLAB_DATA_PAGES,&slab_pa); + if (sh_status_error(status)) return status; + } + if (alloc->slabs_data==SH_NULLPTR && alloc->slab_count==0) { + sh_page_VIRTUAL_ADDRESS alloc_slab_data_va_base=SH_SLAB_REG_VIRT_DATA_VA; + status=sh_page_is_va_range_mapped_ptp(ptp,alloc_slab_data_va_base,SH_SLAB_REG_VIRT_MAX_SLAB*SH_SLAB_REG_VIRT_SLAB_DATA_SIZE_BYTES); + if (status!=SH_STATUS_VA_NOT_MAPPED) return status; + status=sh_page_map_contiguous_pages_range_ptp(ptp,alloc_slab_data_va_base,slab_pa,SH_PAGE_PRESENT | SH_PAGE_NX | SH_PAGE_RW,SH_SLAB_REG_VIRT_SLAB_DATA_SIZE_BYTES); + if (sh_status_error(status)) return status; + alloc->slabs_data=(sh_uint8*)alloc_slab_data_va_base; + sh_slab_reg_virt_SLAB_STRUCT *new_slab_header=alloc->slabs_header; + new_slab_header->slab_index=alloc->slab_count; + new_slab_header->used_count=0; + new_slab_header->prev=SH_NULLPTR; + new_slab_header->next=SH_NULLPTR; + for (sh_iter64 i=0;i<16;i++) new_slab_header->free_bitmap[i]=0; + new_slab_header->free_bitmap[0]|=1ULL; + new_slab_header->used_count=0; + status=sh_mem_set_8(alloc->slabs_data,0x00,SH_SLAB_REG_VIRT_SLAB_DATA_SIZE_BYTES); + if (sh_status_error(status)) return status; + if (!sh_pez_is_available()) { + sh_page_MEM_STATS mem_stats; + status=sh_page_get_memory_stats(&mem_stats); + if (sh_status_error(status)) return status; + status=sh_page_set_pages_range_bitmap((sh_uint8*)sh_page_get_physical_bitmap_ptr(),mem_stats.memory_total_pages,slab_pa/SH_PAGE_SIZE,SH_SLAB_REG_VIRT_SLAB_DATA_PAGES,SH_TRUE); + if (sh_status_error(status)) return status; + } + alloc->partial_head=alloc->slabs_header; + *out_slab=new_slab_header; + alloc->slab_count++; + } else if (alloc->slabs_data!=SH_NULLPTR && alloc->slab_count!=0) { + sh_page_VIRTUAL_ADDRESS alloc_slab_data_va=SH_SLAB_REG_VIRT_DATA_VA+alloc->slab_count*SH_SLAB_REG_VIRT_SLAB_DATA_SIZE_BYTES; + status=sh_page_is_va_range_mapped_ptp(ptp,alloc_slab_data_va,SH_SLAB_REG_VIRT_SLAB_DATA_SIZE_BYTES); + if (status!=SH_STATUS_VA_NOT_MAPPED) return status; + status=sh_page_map_contiguous_pages_range_ptp(ptp,alloc_slab_data_va,slab_pa,SH_PAGE_PRESENT | SH_PAGE_NX | SH_PAGE_RW,SH_SLAB_REG_VIRT_SLAB_DATA_SIZE_BYTES); + if (sh_status_error(status)) return status; + sh_slab_reg_virt_SLAB_STRUCT *new_slab_header=alloc->slabs_header+alloc->slab_count; + new_slab_header->slab_index=alloc->slab_count; + new_slab_header->used_count=0; + new_slab_header->prev=SH_NULLPTR; + new_slab_header->next=alloc->partial_head; + sh_slab_reg_virt_SLAB_STRUCT *next_slab_header=new_slab_header->next; + next_slab_header->prev=new_slab_header; + alloc->partial_head=new_slab_header; + for (sh_iter64 i=0;i<16;i++) new_slab_header->free_bitmap[i]=0; + new_slab_header->free_bitmap[0]|=1ULL; + new_slab_header->used_count=0; + status=sh_mem_set_8(alloc->slabs_data+alloc->slab_count*SH_SLAB_REG_VIRT_SLAB_DATA_SIZE_BYTES,0x00,SH_SLAB_REG_VIRT_SLAB_DATA_SIZE_BYTES); + if (sh_status_error(status)) return status; + if (!sh_pez_is_available()) { + sh_page_MEM_STATS mem_stats; + status=sh_page_get_memory_stats(&mem_stats); + if (sh_status_error(status)) return status; + status=sh_page_set_pages_range_bitmap((sh_uint8*)sh_page_get_physical_bitmap_ptr(),mem_stats.memory_total_pages,slab_pa/SH_PAGE_SIZE,SH_SLAB_REG_VIRT_SLAB_DATA_PAGES,SH_TRUE); + if (sh_status_error(status)) return status; + } + *out_slab=new_slab_header; + alloc->slab_count++; + } + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_reg_virt_get_partial_slab(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL* ptp,sh_slab_reg_virt_SLAB_STRUCT** found_slab) { + if (alloc==SH_NULLPTR || ptp==SH_NULLPTR || found_slab==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (alloc->partial_head==SH_NULLPTR) { + *found_slab=SH_NULLPTR; + return SH_STATUS_NOT_FOUND; + } + sh_slab_reg_virt_SLAB_STRUCT* slab=alloc->partial_head; + if (slab->used_count>=SH_SLAB_REG_VIRT_ACTUAL_OBJECTS_PER_SLAB) { + if (slab->next!=SH_NULLPTR && slab->next->used_countpartial_head=slab->next; + alloc->partial_head->prev=SH_NULLPTR; + slab->next=SH_NULLPTR; + slab->prev=SH_NULLPTR; + *found_slab=alloc->partial_head; + return SH_STATUS_SUCCESS; + } else { + sh_slab_reg_virt_SLAB_STRUCT* new_slab; + SH_STATUS status=sh_slab_reg_virt_add_slab(alloc,ptp,&new_slab); + if (sh_status_error(status)) return status; + *found_slab=new_slab; + return SH_STATUS_NEW_SLAB_ADDED; + } + } + *found_slab=slab; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_reg_virt_scan_slabs(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc) { + if (alloc==SH_NULLPTR || alloc->slabs_data==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_reg_virt_SLAB_STRUCT* first_partial=SH_NULLPTR; + sh_slab_reg_virt_SLAB_STRUCT* last_partial=SH_NULLPTR; + sh_log_debug("Slabs rescan triggered.",SH_LOG_SOURCE_SLAB); + for (sh_uint32 i=0;islab_count;i++) { + sh_slab_reg_virt_SLAB_STRUCT* slab=alloc->slabs_header+i; + sh_uint8* slab_base=alloc->slabs_data+i*SH_SLAB_REG_VIRT_SLAB_DATA_SIZE_BYTES; + slab->used_count=0; + for (sh_iter64 w=0;w<16;w++) slab->free_bitmap[w]=0; + slab->free_bitmap[0]|=1ULL; + for (sh_uint16 obj_idx=1;obj_idx>6; + sh_uint16 bit=obj_idx&63; + slab->free_bitmap[word]&= ~(1ULL<>6; + sh_uint16 bit=obj_idx&63; + slab->free_bitmap[word]|=(1ULL<used_count++; + } + } + sh_log_fdebug(SH_LOG_SOURCE_SLAB,"Slab index: %4u; used count: %4u\n",slab->slab_index,slab->used_count); + slab->prev=SH_NULLPTR; + slab->next=SH_NULLPTR; + if (slab->used_count>0 && slab->used_countprev=last_partial; + last_partial->next=slab; + last_partial=slab; + } + } + } + alloc->partial_head=first_partial; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_reg_virt_find_free_object(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_virt_OBJECT_INDEX* out_ref) { + if (alloc==SH_NULLPTR || ptp==SH_NULLPTR || out_ref==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_reg_virt_SLAB_STRUCT *partial_slab; + sh_slab_reg_virt_SLAB_STRUCT *new_slab; + SH_STATUS status=sh_slab_reg_virt_get_partial_slab(alloc,ptp,&partial_slab); + if (status==SH_STATUS_RESCAN_NEEDED) { + status=sh_slab_reg_virt_scan_slabs(alloc); + if (sh_status_error(status)) return status; + status=sh_slab_reg_virt_get_partial_slab(alloc,ptp,&partial_slab); + } + if (status==SH_STATUS_NOT_FOUND) { + status=sh_slab_reg_virt_add_slab(alloc,ptp,&new_slab); + if (sh_status_error(status)) return status; + status=sh_slab_reg_virt_get_partial_slab(alloc,ptp,&partial_slab); + if (sh_status_error(status)) return status; + } else if (sh_status_error(status) && status!=SH_STATUS_NEW_SLAB_ADDED) { + return status; + } + sh_uint64 obj; + sh_bool obj_found=SH_FALSE; + for (sh_uint8 w=0;w<16;w++) { + sh_uint64 word=partial_slab->free_bitmap[w]; + if (word!=SH_UINT64_MAX) { + if (w==0) { + for (sh_uint8 bit=0;bit<64;bit++) { + if (!(word & (1ULL<slab_index,obj); + return SH_STATUS_SUCCESS; +} +void* sh_slab_reg_virt_ref_to_ptr(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_slab_reg_virt_OBJECT_INDEX ref) { + if (alloc==SH_NULLPTR) { + return SH_NULLPTR; + } + if (SH_SLAB_REG_VIRT_REF_SLAB(ref)>=alloc->slab_count || SH_SLAB_REG_VIRT_REF_OBJECT(ref)==0) { + return SH_NULLPTR; + } + sh_uint32 slab=SH_SLAB_REG_VIRT_REF_SLAB(ref); + sh_uint16 object=SH_SLAB_REG_VIRT_REF_OBJECT(ref); + if (object==0) { + return SH_NULLPTR; + } + return alloc->slabs_data+slab*SH_SLAB_REG_VIRT_SLAB_DATA_SIZE_BYTES+object*SH_SLAB_REG_VIRT_OBJECT_SIZE_BYTES; +} +SH_STATUS sh_slab_reg_virt_alloc(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_page_PAGE_TABLE_POOL *ptp,sh_slab_reg_virt_OBJECT_INDEX* out_index) { + if (alloc==SH_NULLPTR || ptp==SH_NULLPTR || out_index==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + sh_slab_reg_virt_OBJECT_INDEX ref; + SH_STATUS status=sh_slab_reg_virt_find_free_object(alloc,ptp,&ref); + if (sh_status_error(status)) return status; + sh_uint32 slab_idx=SH_SLAB_REG_VIRT_REF_SLAB(ref); + sh_uint16 obj_idx=SH_SLAB_REG_VIRT_REF_OBJECT(ref); + sh_slab_reg_virt_SLAB_STRUCT* slab=alloc->slabs_header+slab_idx; + sh_uint16 word=obj_idx>>6; + sh_uint16 bit=obj_idx&63; + slab->free_bitmap[word]|=(1ULL<used_count++; + *out_index=ref; + return SH_STATUS_SUCCESS; +} +SH_STATUS sh_slab_reg_virt_dealloc(sh_slab_reg_virt_SLAB_ALLOCATOR* alloc,sh_slab_reg_virt_OBJECT_INDEX index) { + if (alloc==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER; + if (SH_SLAB_REG_VIRT_REF_OBJECT(index)==0) return SH_STATUS_INVALID_PARAMETER; + if (SH_SLAB_REG_VIRT_REF_SLAB(index)>=alloc->slab_count) return SH_STATUS_INVALID_PARAMETER; + sh_uint16 obj_idx=SH_SLAB_REG_VIRT_REF_OBJECT(index); + sh_uint16 word=obj_idx>>6; + sh_uint16 bit=obj_idx&63; + sh_slab_reg_virt_SLAB_STRUCT* slab_header=alloc->slabs_header+SH_SLAB_REG_VIRT_REF_SLAB(index); + if (!(slab_header->free_bitmap[word] & (1ULL<free_bitmap[word]&= ~(1ULL<used_count==SH_SLAB_REG_VIRT_ACTUAL_OBJECTS_PER_SLAB) { + slab_header->prev=SH_NULLPTR; + slab_header->next=alloc->partial_head; + if (alloc->partial_head!=SH_NULLPTR) { + alloc->partial_head->prev=slab_header; + } + alloc->partial_head=slab_header; + } + slab_header->used_count--; + sh_uint8* obj_ptr=sh_slab_reg_virt_ref_to_ptr(alloc,index); + if (obj_ptr==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED; + sh_mem_set_8(obj_ptr,0x00,SH_SLAB_REG_VIRT_OBJECT_SIZE_BYTES); + return SH_STATUS_SUCCESS; +} diff --git a/shelter/lib/src/std/malloc.c b/shelter/lib/src/std/malloc.c new file mode 100644 index 0000000..f40366e --- /dev/null +++ b/shelter/lib/src/std/malloc.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "std/malloc.h" +#include "memory/page.h" +#include "memory/heap.h" +#include "memory/vmem_layout.h" +#include "kernel/log.h" +SH_STATUS last_status=0; +SH_STATUS sh_malloc_get_last_status() { + return last_status; +} +void* sh_malloc(sh_uint64 size) { + if (size==0) return SH_NULLPTR; + sh_page_VIRTUAL_ADDRESS va=0; + SH_STATUS status; + if (size<=1024) { + status=sh_heap_allocate_object((sh_uint32)size,&va); + } else { + sh_uint32 pages=(sh_uint32)((size+4095LL)/4096LL); + status=sh_heap_allocate_pages(pages,&va); + } + last_status=status; + return (sh_status_error(status))?SH_NULLPTR:(void*)va; +} +void sh_free(void *ptr) { + if (ptr==SH_NULLPTR) return; + sh_page_VIRTUAL_ADDRESS va=(sh_page_VIRTUAL_ADDRESS)ptr; + if (va>=SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_0_VA && va=SH_VMEM_LAYOUT_HEAP_BIG_VA && valog_ring_size); + sh_log_log("Loading serial port setting...",SH_LOG_SOURCE_MAIN); + sh_log_load_serial_setting(boot_config->log_disable_serial_port); + sh_serial_load_serial_port_setting(boot_config->disable_serial_port); + sh_log_log("Loading PTP VA...",SH_LOG_SOURCE_MAIN); + sh_page_load_boot_ptp_va(boot_config->page_table_pool_va); + sh_log_log("Loading log level...",SH_LOG_SOURCE_MAIN); + sh_log_load_log_level(boot_config->log_level); + sh_log_string("[Shelter:Log @Log] "); + sh_log_uint64((sh_uint64)sh_tsc_get_kernel_current_tsc()); + sh_log_string(" : Successfully set log level to "); + sh_log_uint64((sh_uint64)sh_log_get_log_level()); + sh_log_string("\n"); + sh_log_flog(SH_LOG_SOURCE_MAIN,"Set logging ring buffer to %2u pages.\n",boot_config->log_ring_size); + sh_log_log("Trying to obtain memory map...",SH_LOG_SOURCE_MAIN); + status=sh_page_copy_memory_map(); + if (status==SH_STATUS_SUCCESS) { + sh_log_log("Successfully copied memory map.",SH_LOG_SOURCE_PAGE); + sh_log_log("Successfully obtained memory map.",SH_LOG_SOURCE_MAIN); + } else { + sh_log_error("Can't copy memory map.",SH_LOG_SOURCE_PAGE); + sh_log_fatal("Can't obtain memory map.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + sh_log_log("Checking memory map...",SH_LOG_SOURCE_MAIN); + if (status==SH_STATUS_SUCCESS) { + sh_log_log("Successfully checked memory map.",SH_LOG_SOURCE_MAIN); + } else if (status==SH_STATUS_INVALID_SIGNATURE) { + sh_log_fatal("Can't check memory map.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } else if (status==SH_STATUS_MMAP_BUFFER_OVERFLOW) { + sh_log_fatal("Memory map is overflowing. Can't safely continue.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + sh_log_log("Initializing current page table pool...",SH_LOG_SOURCE_MAIN); + sh_page_PAGE_TABLE_POOL kernel_ptp; + status=sh_page_init_ptp(boot_config->page_table_pool_pa,sh_page_get_boot_ptp_va(),boot_config->page_table_allocator_level,&kernel_ptp); + if (status==SH_STATUS_PT_POOL_NO_BITMAP_INIT) { + sh_log_fatal("Couldn't initialize current page table pool.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } else if (status==SH_STATUS_PT_POOL_NO_PAGE_SET) { + sh_log_fatal("Couldn't initialize current page table pool.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + sh_log_log("Successfully initialized current page table pool.",SH_LOG_SOURCE_MAIN); + sh_log_log("Parsing memory map...",SH_LOG_SOURCE_MAIN); + status=sh_page_analyse_memory_map(&kernel_ptp); + if (sh_status_error(status)) { + sh_log_ffatal(SH_LOG_SOURCE_MAIN,"Couldn't parse memory map. Error code: %8s\n",status); + while (SH_TRUE) {}; + } + sh_log_log("Successfully parsed memory map.",SH_LOG_SOURCE_MAIN); + sh_log_mem_stats(SH_LOG_SOURCE_MAIN); + sh_log_log("Page managment subsystem successfully initialized",SH_LOG_SOURCE_MAIN); + sh_slab_reg_phys_SLAB_ALLOCATOR slab_reg_phys; + status=sh_slab_reg_phys_alloc_init(&slab_reg_phys,&kernel_ptp); + if (sh_status_error(status)) { + sh_log_fatal("Couldn't initialize physical region object slab allocator.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + sh_log_log("Successfully initialize physical region object slab allocator.",SH_LOG_SOURCE_MAIN); + sh_log_log("Allocating first slab...",SH_LOG_SOURCE_MAIN); + sh_slab_reg_phys_SLAB_STRUCT *first_slab_rp=SH_NULLPTR; + status=sh_slab_reg_phys_add_slab(&slab_reg_phys,&kernel_ptp,&first_slab_rp); + if (status!=SH_STATUS_SUCCESS) { + sh_log_ffatal(SH_LOG_SOURCE_MAIN,"Couldn't allocate first slab. Error code: %8s\n",status); + while (SH_TRUE) {}; + } else { + sh_log_log("Successfully allocated first slab. Verifying data...",SH_LOG_SOURCE_MAIN); + if (first_slab_rp->free_bitmap[0]!=1 || + first_slab_rp->slab_index!=0 || + first_slab_rp->next!=SH_NULLPTR || + first_slab_rp->prev!=SH_NULLPTR || + first_slab_rp->used_count!=0) { + sh_log_error("One of the attributes inside first slab is wrong.",SH_LOG_SOURCE_MAIN); + sh_log_fatal("Stopping for safety.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } else { + sh_log_log("All attributes are good.",SH_LOG_SOURCE_MAIN); + } + } + sh_slab_reg_virt_SLAB_ALLOCATOR slab_reg_virt; + status=sh_slab_reg_virt_alloc_init(&slab_reg_virt,&kernel_ptp); + if (sh_status_error(status)) { + sh_log_fatal("Couldn't initialize virtual region object slab allocator.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + sh_log_log("Successfully initialize virtual region object slab allocator.",SH_LOG_SOURCE_MAIN); + sh_log_log("Allocating first slab...",SH_LOG_SOURCE_MAIN); + sh_slab_reg_virt_SLAB_STRUCT *first_slab_rv=SH_NULLPTR; + status=sh_slab_reg_virt_add_slab(&slab_reg_virt,&kernel_ptp,&first_slab_rv); + if (status!=SH_STATUS_SUCCESS) { + sh_log_ffatal(SH_LOG_SOURCE_MAIN,"Couldn't allocate first slab. Error code: %8s\n",status); + while (SH_TRUE) {}; + } else { + sh_log_log("Successfully allocated first slab. Verifying data...",SH_LOG_SOURCE_MAIN); + if (first_slab_rv->free_bitmap[0]!=1 || + first_slab_rv->slab_index!=0 || + first_slab_rv->next!=SH_NULLPTR || + first_slab_rv->prev!=SH_NULLPTR || + first_slab_rv->used_count!=0) { + sh_log_error("One of the attributes inside first slab is wrong.",SH_LOG_SOURCE_MAIN); + sh_log_fatal("Stopping for safety.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } else { + sh_log_log("All attributes are good.",SH_LOG_SOURCE_MAIN); + } + } + sh_pba_PAGE_BLOCK_ALLOCATOR slab_radix_node_pba; + status=sh_pba_init(&slab_radix_node_pba,SH_VMEM_LAYOUT_SLAB_RADIX_NODE_VA,SH_VMEM_LAYOUT_SLAB_RADIX_NODE_SIZE_BYTES/SH_PAGE_SIZE,SH_SLAB_RADIX_NODE_SLAB_DATA_PAGES); + if (sh_status_error(status)) { + sh_log_fatal("Couldn't initialize PBA for radix nodes slab allocator.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + sh_log_log("Successfully initialize PBA for radix nodes slab allocator.",SH_LOG_SOURCE_MAIN); + struct sh_slab_radix_node_SLAB_ALLOCATOR slab_radix_node; + status=sh_slab_radix_node_alloc_init(&slab_radix_node,&slab_radix_node_pba); + if (sh_status_error(status)) { + sh_log_fatal("Couldn't initialize radix nodes slab allocator.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + sh_log_log("Successfully initialize radix nodes slab allocator.",SH_LOG_SOURCE_MAIN); + sh_log_log("Allocating first slab...",SH_LOG_SOURCE_MAIN); + sh_slab_radix_node_SLAB *first_slab_rn=SH_NULLPTR; + status=sh_slab_radix_node_add_slab(&slab_radix_node,&kernel_ptp,&first_slab_rn); + if (status!=SH_STATUS_SUCCESS) { + sh_log_ffatal(SH_LOG_SOURCE_MAIN,"Couldn't allocate first slab. Error code: %8s\n",status); + while (SH_TRUE) {}; + } else { + sh_log_log("Successfully allocated first slab. Verifying data...",SH_LOG_SOURCE_MAIN); + if (first_slab_rn->free_bitmap[0]!=0x3FFFF || + first_slab_rn->slab_index!=0 || + first_slab_rn->next_slab!=SH_NULLPTR || + first_slab_rn->prev_slab!=SH_NULLPTR || + first_slab_rn->next_partial!=SH_NULLPTR || + first_slab_rn->prev_partial!=SH_NULLPTR || + first_slab_rn->used_count!=0 ) { + sh_log_error("One of the attributes inside first slab is wrong.",SH_LOG_SOURCE_MAIN); + sh_log_fatal("Stopping for safety.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } else { + sh_log_log("All attributes are good.",SH_LOG_SOURCE_MAIN); + } + } + if (boot_config->test_benchmark) { + status=sh_test_slabs_benchmark(&slab_reg_phys,&slab_reg_virt,&slab_radix_node,&kernel_ptp); + if (status!=SH_STATUS_SUCCESS) { + sh_log_fatal("Couldn't benchmark one of the slab allocator.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + status=sh_test_radix_benchmark(&slab_radix_node,&kernel_ptp); + if (status!=SH_STATUS_SUCCESS) { + sh_log_fatal("Couldn't benchmark radix trees subsystem.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + } + sh_log_log("Creating Pez physical plane...",SH_LOG_SOURCE_MAIN); + sh_pez_PHYSICAL_PLANE physical_plane; + status=sh_pez_init_physical_plane((sh_uint8*)sh_page_get_physical_bitmap_ptr(),sh_page_get_physical_memory_amount_pages(),&slab_reg_phys,&slab_radix_node,&kernel_ptp,&physical_plane); + if (status!=SH_STATUS_SUCCESS) { + sh_log_ffatal(SH_LOG_SOURCE_MAIN,"Couldn't create Pez physical plane. Error code: %8s\n",status); + while (SH_TRUE) {}; + } + sh_log_log("Succesfully created Pez physical plane.",SH_LOG_SOURCE_MAIN); + if (boot_config->test_benchmark) { + status=sh_test_pez_benchmark_physical(&physical_plane); + if (status!=SH_STATUS_SUCCESS) { + sh_log_fatal("Couldn't benchmark Pez physical plane.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + } + sh_log_log("Creating Pez virtual plane for kernel heap...",SH_LOG_SOURCE_MAIN); + sh_pez_VIRTUAL_PLANE virtual_plane_kernel_heap; + status=sh_pez_init_virtual_plane(SH_VMEM_LAYOUT_HEAP_BIG_VA,&slab_reg_virt,&slab_radix_node,&kernel_ptp,&kernel_ptp,&virtual_plane_kernel_heap); + if (status!=SH_STATUS_SUCCESS) { + sh_log_ffatal(SH_LOG_SOURCE_MAIN,"Couldn't create Pez virtual plane for kernel heap. Error code: %8s\n",status); + while (SH_TRUE) {}; + } + sh_log_log("Succesfully created Pez virtual plane for kernel heap.",SH_LOG_SOURCE_MAIN); + sh_pez_set_available(); + sh_heap_KERNEL_HEAP kernel_heap; + status=sh_heap_init_heap(&physical_plane,&virtual_plane_kernel_heap,&kernel_ptp,&kernel_heap); + if (sh_status_error(status)) { + sh_log_ffatal(SH_LOG_SOURCE_MAIN,"Couldn't initialize kernel heap. Error code: %8s",status); + while (SH_TRUE) {}; + } + sh_uint64 slab_data_pages[8]=SH_SLAB_GENERIC_SLAB_DATA_PAGES; + for (sh_iter64 i=0;i<8;i++) { + status=sh_pba_init(&kernel_heap.pba[i],SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_0_VA+i*SH_VMEM_LAYOUT_HEAP_SLAB_SPACING,((SH_VMEM_LAYOUT_HEAP_SLAB_LEVEL_0_SIZE_BYTES/SH_PAGE_SIZE)/slab_data_pages[i])*slab_data_pages[i],slab_data_pages[i]); + if (sh_status_error(status)) { + sh_log_ffatal(SH_LOG_SOURCE_MAIN,"Couldn't create PBA for heap allocations level %8u. Error code: %8s\n",i,status); + while (SH_TRUE); + } + status=sh_slab_generic_alloc_init((sh_uint8)i,&kernel_heap.slabs_allocator[i],&kernel_heap.pba[i]); + if (sh_status_error(status)) { + sh_log_ffatal(SH_LOG_SOURCE_MAIN,"Couldn't create slab allocator for heap allocations level %8u. Error code: %8s\n",i,status); + while (SH_TRUE); + } + sh_slab_generic_SLAB *first_slab_generic; + status=sh_slab_generic_add_slab(&kernel_heap.slabs_allocator[i],&kernel_ptp,&first_slab_generic); + if (status!=SH_STATUS_SUCCESS) { + sh_log_ffatal(SH_LOG_SOURCE_MAIN,"Couldn't allocate first slab for heap allocations level %8u. Error code: %8s\n",i,status); + while (SH_TRUE) {}; + } else { + if (first_slab_generic->free_bitmap[0]!=kernel_heap.slabs_allocator[i].bitmap_init || + first_slab_generic->slab_index!=0 || + first_slab_generic->next_slab!=SH_NULLPTR || + first_slab_generic->prev_slab!=SH_NULLPTR || + first_slab_generic->next_partial!=SH_NULLPTR || + first_slab_generic->prev_partial!=SH_NULLPTR || + first_slab_generic->used_count!=0 ) { + sh_log_error("One of the attributes inside first slab is wrong.",SH_LOG_SOURCE_MAIN); + sh_log_fatal("Stopping for safety.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + } + sh_log_flog(SH_LOG_SOURCE_MAIN,"Succesfully created heap slab allocator with level %8u.\n",i); + } + sh_heap_load_default_heap(&kernel_heap); + sh_log_log("Succesfully created kernel heap.",SH_LOG_SOURCE_MAIN); + if (boot_config->test_benchmark) { + status=sh_test_malloc_benchmark(); + if (status!=SH_STATUS_SUCCESS) { + sh_log_fatal("Couldn't benchmark Pez physical plane.",SH_LOG_SOURCE_MAIN); + while (SH_TRUE) {}; + } + } + while (1) {}; + return 0; +} diff --git a/shelter/tools/checker/vmem_layout_check.py b/shelter/tools/checker/vmem_layout_check.py new file mode 100644 index 0000000..f461339 --- /dev/null +++ b/shelter/tools/checker/vmem_layout_check.py @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: MPL-2.0 +import os +import sys +import subprocess +class VirtualRegion: + def __init__(self,start,size): + self.start=start + self.size=size + def compute_end(self): + self.end=self.start+self.size +args=sys.argv +if len(args)!=2: + print("[VMLC] Error: not enough arguments") + exit(-1) +hfile=args[1] +if not os.path.exists(hfile): + print("[VMLC] Error: provided .h file doesn't exist") + exit(-1) +hfile=os.path.abspath(hfile) +print("[VMLC] Provided file: "+hfile) +incomplete_region_va=[] +incomplete_region_size_bytes=[] +region_name=[] +fileslines=open(hfile,"r").readlines() +macros=[] +for i in range(len(fileslines)): + if fileslines[i].startswith("#define"): + macros.append(fileslines[i]) +for i in range(len(macros)): + macroname=str(str(macros[i]).strip(" ").split(" ")[1]) + if macroname.endswith("_VA"): + if not macroname in incomplete_region_va: + incomplete_region_va.append(macroname) + else: + print("[VMLC] Error: found duplicate macro: "+macroname) + exit(-1) + elif macroname.endswith("_SIZE_BYTES"): + if not macroname in incomplete_region_size_bytes: + incomplete_region_size_bytes.append(macroname) + else: + print("[VMLC] Error: found duplicate macro: "+macroname) + exit(-1) +for i in range(len(incomplete_region_va)): + macronamesize=str(incomplete_region_va[i]).removesuffix("_VA")+"_SIZE_BYTES" + if not macronamesize in incomplete_region_size_bytes: + print("[VMLC] Error: for region \""+str(incomplete_region_va[i]).removesuffix("_VA")+"\", corresponding size wasn't found.") + exit(-1) +for i in range(len(incomplete_region_size_bytes)): + macronameva=str(incomplete_region_size_bytes[i]).removesuffix("_SIZE_BYTES")+"_VA" + if not macronameva in incomplete_region_va: + print("[VMLC] Warning: for region \""+str(incomplete_region_size_bytes[i]).removesuffix("_SIZE_BYTES")+"\", corresponding VA wasn't found. Skipping") +print("[VMLC] Found "+str(len(incomplete_region_va))+" valid virtual regions: ",end="") +for i in range(len(incomplete_region_va)): + if i!=len(incomplete_region_va)-1: + print(incomplete_region_va[i].removesuffix("_VA")+", ",end="") + else: + print(incomplete_region_va[i].removesuffix("_VA")) + region_name.append(incomplete_region_va[i].removesuffix("_VA")) +os.chdir(os.path.dirname(__file__)) +try: + os.remove("cfile.c") +except FileNotFoundError: + pass +tmpcfile=open("cfile.c","w") +tmpcfile.write("#include \n#include \n#include \n#include \""+hfile+"\"\n") +tmpcfile.write("int main() {\n char buf[256];\n FILE *f=fopen(\"reg.txt\",\"w\");\n") +tmpcfile.write(" if (!f) return -1;\n") +for i in range(len(region_name)): + tmpcfile.write(" snprintf(buf,256,\"%\" PRIu64 \" %\" PRIu64 \"\\n\",(uint64_t)"+region_name[i]+"_VA,(uint64_t)"+region_name[i]+"_SIZE_BYTES);\n") + tmpcfile.write(" fwrite(buf,1,strlen(buf),f);\n") +tmpcfile.write(" fclose(f);\n return 0;\n}") +tmpcfile.close() +try: + os.remove("cfile") + os.remove("reg.txt") +except FileNotFoundError: + pass +r=subprocess.run(["gcc","cfile.c","-o","cfile"]) +if r.returncode!=0: + print("[VMLC] Error: can't compile generated cfile.c") + exit(-1) +r=subprocess.run(["./cfile"]) +if r.returncode!=0: + print("[VMLC] Error: can't run compiled cfile") + exit(-1) +if not os.path.exists("reg.txt"): + print("[VMLC] Error: reg.txt doesn't exist") + exit(-1) +reglist={} +regstart=[] +with open("reg.txt") as f: + for line in f: + va,size=line.strip().split() + vr=VirtualRegion(int(va),int(size)) + vr.compute_end() + reglist[int(va)]=vr + if not vr.start in regstart: + regstart.append(vr.start) + else: + print("[VMLC] Error: found two regions starting at the same VA: "+str(vr.start)+" / 0x"+str(hex(vr.start))) + exit(-1) +print("[VMLC] Obtained region decimal VA and size.") +regstart.sort() +regboundaries=[] +for i in range(len(regstart)): + regboundaries.append(reglist[regstart[i]].start) + regboundaries.append(reglist[regstart[i]].end) +print("[VMLC] Checking for overlaps...") +for i in range(1,len(regboundaries)-1,2): + if regboundaries[i]>regboundaries[i+1]: + print("[VMLC] Error: found overlap between region ending at "+str(regboundaries[i])+" / 0x"+str(hex(regboundaries[i]))+" and region starting at "+str(regboundaries[i+1])+" / 0x"+str(hex(regboundaries[i+1]))) + exit(-1) +print("[VMLC] No overlaps found.") +try: + os.remove("reg.txt") + os.remove("cfile.c") + os.remove("cfile") +except FileNotFoundError: + pass diff --git a/shelter/tools/generator/malloc_payload_gen.py b/shelter/tools/generator/malloc_payload_gen.py new file mode 100644 index 0000000..523cca6 --- /dev/null +++ b/shelter/tools/generator/malloc_payload_gen.py @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: MPL-2.0 +import random +print("// SPDX-License-Identifier: MPL-2.0\n// This file contain the values to use as alloc size for the benchmark of sh_malloc and sh_free\n// It's autogenerated by tools/generator/malloc_payload_gen.py\n// Copy the output of this script in this file to regenerate it.") +print("#ifndef SH_LIB_PAYLOADS_TEST_MALLOC\n#define SH_LIB_PAYLOADS_TEST_MALLOC\n#include \"std/type.h\"") +print("__attribute__((section(\".data\"))) sh_uint32 test_malloc_small_size[10000]={",end="") +out=[] +lambd=0.006 +while len(out)<10000: + val=int(random.expovariate(lambd))+2 + if val<=1024: + out.append(val) +random.shuffle(out) +for i in range(len(out)): + print(out[i],end="") + if i!=(len(out)-1): + print(",",end="") +print("};") +print("__attribute__((section(\".data\"))) sh_uint32 test_malloc_big_size[1000]={",end="") +out=[] +lambd=0.006 +while len(out)<1000: + val=int(random.expovariate(lambd))+2 + if val<=25: + out.append(val) +random.shuffle(out) +for i in range(len(out)): + print(out[i],end="") + if i!=(len(out)-1): + print(",",end="") +print("};") +print("__attribute__((section(\".data\"))) sh_uint16 test_malloc_big_alloc[2000]={",end="") +out=[] +for i in range(0,1000): + out.append(i) +for i in range(0,1000): + out.append(i) +random.shuffle(out) +for i in range(len(out)): + print(out[i],end="") + if i!=(len(out)-1): + print(",",end="") +print("};") +print("#endif") diff --git a/shelter/tools/generator/pez_alloc_free_payload_gen.py b/shelter/tools/generator/pez_alloc_free_payload_gen.py new file mode 100644 index 0000000..c23fbd2 --- /dev/null +++ b/shelter/tools/generator/pez_alloc_free_payload_gen.py @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: MPL-2.0 +import random +print("// SPDX-License-Identifier: MPL-2.0\n// This file contain the values to use as pages count for the benchmark of allocs and free with Pez\n// It's autogenerated by tools/generator/pez_alloc_free_payload_gen.py\n// Copy the output of this script in this file to regenerate it.") +print("#ifndef SH_LIB_PAYLOADS_TEST_PEZ\n#define SH_LIB_PAYLOADS_TEST_PEZ\n#include \"std/type.h\"") +print("__attribute__((section(\".data\"))) sh_uint64 test_pez_physical_size[2000]={",end="") +out=[] +for i in range(1000): + out.append(1) +rand_list=[] +lambd=0.006 +while len(rand_list)<1000: + val=int(random.expovariate(lambd))+2 + if val<=1000: + rand_list.append(val) +out=out+rand_list +random.shuffle(out) +for i in range(len(out)): + print(out[i],end="") + if i!=(len(out)-1): + print(",",end="") +print("};") +print("__attribute__((section(\".data\"))) sh_uint64 test_pez_physical_alloc[4000]={",end="") +out=[] +for i in range(0,2000): + out.append(i) +for i in range(0,2000): + out.append(i) +random.shuffle(out) +for i in range(len(out)): + print(out[i],end="") + if i!=(len(out)-1): + print(",",end="") +print("};") +print("#endif") diff --git a/shelter/tools/generator/radix_tree_payload_gen.py b/shelter/tools/generator/radix_tree_payload_gen.py new file mode 100644 index 0000000..09a2f65 --- /dev/null +++ b/shelter/tools/generator/radix_tree_payload_gen.py @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: MPL-2.0 +import random +print("// SPDX-License-Identifier: MPL-2.0\n// This file contain the values to use as keys, values and search for the benchmarks of the radix trees subsystem\n// It's autogenerated by tools/generator/radix_tree_payload_gen.py\n// Copy the output of this script in this file to regenerate it.") +print("#ifndef SH_LIB_PAYLOADS_TEST_RADIX\n#define SH_LIB_PAYLOADS_TEST_RADIX\n#include \"std/type.h\"") +hex_char=["1","2","3","4","5","6","7","8","9","0","A","B","C","D","E","F"]; +def randlist(num:int,hex_char_num:int,name:str): + print("__attribute__((section(\".data\"))) sh_uint64 "+name+"["+str(num)+"]={",end="") + for i in range(num): + print("0x",end="") + for y in range(hex_char_num): + print(hex_char[random.randint(0,15)],end="") + if i!=(num-1): + print(",",end="") + print("};") +randlist(10000,16,"test_keys") +randlist(10000,16,"test_values") +randlist(10000,16,"test_search") +print("#endif") diff --git a/vyld/_vyx_start.c b/vyld/_vyx_start.c new file mode 100644 index 0000000..83de03c --- /dev/null +++ b/vyld/_vyx_start.c @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MPL-2.0 +extern int main(void); +void _vyx_start(void) { + main(); + for (;;); +} diff --git a/vyld/elfio.hpp b/vyld/elfio.hpp new file mode 100644 index 0000000..2980d85 --- /dev/null +++ b/vyld/elfio.hpp @@ -0,0 +1,1260 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_HPP +#define ELFIO_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "elfio/elf_types.hpp" +#include "elfio/elfio_version.hpp" +#include "elfio/elfio_utils.hpp" +#include "elfio/elfio_header.hpp" +#include "elfio/elfio_section.hpp" +#include "elfio/elfio_segment.hpp" +#include "elfio/elfio_strings.hpp" + +#define ELFIO_HEADER_ACCESS_GET( TYPE, FNAME ) \ + TYPE get_##FNAME() const { return header ? ( header->get_##FNAME() ) : 0; } + +#define ELFIO_HEADER_ACCESS_GET_SET( TYPE, FNAME ) \ + TYPE get_##FNAME() const \ + { \ + return header ? ( header->get_##FNAME() ) : 0; \ + } \ + void set_##FNAME( TYPE val ) \ + { \ + if ( header ) { \ + header->set_##FNAME( val ); \ + } \ + } + +namespace ELFIO { + +//------------------------------------------------------------------------------ +//! \class elfio +//! \brief The elfio class represents an ELF file and provides methods to manipulate it. +class elfio +{ + public: + //------------------------------------------------------------------------------ + //! \brief Default constructor + elfio() noexcept : sections( this ), segments( this ) + { + convertor = std::make_shared(); + addr_translator = std::make_shared(); + create( ELFCLASS32, ELFDATA2LSB ); + } + + //------------------------------------------------------------------------------ + //! \brief Constructor with compression interface + //! \param compression Pointer to the compression interface + explicit elfio( compression_interface* compression_ptr ) noexcept : elfio() + { + this->compression = + std::shared_ptr( compression_ptr ); + } + + //------------------------------------------------------------------------------ + //! \brief Move constructor + //! \param other The other elfio object to move from + elfio( elfio&& other ) noexcept + : sections( this ), segments( this ), + current_file_pos( other.current_file_pos ) + { + header = std::move( other.header ); + sections_ = std::move( other.sections_ ); + segments_ = std::move( other.segments_ ); + convertor = std::move( other.convertor ); + addr_translator = std::move( other.addr_translator ); + compression = std::move( other.compression ); + + other.header = nullptr; + other.sections_.clear(); + other.segments_.clear(); + other.compression = nullptr; + } + + //------------------------------------------------------------------------------ + //! \brief Move assignment operator + //! \param other The other elfio object to move from + //! \return Reference to this object + elfio& operator=( elfio&& other ) noexcept + { + if ( this != &other ) { + header = std::move( other.header ); + sections_ = std::move( other.sections_ ); + segments_ = std::move( other.segments_ ); + convertor = std::move( other.convertor ); + addr_translator = std::move( other.addr_translator ); + current_file_pos = other.current_file_pos; + compression = std::move( other.compression ); + + other.current_file_pos = 0; + other.header = nullptr; + other.compression = nullptr; + other.sections_.clear(); + other.segments_.clear(); + } + return *this; + } + + //------------------------------------------------------------------------------ + //! \brief Delete copy constructor and copy assignment operator + elfio( const elfio& ) = delete; + elfio& operator=( const elfio& ) = delete; + ~elfio() = default; + + //------------------------------------------------------------------------------ + //! \brief Create a new ELF file with the specified class and encoding + //! \param file_class The class of the ELF file (ELFCLASS32 or ELFCLASS64) + //! \param encoding The encoding of the ELF file (ELFDATA2LSB or ELFDATA2MSB) + void create( unsigned char file_class, unsigned char encoding ) + { + sections_.clear(); + segments_.clear(); + ( *convertor ).setup( encoding ); + header = create_header( file_class, encoding ); + create_mandatory_sections(); + } + + //------------------------------------------------------------------------------ + //! \brief Set address translation + //! \param addr_trans Vector of address translations + void set_address_translation( std::vector& addr_trans ) + { + ( *addr_translator ).set_address_translation( addr_trans ); + } + + //------------------------------------------------------------------------------ + //! \brief Load an ELF file from a file + //! \param file_name The name of the file to load + //! \param is_lazy Whether to load the file lazily + //! \return True if successful, false otherwise + bool load( const std::string& file_name, bool is_lazy = false ) + { + pstream = std::make_unique(); + if ( !pstream ) { + return false; + } + + pstream->open( file_name.c_str(), std::ios::in | std::ios::binary ); + if ( !*pstream ) { + return false; + } + + bool ret = load( *pstream, is_lazy ); + + if ( !is_lazy ) { + pstream.reset(); + } + + return ret; + } + + //------------------------------------------------------------------------------ + //! \brief Load an ELF file from a stream + //! \param stream The input stream to load from + //! \param is_lazy Whether to load the file lazily + //! \return True if successful, false otherwise + bool load( std::istream& stream, bool is_lazy = false ) + { + sections_.clear(); + segments_.clear(); + + std::array e_ident = { 0 }; + // Read ELF file signature + stream.seekg( ( *addr_translator )[0] ); + stream.read( e_ident.data(), sizeof( e_ident ) ); + + // Is it ELF file? + if ( stream.gcount() != sizeof( e_ident ) || + e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || + e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3 ) { + return false; + } + + if ( ( e_ident[EI_CLASS] != ELFCLASS64 ) && + ( e_ident[EI_CLASS] != ELFCLASS32 ) ) { + return false; + } + + if ( ( e_ident[EI_DATA] != ELFDATA2LSB ) && + ( e_ident[EI_DATA] != ELFDATA2MSB ) ) { + return false; + } + + ( *convertor ).setup( e_ident[EI_DATA] ); + header = create_header( e_ident[EI_CLASS], e_ident[EI_DATA] ); + if ( nullptr == header ) { + return false; + } + if ( !header->load( stream ) ) { + return false; + } + + load_sections( stream, is_lazy ); + bool is_still_good = load_segments( stream, is_lazy ); + return is_still_good; + } + + //------------------------------------------------------------------------------ + //! \brief Save the ELF file to a file + //! \param file_name The name of the file to save to + //! \return True if successful, false otherwise + bool save( const std::string& file_name ) + { + std::ofstream stream; + stream.open( file_name.c_str(), std::ios::out | std::ios::binary ); + if ( !stream ) { + return false; + } + + return save( stream ); + } + + //------------------------------------------------------------------------------ + //! \brief Save the ELF file to a stream + //! \param stream The output stream to save to + //! \return True if successful, false otherwise + bool save( std::ostream& stream ) + { + if ( !stream || header == nullptr ) { + return false; + } + + // Define layout specific header fields + // The position of the segment table is fixed after the header. + // The position of the section table is variable and needs to be fixed + // before saving. + header->set_segments_num( segments.size() ); + header->set_segments_offset( + segments.size() > 0 ? header->get_header_size() : 0 ); + header->set_sections_num( sections.size() ); + header->set_sections_offset( 0 ); + + // Layout the first section right after the segment table + current_file_pos = + header->get_header_size() + + header->get_segment_entry_size() * + static_cast( header->get_segments_num() ); + + calc_segment_alignment(); + + bool is_still_good = layout_segments_and_their_sections(); + is_still_good = is_still_good && layout_sections_without_segments(); + is_still_good = is_still_good && layout_section_table(); + + is_still_good = is_still_good && save_header( stream ); + is_still_good = is_still_good && save_sections( stream ); + is_still_good = is_still_good && save_segments( stream ); + + return is_still_good; + } + + //------------------------------------------------------------------------------ + // ELF header access functions + ELFIO_HEADER_ACCESS_GET( unsigned char, class ); + ELFIO_HEADER_ACCESS_GET( unsigned char, elf_version ); + ELFIO_HEADER_ACCESS_GET( unsigned char, encoding ); + ELFIO_HEADER_ACCESS_GET( Elf_Word, version ); + ELFIO_HEADER_ACCESS_GET( Elf_Half, header_size ); + ELFIO_HEADER_ACCESS_GET( Elf_Half, section_entry_size ); + ELFIO_HEADER_ACCESS_GET( Elf_Half, segment_entry_size ); + + ELFIO_HEADER_ACCESS_GET_SET( unsigned char, os_abi ); + ELFIO_HEADER_ACCESS_GET_SET( unsigned char, abi_version ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, type ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, machine ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Word, flags ); + ELFIO_HEADER_ACCESS_GET_SET( Elf64_Addr, entry ); + ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, sections_offset ); + ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, segments_offset ); + ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, section_name_str_index ); + + //------------------------------------------------------------------------------ + //! \brief Get the endianness convertor + //! \return Reference to the endianness convertor + const std::shared_ptr& get_convertor() const + { + return convertor; + } + + //------------------------------------------------------------------------------ + //! \brief Get the default entry size for a section type + //! \param section_type The type of the section + //! \return The default entry size for the section type + Elf_Xword get_default_entry_size( Elf_Word section_type ) const + { + switch ( section_type ) { + case SHT_RELA: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Rela ); + } + else { + return sizeof( Elf32_Rela ); + } + case SHT_REL: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Rel ); + } + else { + return sizeof( Elf32_Rel ); + } + case SHT_SYMTAB: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Sym ); + } + else { + return sizeof( Elf32_Sym ); + } + case SHT_DYNAMIC: + if ( header->get_class() == ELFCLASS64 ) { + return sizeof( Elf64_Dyn ); + } + else { + return sizeof( Elf32_Dyn ); + } + default: + return 0; + } + } + + //------------------------------------------------------------------------------ + //! \brief Validate the ELF file + //! \return An empty string if no problems are detected, or a string containing an error message if problems are found, with one error per line. + std::string validate() const + { + // clang-format off + + std::string errors; + // Check for overlapping sections in the file + // This is explicitly forbidden by ELF specification + for ( int i = 0; i < sections.size(); ++i) { + for ( int j = i+1; j < sections.size(); ++j ) { + const section* a = sections[i]; + const section* b = sections[j]; + if ( ( ( a->get_type() & SHT_NOBITS) == 0 ) + && ( ( b->get_type() & SHT_NOBITS) == 0 ) + && ( a->get_size() > 0 ) + && ( b->get_size() > 0 ) + && ( a->get_offset() > 0 ) + && ( b->get_offset() > 0 ) + && ( is_offset_in_section( a->get_offset(), b ) + || is_offset_in_section( a->get_offset()+a->get_size()-1, b ) + || is_offset_in_section( b->get_offset(), a ) + || is_offset_in_section( b->get_offset()+b->get_size()-1, a ) ) ) { + errors += "Sections " + a->get_name() + " and " + b->get_name() + " overlap in file\n"; + } + } + } + // clang-format on + + // Check for conflicting section / program header tables, where + // the same offset has different vaddresses in section table and + // program header table. + // This doesn't seem to be explicitly forbidden by ELF specification, + // but: + // - it doesn't make any sense + // - ELFIO relies on this being consistent when writing ELF files, + // since offsets are re-calculated from vaddress + for ( int h = 0; h < segments.size(); ++h ) { + const segment* seg = segments[h]; + const section* sec = + find_prog_section_for_offset( seg->get_offset() ); + if ( seg->get_type() == PT_LOAD && seg->get_file_size() > 0 && + sec != nullptr ) { + Elf64_Addr sec_addr = + get_virtual_addr( seg->get_offset(), sec ); + if ( sec_addr != seg->get_virtual_address() ) { + errors += "Virtual address of segment " + + std::to_string( h ) + " (" + + to_hex_string( seg->get_virtual_address() ) + + ")" + " conflicts with address of section " + + sec->get_name() + " (" + + to_hex_string( sec_addr ) + ")" + " at offset " + + to_hex_string( seg->get_offset() ) + "\n"; + } + } + } + + // more checks to be added here... + + return errors; + } + + private: + //------------------------------------------------------------------------------ + //! \brief Check if an offset is within a section + //! \param offset The offset to check + //! \param sec Pointer to the section + //! \return True if the offset is within the section, false otherwise + static bool is_offset_in_section( Elf64_Off offset, const section* sec ) + { + return ( offset >= sec->get_offset() ) && + ( offset < ( sec->get_offset() + sec->get_size() ) ); + } + + //------------------------------------------------------------------------------ + //! \brief Get the virtual address of an offset within a section + //! \param offset The offset within the section + //! \param sec Pointer to the section + //! \return The virtual address of the offset within the section + static Elf64_Addr get_virtual_addr( Elf64_Off offset, const section* sec ) + { + return sec->get_address() + offset - sec->get_offset(); + } + + //------------------------------------------------------------------------------ + //! \brief Find the section that contains a given offset + //! \param offset The offset to find + //! \return Pointer to the section that contains the offset, or nullptr if not found + const section* find_prog_section_for_offset( Elf64_Off offset ) const + { + for ( const auto& sec : sections ) { + if ( sec->get_type() == SHT_PROGBITS && + is_offset_in_section( offset, sec.get() ) ) { + return sec.get(); + } + } + return nullptr; + } + + //------------------------------------------------------------------------------ + //! \brief Create an ELF header + //! \param file_class The class of the ELF file (ELFCLASS32 or ELFCLASS64) + //! \param encoding The encoding of the ELF file (ELFDATA2LSB or ELFDATA2MSB) + //! \return Unique pointer to the created ELF header + std::unique_ptr create_header( unsigned char file_class, + unsigned char encoding ) + { + std::unique_ptr new_header; + + if ( file_class == ELFCLASS64 ) { + new_header = std::unique_ptr( + new ( std::nothrow ) elf_header_impl( + convertor, encoding, addr_translator ) ); + } + else if ( file_class == ELFCLASS32 ) { + new_header = std::unique_ptr( + new ( std::nothrow ) elf_header_impl( + convertor, encoding, addr_translator ) ); + } + else { + return nullptr; + } + + return new_header; + } + + //------------------------------------------------------------------------------ + //! \brief Create a new section + //! \return Pointer to the created section + section* create_section() + { + if ( auto file_class = get_class(); file_class == ELFCLASS64 ) { + sections_.emplace_back( + new ( std::nothrow ) section_impl( + convertor, addr_translator, compression ) ); + } + else if ( file_class == ELFCLASS32 ) { + sections_.emplace_back( + new ( std::nothrow ) section_impl( + convertor, addr_translator, compression ) ); + } + else { + sections_.pop_back(); + return nullptr; + } + + section* new_section = sections_.back().get(); + new_section->set_index( static_cast( sections_.size() - 1 ) ); + + return new_section; + } + + //------------------------------------------------------------------------------ + //! \brief Create a new segment + //! \return Pointer to the created segment + segment* create_segment() + { + if ( auto file_class = header->get_class(); file_class == ELFCLASS64 ) { + segments_.emplace_back( + new ( std::nothrow ) + segment_impl( convertor, addr_translator ) ); + } + else if ( file_class == ELFCLASS32 ) { + segments_.emplace_back( + new ( std::nothrow ) + segment_impl( convertor, addr_translator ) ); + } + else { + segments_.pop_back(); + return nullptr; + } + + segment* new_segment = segments_.back().get(); + new_segment->set_index( static_cast( segments_.size() - 1 ) ); + + return new_segment; + } + + //------------------------------------------------------------------------------ + //! \brief Create mandatory sections + void create_mandatory_sections() + { + // Create null section without calling to 'add_section' as no string + // section containing section names exists yet + section* sec0 = create_section(); + sec0->set_index( 0 ); + sec0->set_name( "" ); + sec0->set_name_string_offset( 0 ); + + set_section_name_str_index( 1 ); + section* shstrtab = sections.add( ".shstrtab" ); + shstrtab->set_type( SHT_STRTAB ); + shstrtab->set_addr_align( 1 ); + } + + //------------------------------------------------------------------------------ + //! \brief Load sections from a stream + //! \param stream The input stream to load from + //! \param is_lazy Whether to load the sections lazily + //! \return True if successful, false otherwise + bool load_sections( std::istream& stream, bool is_lazy ) + { + unsigned char file_class = header->get_class(); + Elf_Half entry_size = header->get_section_entry_size(); + Elf_Half num = header->get_sections_num(); + Elf64_Off offset = header->get_sections_offset(); + + if ( ( num != 0 && file_class == ELFCLASS64 && + entry_size < sizeof( Elf64_Shdr ) ) || + ( num != 0 && file_class == ELFCLASS32 && + entry_size < sizeof( Elf32_Shdr ) ) ) { + return false; + } + + for ( Elf_Half i = 0; i < num; ++i ) { + section* sec = create_section(); + + // Load return value is ignored here + // This allows retrieval of information from corrupted sections + sec->load( stream, + static_cast( offset ) + + static_cast( i ) * entry_size, + is_lazy ); + // To mark that the section is not permitted to reassign address + // during layout calculation + sec->set_address( sec->get_address() ); + } + + if ( Elf_Half shstrndx = get_section_name_str_index(); + SHN_UNDEF != shstrndx ) { + string_section_accessor str_reader( sections[shstrndx] ); + for ( Elf_Half i = 0; i < num; ++i ) { + Elf_Word section_offset = sections[i]->get_name_string_offset(); + const char* p = str_reader.get_string( section_offset ); + if ( p != nullptr ) { + sections[i]->set_name( p ); + } + } + } + + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Checks whether the addresses of the section entirely fall within the given segment. + //! It doesn't matter if the addresses are memory addresses, or file offsets, + //! they just need to be in the same address space + //! \param sect_begin The beginning address of the section + //! \param sect_size The size of the section + //! \param seg_begin The beginning address of the segment + //! \param seg_end The end address of the segment + //! \return True if the section is within the segment, false otherwise + static bool is_sect_in_seg( Elf64_Off sect_begin, + Elf_Xword sect_size, + Elf64_Off seg_begin, + Elf64_Off seg_end ) + { + return ( seg_begin <= sect_begin ) && + ( sect_begin + sect_size <= seg_end ) && + ( sect_begin < + seg_end ); // this is important criteria when sect_size == 0 + // Example: seg_begin=10, seg_end=12 (-> covering the bytes 10 and 11) + // sect_begin=12, sect_size=0 -> shall return false! + } + + //------------------------------------------------------------------------------ + //! \brief Load segments from a stream + //! \param stream The input stream to load from + //! \param is_lazy Whether to load the segments lazily + //! \return True if successful, false otherwise + bool load_segments( std::istream& stream, bool is_lazy ) + { + unsigned char file_class = header->get_class(); + Elf_Half entry_size = header->get_segment_entry_size(); + Elf_Half num = header->get_segments_num(); + Elf64_Off offset = header->get_segments_offset(); + + if ( ( num != 0 && file_class == ELFCLASS64 && + entry_size < sizeof( Elf64_Phdr ) ) || + ( num != 0 && file_class == ELFCLASS32 && + entry_size < sizeof( Elf32_Phdr ) ) ) { + return false; + } + + for ( Elf_Half i = 0; i < num; ++i ) { + if ( file_class == ELFCLASS64 ) { + segments_.emplace_back( new ( std::nothrow ) + segment_impl( + convertor, addr_translator ) ); + } + else if ( file_class == ELFCLASS32 ) { + segments_.emplace_back( new ( std::nothrow ) + segment_impl( + convertor, addr_translator ) ); + } + else { + segments_.pop_back(); + return false; + } + + segment* seg = segments_.back().get(); + + if ( !seg->load( stream, + static_cast( offset ) + + static_cast( i ) * entry_size, + is_lazy ) || + stream.fail() ) { + segments_.pop_back(); + return false; + } + + seg->set_index( i ); + + // Add sections to the segments (similar to readelfs algorithm) + Elf64_Off segBaseOffset = seg->get_offset(); + Elf64_Off segEndOffset = segBaseOffset + seg->get_file_size(); + Elf64_Off segVBaseAddr = seg->get_virtual_address(); + Elf64_Off segVEndAddr = segVBaseAddr + seg->get_memory_size(); + for ( const auto& psec : sections ) { + // SHF_ALLOC sections are matched based on the virtual address + // otherwise the file offset is matched + if ( ( ( psec->get_flags() & SHF_ALLOC ) == SHF_ALLOC ) + ? is_sect_in_seg( psec->get_address(), + psec->get_size(), segVBaseAddr, + segVEndAddr ) + : is_sect_in_seg( psec->get_offset(), psec->get_size(), + segBaseOffset, segEndOffset ) ) { + + // If it is a TLS segment, add TLS sections only and vice versa + if ( ( ( seg->get_type() == PT_TLS ) && + ( ( psec->get_flags() & SHF_TLS ) != SHF_TLS ) ) || + ( ( ( psec->get_flags() & SHF_TLS ) == SHF_TLS ) && + ( seg->get_type() != PT_TLS ) ) ) + continue; + + // Alignment of segment shall not be updated, to preserve original value + // It will be re-calculated on saving. + seg->add_section_index( psec->get_index(), 0 ); + } + } + } + + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Save the ELF header to a stream + //! \param stream The output stream to save to + //! \return True if successful, false otherwise + bool save_header( std::ostream& stream ) const + { + return header->save( stream ); + } + + //------------------------------------------------------------------------------ + //! \brief Save the sections to a stream + //! \param stream The output stream to save to + //! \return True if successful, false otherwise + bool save_sections( std::ostream& stream ) const + { + for ( const auto& sec : sections_ ) { + std::streampos headerPosition = + static_cast( header->get_sections_offset() ) + + static_cast( + header->get_section_entry_size() ) * + sec->get_index(); + + sec->save( stream, headerPosition, sec->get_offset() ); + } + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Save the segments to a stream + //! \param stream The output stream to save to + //! \return True if successful, false otherwise + bool save_segments( std::ostream& stream ) const + { + for ( const auto& seg : segments_ ) { + std::streampos headerPosition = + static_cast( header->get_segments_offset() ) + + static_cast( + header->get_segment_entry_size() ) * + seg->get_index(); + + seg->save( stream, headerPosition, seg->get_offset() ); + } + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Check if a section is without a segment + //! \param section_index The index of the section + //! \return True if the section is without a segment, false otherwise + bool is_section_without_segment( unsigned int section_index ) const + { + bool found = false; + + for ( unsigned int j = 0; !found && ( j < segments.size() ); ++j ) { + for ( Elf_Half k = 0; + !found && ( k < segments[j]->get_sections_num() ); ++k ) { + found = segments[j]->get_section_index_at( k ) == section_index; + } + } + + return !found; + } + + //------------------------------------------------------------------------------ + //! \brief Check if a segment is a subsequence of another segment + //! \param seg1 Pointer to the first segment + //! \param seg2 Pointer to the second segment + //! \return True if seg1 is a subsequence of seg2, false otherwise + static bool is_subsequence_of( const segment* seg1, const segment* seg2 ) + { + // Return 'true' if sections of seg1 are a subset of sections in seg2 + const std::vector& sections1 = seg1->get_sections(); + const std::vector& sections2 = seg2->get_sections(); + + bool found = false; + if ( sections1.size() < sections2.size() ) { + found = std::includes( sections2.begin(), sections2.end(), + sections1.begin(), sections1.end() ); + } + + return found; + } + + //------------------------------------------------------------------------------ + //! \brief Get ordered segments + //! \return Vector of ordered segments + std::vector get_ordered_segments() const + { + std::vector res; + std::deque worklist; + + res.reserve( segments.size() ); + for ( const auto& seg : segments ) { + worklist.emplace_back( seg.get() ); + } + + // Bring the segments which start at address 0 to the front + size_t nextSlot = 0; + for ( size_t i = 0; i < worklist.size(); ++i ) { + if ( i != nextSlot && worklist[i]->is_offset_initialized() && + worklist[i]->get_offset() == 0 ) { + if ( worklist[nextSlot]->get_offset() == 0 ) { + ++nextSlot; + } + std::swap( worklist[i], worklist[nextSlot] ); + ++nextSlot; + } + } + + while ( !worklist.empty() ) { + segment* seg = worklist.front(); + worklist.pop_front(); + + size_t i = 0; + for ( ; i < worklist.size(); ++i ) { + if ( is_subsequence_of( seg, worklist[i] ) ) { + break; + } + } + + if ( i < worklist.size() ) { + worklist.emplace_back( seg ); + } + else { + res.emplace_back( seg ); + } + } + + return res; + } + + //------------------------------------------------------------------------------ + //! \brief Layout sections without segments + //! \return True if successful, false otherwise + bool layout_sections_without_segments() + { + for ( unsigned int i = 0; i < sections_.size(); ++i ) { + if ( is_section_without_segment( i ) ) { + const auto& sec = sections_[i]; + + if ( Elf_Xword section_align = sec->get_addr_align(); + section_align > 1 && + current_file_pos % section_align != 0 ) { + current_file_pos += + section_align - current_file_pos % section_align; + } + + if ( 0 != sec->get_index() ) { + sec->set_offset( current_file_pos ); + } + + if ( SHT_NOBITS != sec->get_type() && + SHT_NULL != sec->get_type() ) { + current_file_pos += sec->get_size(); + } + } + } + + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Calculate segment alignment + void calc_segment_alignment() const + { + for ( const auto& seg : segments_ ) { + for ( Elf_Half i = 0; i < seg->get_sections_num(); ++i ) { + const auto& sect = sections_[seg->get_section_index_at( i )]; + if ( sect->get_addr_align() > seg->get_align() ) { + seg->set_align( sect->get_addr_align() ); + } + } + } + } + + //------------------------------------------------------------------------------ + //! \brief Layout segments and their sections + //! \return True if successful, false otherwise + bool layout_segments_and_their_sections() + { + std::vector worklist; + std::vector section_generated( sections.size(), false ); + + // Get segments in a order in where segments which contain a + // sub sequence of other segments are located at the end + worklist = get_ordered_segments(); + + for ( auto* seg : worklist ) { + Elf_Xword segment_memory = 0; + Elf_Xword segment_filesize = 0; + Elf_Xword seg_start_pos = current_file_pos; + // Special case: PHDR segment + // This segment contains the program headers but no sections + if ( seg->get_type() == PT_PHDR && seg->get_sections_num() == 0 ) { + seg_start_pos = header->get_segments_offset(); + segment_memory = segment_filesize = + header->get_segment_entry_size() * + static_cast( header->get_segments_num() ); + } + // Special case: + else if ( seg->is_offset_initialized() && seg->get_offset() == 0 ) { + seg_start_pos = 0; + if ( seg->get_sections_num() > 0 ) { + segment_memory = segment_filesize = current_file_pos; + } + } + // New segments with not generated sections + // have to be aligned + else if ( seg->get_sections_num() > 0 && + !section_generated[seg->get_section_index_at( 0 )] ) { + Elf_Xword align = seg->get_align() > 0 ? seg->get_align() : 1; + Elf64_Off cur_page_alignment = current_file_pos % align; + Elf64_Off req_page_alignment = + seg->get_virtual_address() % align; + Elf64_Off adjustment = req_page_alignment - cur_page_alignment; + + current_file_pos += ( seg->get_align() + adjustment ) % align; + seg_start_pos = current_file_pos; + } + else if ( seg->get_sections_num() > 0 ) { + seg_start_pos = + sections[seg->get_section_index_at( 0 )]->get_offset(); + } + + // Write segment's data + if ( !write_segment_data( seg, section_generated, segment_memory, + segment_filesize, seg_start_pos ) ) { + return false; + } + + seg->set_file_size( segment_filesize ); + + // If we already have a memory size from loading an elf file (value > 0), + // it must not shrink! + // Memory size may be bigger than file size and it is the loader's job to do something + // with the surplus bytes in memory, like initializing them with a defined value. + if ( seg->get_memory_size() < segment_memory ) { + seg->set_memory_size( segment_memory ); + } + + seg->set_offset( seg_start_pos ); + } + + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Layout the section table + //! \return True if successful, false otherwise + bool layout_section_table() + { + // Simply place the section table at the end for now + Elf64_Off alignmentError = current_file_pos % 16; + current_file_pos += 16 - alignmentError; + header->set_sections_offset( current_file_pos ); + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Write segment data + //! \param seg Pointer to the segment + //! \param section_generated Vector of section generated flags + //! \param segment_memory Reference to the segment memory size + //! \param segment_filesize Reference to the segment file size + //! \param seg_start_pos The start position of the segment + //! \return True if successful, false otherwise + bool write_segment_data( const segment* seg, + std::vector& section_generated, + Elf_Xword& segment_memory, + Elf_Xword& segment_filesize, + const Elf_Xword& seg_start_pos ) + { + for ( Elf_Half j = 0; j < seg->get_sections_num(); ++j ) { + Elf_Half index = seg->get_section_index_at( j ); + + section* sec = sections[index]; + + // The NULL section is always generated + if ( SHT_NULL == sec->get_type() ) { + section_generated[index] = true; + continue; + } + + Elf_Xword section_align = 0; + // Fix up the alignment + if ( !section_generated[index] && sec->is_address_initialized() && + SHT_NOBITS != sec->get_type() && SHT_NULL != sec->get_type() && + 0 != sec->get_size() ) { + // Align the sections based on the virtual addresses + // when possible (this is what matters for execution) + Elf64_Off req_offset = + sec->get_address() - seg->get_virtual_address(); + Elf64_Off cur_offset = current_file_pos - seg_start_pos; + if ( req_offset < cur_offset ) { + // something has gone awfully wrong, abort! + // section_align would turn out negative, seeking backwards and overwriting previous data + return false; + } + section_align = req_offset - cur_offset; + } + else if ( !section_generated[index] && + !sec->is_address_initialized() ) { + // If no address has been specified then only the section + // alignment constraint has to be matched + Elf_Xword align = sec->get_addr_align(); + if ( align == 0 ) { + align = 1; + } + Elf64_Off error = current_file_pos % align; + section_align = ( align - error ) % align; + } + else if ( section_generated[index] ) { + // Alignment for already generated sections + section_align = + sec->get_offset() - seg_start_pos - segment_filesize; + } + + // Determine the segment file and memory sizes + // Special case .tbss section (NOBITS) in non TLS segment + if ( ( ( sec->get_flags() & SHF_ALLOC ) == SHF_ALLOC ) && + !( ( ( sec->get_flags() & SHF_TLS ) == SHF_TLS ) && + ( seg->get_type() != PT_TLS ) && + ( SHT_NOBITS == sec->get_type() ) ) ) { + segment_memory += sec->get_size() + section_align; + } + + if ( SHT_NOBITS != sec->get_type() ) { + segment_filesize += sec->get_size() + section_align; + } + + // Nothing to be done when generating nested segments + if ( section_generated[index] ) { + continue; + } + + current_file_pos += section_align; + + // Set the section addresses when missing + if ( !sec->is_address_initialized() ) { + sec->set_address( seg->get_virtual_address() + + current_file_pos - seg_start_pos ); + } + + if ( 0 != sec->get_index() ) { + sec->set_offset( current_file_pos ); + } + + if ( SHT_NOBITS != sec->get_type() ) { + current_file_pos += sec->get_size(); + } + + section_generated[index] = true; + } + + return true; + } + + //------------------------------------------------------------------------------ + public: + //! \class Sections + //! \brief The Sections class provides methods to manipulate sections in an ELF file. + friend class Sections; + class Sections + { + public: + //------------------------------------------------------------------------------ + //! \brief Constructor + //! \param parent Pointer to the parent elfio object + explicit Sections( elfio* parent ) : parent( parent ) {} + + //------------------------------------------------------------------------------ + //! \brief Get the number of sections + //! \return The number of sections + Elf_Half size() const + { + return static_cast( parent->sections_.size() ); + } + + //------------------------------------------------------------------------------ + //! \brief Get a section by index + //! \param index The index of the section + //! \return Pointer to the section, or nullptr if not found + section* operator[]( unsigned int index ) const + { + section* sec = nullptr; + + if ( index < parent->sections_.size() ) { + sec = parent->sections_[index].get(); + } + + return sec; + } + + //------------------------------------------------------------------------------ + //! \brief Get a section by name + //! \param name The name of the section + //! \return Pointer to the section, or nullptr if not found + section* operator[]( const std::string_view& name ) const + { + section* sec = nullptr; + + for ( const auto& it : parent->sections_ ) { + if ( it->get_name() == name ) { + sec = it.get(); + break; + } + } + + return sec; + } + + //------------------------------------------------------------------------------ + //! \brief Add a new section + //! \param name The name of the section + //! \return Pointer to the created section + section* add( const std::string& name ) const + { + section* new_section = parent->create_section(); + new_section->set_name( name ); + + Elf_Half str_index = parent->get_section_name_str_index(); + section* string_table( parent->sections_[str_index].get() ); + string_section_accessor str_writer( string_table ); + Elf_Word pos = str_writer.add_string( name ); + new_section->set_name_string_offset( pos ); + + return new_section; + } + + //------------------------------------------------------------------------------ + //! \brief Get an iterator to the beginning of the sections + //! \return Iterator to the beginning of the sections + std::vector>::iterator begin() + { + return parent->sections_.begin(); + } + + //------------------------------------------------------------------------------ + //! \brief Get an iterator to the end of the sections + //! \return Iterator to the end of the sections + std::vector>::iterator end() + { + return parent->sections_.end(); + } + + //------------------------------------------------------------------------------ + //! \brief Get a const iterator to the beginning of the sections + //! \return Const iterator to the beginning of the sections + std::vector>::const_iterator begin() const + { + return parent->sections_.cbegin(); + } + + //------------------------------------------------------------------------------ + //! \brief Get a const iterator to the end of the sections + //! \return Const iterator to the end of the sections + std::vector>::const_iterator end() const + { + return parent->sections_.cend(); + } + + //------------------------------------------------------------------------------ + private: + elfio* parent; //!< Pointer to the parent elfio object + }; + Sections sections; //!< Sections object + + //------------------------------------------------------------------------------ + //! \class Segments + //! \brief The Segments class provides methods to manipulate segments in an ELF file. + friend class Segments; + class Segments + { + public: + //------------------------------------------------------------------------------ + //! \brief Constructor + //! \param parent Pointer to the parent elfio object + explicit Segments( elfio* parent ) : parent( parent ) {} + + //------------------------------------------------------------------------------ + //! \brief Get the number of segments + //! \return The number of segments + Elf_Half size() const + { + return static_cast( parent->segments_.size() ); + } + + //------------------------------------------------------------------------------ + //! \brief Get a segment by index + //! \param index The index of the segment + //! \return Pointer to the segment, or nullptr if not found + segment* operator[]( unsigned int index ) const + { + return parent->segments_[index].get(); + } + + //------------------------------------------------------------------------------ + //! \brief Add a new segment + //! \return Pointer to the created segment + segment* add() { return parent->create_segment(); } + + //------------------------------------------------------------------------------ + //! \brief Get an iterator to the beginning of the segments + //! \return Iterator to the beginning of the segments + std::vector>::iterator begin() + { + return parent->segments_.begin(); + } + + //------------------------------------------------------------------------------ + //! \brief Get an iterator to the end of the segments + //! \return Iterator to the end of the segments + std::vector>::iterator end() + { + return parent->segments_.end(); + } + + //------------------------------------------------------------------------------ + //! \brief Get a const iterator to the beginning of the segments + //! \return Const iterator to the beginning of the segments + std::vector>::const_iterator begin() const + { + return parent->segments_.cbegin(); + } + + //------------------------------------------------------------------------------ + //! \brief Get a const iterator to the end of the segments + //! \return Const iterator to the end of the segments + std::vector>::const_iterator end() const + { + return parent->segments_.cend(); + } + + //------------------------------------------------------------------------------ + private: + elfio* parent; //!< Pointer to the parent elfio object + }; + Segments segments; //!< Segments object + + //------------------------------------------------------------------------------ + private: + std::unique_ptr pstream = + nullptr; //!< Pointer to the input stream + std::unique_ptr header = nullptr; //!< Pointer to the ELF header + std::vector> sections_; //!< Vector of sections + std::vector> segments_; //!< Vector of segments + std::shared_ptr convertor; //!< Endianness convertor + std::shared_ptr addr_translator; //!< Address translator + std::shared_ptr compression = + nullptr; //!< Pointer to the compression interface + + Elf_Xword current_file_pos = 0; //!< Current file position +}; + +} // namespace ELFIO + +#include "elfio/elfio_symbols.hpp" +#include "elfio/elfio_note.hpp" +#include "elfio/elfio_relocation.hpp" +#include "elfio/elfio_dynamic.hpp" +#include "elfio/elfio_array.hpp" +#include "elfio/elfio_modinfo.hpp" +#include "elfio/elfio_versym.hpp" + +#endif // ELFIO_HPP diff --git a/vyld/elfio/elf_types.hpp b/vyld/elfio/elf_types.hpp new file mode 100644 index 0000000..ff6743f --- /dev/null +++ b/vyld/elfio/elf_types.hpp @@ -0,0 +1,1564 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFTYPES_H +#define ELFTYPES_H + +#include + +#ifdef __cplusplus +namespace ELFIO { +#endif + +using Elf_Half = std::uint16_t; +using Elf_Word = std::uint32_t; +using Elf_Sword = std::int32_t; +using Elf_Xword = std::uint64_t; +using Elf_Sxword = std::int64_t; + +using Elf32_Addr = std::uint32_t; +using Elf32_Off = std::uint32_t; +using Elf64_Addr = std::uint64_t; +using Elf64_Off = std::uint64_t; + +using Elf32_Half = Elf_Half; +using Elf64_Half = Elf_Half; +using Elf32_Word = Elf_Word; +using Elf64_Word = Elf_Word; +using Elf32_Sword = Elf_Sword; +using Elf64_Sword = Elf_Sword; + +/////////////////////// +// ELF Header Constants + +// File type +constexpr Elf_Half ET_NONE = 0; +constexpr Elf_Half ET_REL = 1; +constexpr Elf_Half ET_EXEC = 2; +constexpr Elf_Half ET_DYN = 3; +constexpr Elf_Half ET_CORE = 4; +constexpr Elf_Half ET_LOOS = 0xFE00; +constexpr Elf_Half ET_HIOS = 0xFEFF; +constexpr Elf_Half ET_LOPROC = 0xFF00; +constexpr Elf_Half ET_HIPROC = 0xFFFF; + +// clang-format off +// Machine number +constexpr Elf_Half EM_NONE = 0 ; // No machine +constexpr Elf_Half EM_M32 = 1 ; // AT&T WE 32100 +constexpr Elf_Half EM_SPARC = 2 ; // SUN SPARC +constexpr Elf_Half EM_386 = 3 ; // Intel 80386 +constexpr Elf_Half EM_68K = 4 ; // Motorola m68k family +constexpr Elf_Half EM_88K = 5 ; // Motorola m88k family +constexpr Elf_Half EM_486 = 6 ; // Intel 80486// Reserved for future use +constexpr Elf_Half EM_860 = 7 ; // Intel 80860 +constexpr Elf_Half EM_MIPS = 8 ; // MIPS R3000 (officially, big-endian only) +constexpr Elf_Half EM_S370 = 9 ; // IBM System/370 +constexpr Elf_Half EM_MIPS_RS3_LE = 10; // MIPS R3000 little-endian (Deprecated) +constexpr Elf_Half EM_res011 = 11; // Reserved +constexpr Elf_Half EM_res012 = 12; // Reserved +constexpr Elf_Half EM_res013 = 13; // Reserved +constexpr Elf_Half EM_res014 = 14; // Reserved +constexpr Elf_Half EM_PARISC = 15; // HPPA +constexpr Elf_Half EM_res016 = 16; // Reserved +constexpr Elf_Half EM_VPP550 = 17; // Fujitsu VPP500 +constexpr Elf_Half EM_SPARC32PLUS = 18; // Sun's "v8plus" +constexpr Elf_Half EM_960 = 19; // Intel 80960 +constexpr Elf_Half EM_PPC = 20; // PowerPC +constexpr Elf_Half EM_PPC64 = 21; // 64-bit PowerPC +constexpr Elf_Half EM_S390 = 22; // IBM S/390 +constexpr Elf_Half EM_SPU = 23; // Sony/Toshiba/IBM SPU +constexpr Elf_Half EM_res024 = 24; // Reserved +constexpr Elf_Half EM_res025 = 25; // Reserved +constexpr Elf_Half EM_res026 = 26; // Reserved +constexpr Elf_Half EM_res027 = 27; // Reserved +constexpr Elf_Half EM_res028 = 28; // Reserved +constexpr Elf_Half EM_res029 = 29; // Reserved +constexpr Elf_Half EM_res030 = 30; // Reserved +constexpr Elf_Half EM_res031 = 31; // Reserved +constexpr Elf_Half EM_res032 = 32; // Reserved +constexpr Elf_Half EM_res033 = 33; // Reserved +constexpr Elf_Half EM_res034 = 34; // Reserved +constexpr Elf_Half EM_res035 = 35; // Reserved +constexpr Elf_Half EM_V800 = 36; // NEC V800 series +constexpr Elf_Half EM_FR20 = 37; // Fujitsu FR20 +constexpr Elf_Half EM_RH32 = 38; // TRW RH32 +constexpr Elf_Half EM_MCORE = 39; // Motorola M*Core // May also be taken by Fujitsu MMA +constexpr Elf_Half EM_RCE = 39; // Old name for MCore +constexpr Elf_Half EM_ARM = 40; // ARM +constexpr Elf_Half EM_OLD_ALPHA = 41; // Digital Alpha +constexpr Elf_Half EM_SH = 42; // Renesas (formerly Hitachi) / SuperH SH +constexpr Elf_Half EM_SPARCV9 = 43; // SPARC v9 64-bit +constexpr Elf_Half EM_TRICORE = 44; // Siemens Tricore embedded processor +constexpr Elf_Half EM_ARC = 45; // ARC Cores +constexpr Elf_Half EM_H8_300 = 46; // Renesas (formerly Hitachi) H8/300 +constexpr Elf_Half EM_H8_300H = 47; // Renesas (formerly Hitachi) H8/300H +constexpr Elf_Half EM_H8S = 48; // Renesas (formerly Hitachi) H8S +constexpr Elf_Half EM_H8_500 = 49; // Renesas (formerly Hitachi) H8/500 +constexpr Elf_Half EM_IA_64 = 50; // Intel IA-64 Processor +constexpr Elf_Half EM_MIPS_X = 51; // Stanford MIPS-X +constexpr Elf_Half EM_COLDFIRE = 52; // Motorola Coldfire +constexpr Elf_Half EM_68HC12 = 53; // Motorola M68HC12 +constexpr Elf_Half EM_MMA = 54; // Fujitsu Multimedia Accelerator +constexpr Elf_Half EM_PCP = 55; // Siemens PCP +constexpr Elf_Half EM_NCPU = 56; // Sony nCPU embedded RISC processor +constexpr Elf_Half EM_NDR1 = 57; // Denso NDR1 microprocesspr +constexpr Elf_Half EM_STARCORE = 58; // Motorola Star*Core processor +constexpr Elf_Half EM_ME16 = 59; // Toyota ME16 processor +constexpr Elf_Half EM_ST100 = 60; // STMicroelectronics ST100 processor +constexpr Elf_Half EM_TINYJ = 61; // Advanced Logic Corp. TinyJ embedded processor +constexpr Elf_Half EM_X86_64 = 62; // Advanced Micro Devices X86-64 processor +constexpr Elf_Half EM_PDSP = 63; // Sony DSP Processor +constexpr Elf_Half EM_PDP10 = 64; // Digital Equipment Corp. PDP-10 +constexpr Elf_Half EM_PDP11 = 65; // Digital Equipment Corp. PDP-11 +constexpr Elf_Half EM_FX66 = 66; // Siemens FX66 microcontroller +constexpr Elf_Half EM_ST9PLUS = 67; // STMicroelectronics ST9+ 8/16 bit microcontroller +constexpr Elf_Half EM_ST7 = 68 ; // STMicroelectronics ST7 8-bit microcontroller +constexpr Elf_Half EM_68HC16 = 69 ; // Motorola MC68HC16 Microcontroller +constexpr Elf_Half EM_68HC11 = 70 ; // Motorola MC68HC11 Microcontroller +constexpr Elf_Half EM_68HC08 = 71 ; // Motorola MC68HC08 Microcontroller +constexpr Elf_Half EM_68HC05 = 72 ; // Motorola MC68HC05 Microcontroller +constexpr Elf_Half EM_SVX = 73 ; // Silicon Graphics SVx +constexpr Elf_Half EM_ST19 = 74 ; // STMicroelectronics ST19 8-bit cpu +constexpr Elf_Half EM_VAX = 75 ; // Digital VAX +constexpr Elf_Half EM_CRIS = 76 ; // Axis Communications 32-bit embedded processor +constexpr Elf_Half EM_JAVELIN = 77 ; // Infineon Technologies 32-bit embedded cpu +constexpr Elf_Half EM_FIREPATH = 78 ; // Element 14 64-bit DSP processor +constexpr Elf_Half EM_ZSP = 79 ; // LSI Logic's 16-bit DSP processor +constexpr Elf_Half EM_MMIX = 80 ; // Donald Knuth's educational 64-bit processor +constexpr Elf_Half EM_HUANY = 81 ; // Harvard's machine-independent format +constexpr Elf_Half EM_PRISM = 82 ; // SiTera Prism +constexpr Elf_Half EM_AVR = 83 ; // Atmel AVR 8-bit microcontroller +constexpr Elf_Half EM_FR30 = 84 ; // Fujitsu FR30 +constexpr Elf_Half EM_D10V = 85 ; // Mitsubishi D10V +constexpr Elf_Half EM_D30V = 86 ; // Mitsubishi D30V +constexpr Elf_Half EM_V850 = 87 ; // NEC v850 +constexpr Elf_Half EM_M32R = 88 ; // Renesas M32R (formerly Mitsubishi M32R) +constexpr Elf_Half EM_MN10300 = 89 ; // Matsushita MN10300 +constexpr Elf_Half EM_MN10200 = 90 ; // Matsushita MN10200 +constexpr Elf_Half EM_PJ = 91 ; // picoJava +constexpr Elf_Half EM_OPENRISC = 92 ; // OpenRISC 32-bit embedded processor +constexpr Elf_Half EM_ARC_A5 = 93 ; // ARC Cores Tangent-A5 +constexpr Elf_Half EM_XTENSA = 94 ; // Tensilica Xtensa Architecture +constexpr Elf_Half EM_VIDEOCORE = 95 ; // Alphamosaic VideoCore processor +constexpr Elf_Half EM_TMM_GPP = 96 ; // Thompson Multimedia General Purpose Processor +constexpr Elf_Half EM_NS32K = 97 ; // National Semiconductor 32000 series +constexpr Elf_Half EM_TPC = 98 ; // Tenor Network TPC processor +constexpr Elf_Half EM_SNP1K = 99 ; // Trebia SNP 1000 processor +constexpr Elf_Half EM_ST200 = 100; // STMicroelectronics ST200 microcontroller +constexpr Elf_Half EM_IP2K = 101; // Ubicom IP2022 micro controller +constexpr Elf_Half EM_MAX = 102; // MAX Processor +constexpr Elf_Half EM_CR = 103; // National Semiconductor CompactRISC +constexpr Elf_Half EM_F2MC16 = 104; // Fujitsu F2MC16 +constexpr Elf_Half EM_MSP430 = 105; // TI msp430 micro controller +constexpr Elf_Half EM_BLACKFIN = 106; // ADI Blackfin +constexpr Elf_Half EM_SE_C33 = 107; // S1C33 Family of Seiko Epson processors +constexpr Elf_Half EM_SEP = 108; // Sharp embedded microprocessor +constexpr Elf_Half EM_ARCA = 109; // Arca RISC Microprocessor +constexpr Elf_Half EM_UNICORE = 110; // Microprocessor series from PKU-Unity Ltd. +constexpr Elf_Half EM_EXCESS = 111; // eXcess: 16/32/64-bit configurable embedded CPU +constexpr Elf_Half EM_DXP = 112; // Icera Semiconductor Inc. Deep Execution Processor +constexpr Elf_Half EM_ALTERA_NIOS2 = 113; // Altera Nios II soft-core processor +constexpr Elf_Half EM_CRX = 114; // National Semiconductor CRX +constexpr Elf_Half EM_XGATE = 115; // Motorola XGATE embedded processor +constexpr Elf_Half EM_C166 = 116; // Infineon C16x/XC16x processor +constexpr Elf_Half EM_M16C = 117; // Renesas M16C series microprocessors +constexpr Elf_Half EM_DSPIC30F = 118; // Microchip Technology dsPIC30F DSignal Controller +constexpr Elf_Half EM_CE = 119; // Freescale Communication Engine RISC core +constexpr Elf_Half EM_M32C = 120; // Renesas M32C series microprocessors +constexpr Elf_Half EM_res121 = 121; // Reserved +constexpr Elf_Half EM_res122 = 122; // Reserved +constexpr Elf_Half EM_res123 = 123; // Reserved +constexpr Elf_Half EM_res124 = 124; // Reserved +constexpr Elf_Half EM_res125 = 125; // Reserved +constexpr Elf_Half EM_res126 = 126; // Reserved +constexpr Elf_Half EM_res127 = 127; // Reserved +constexpr Elf_Half EM_res128 = 128; // Reserved +constexpr Elf_Half EM_res129 = 129; // Reserved +constexpr Elf_Half EM_res130 = 130; // Reserved +constexpr Elf_Half EM_TSK3000 = 131; // Altium TSK3000 core +constexpr Elf_Half EM_RS08 = 132; // Freescale RS08 embedded processor +constexpr Elf_Half EM_res133 = 133; // Reserved +constexpr Elf_Half EM_ECOG2 = 134; // Cyan Technology eCOG2 microprocessor +constexpr Elf_Half EM_SCORE = 135; // Sunplus Score +constexpr Elf_Half EM_SCORE7 = 135; // Sunplus S+core7 RISC processor +constexpr Elf_Half EM_DSP24 = 136; // New Japan Radio (NJR) 24-bit DSP Processor +constexpr Elf_Half EM_VIDEOCORE3 = 137; // Broadcom VideoCore III processor +constexpr Elf_Half EM_LATTICEMICO32 = 138; // RISC processor for Lattice FPGA architecture +constexpr Elf_Half EM_SE_C17 = 139; // Seiko Epson C17 family +constexpr Elf_Half EM_TI_C6000 = 140; // Texas Instruments TMS320C6000 DSP family +constexpr Elf_Half EM_TI_C2000 = 141; // Texas Instruments TMS320C2000 DSP family +constexpr Elf_Half EM_TI_C5500 = 142; // Texas Instruments TMS320C55x DSP family +constexpr Elf_Half EM_res143 = 143; // Reserved +constexpr Elf_Half EM_res144 = 144; // Reserved +constexpr Elf_Half EM_res145 = 145; // Reserved +constexpr Elf_Half EM_res146 = 146; // Reserved +constexpr Elf_Half EM_res147 = 147; // Reserved +constexpr Elf_Half EM_res148 = 148; // Reserved +constexpr Elf_Half EM_res149 = 149; // Reserved +constexpr Elf_Half EM_res150 = 150; // Reserved +constexpr Elf_Half EM_res151 = 151; // Reserved +constexpr Elf_Half EM_res152 = 152; // Reserved +constexpr Elf_Half EM_res153 = 153; // Reserved +constexpr Elf_Half EM_res154 = 154; // Reserved +constexpr Elf_Half EM_res155 = 155; // Reserved +constexpr Elf_Half EM_res156 = 156; // Reserved +constexpr Elf_Half EM_res157 = 157; // Reserved +constexpr Elf_Half EM_res158 = 158; // Reserved +constexpr Elf_Half EM_res159 = 159; // Reserved +constexpr Elf_Half EM_MMDSP_PLUS = 160; // STMicroelectronics 64bit VLIW Data Signal Processor +constexpr Elf_Half EM_CYPRESS_M8C = 161; // Cypress M8C microprocessor +constexpr Elf_Half EM_R32C = 162; // Renesas R32C series microprocessors +constexpr Elf_Half EM_TRIMEDIA = 163; // NXP Semiconductors TriMedia architecture family +constexpr Elf_Half EM_QDSP6 = 164; // QUALCOMM DSP6 Processor +constexpr Elf_Half EM_8051 = 165; // Intel 8051 and variants +constexpr Elf_Half EM_STXP7X = 166; // STMicroelectronics STxP7x family +constexpr Elf_Half EM_NDS32 = 167; // Andes Technology embedded RISC processor family +constexpr Elf_Half EM_ECOG1 = 168; // Cyan Technology eCOG1X family +constexpr Elf_Half EM_ECOG1X = 168; // Cyan Technology eCOG1X family +constexpr Elf_Half EM_MAXQ30 = 169; // Dallas Semiconductor MAXQ30 Core Micro-controllers +constexpr Elf_Half EM_XIMO16 = 170; // New Japan Radio (NJR) 16-bit DSP Processor +constexpr Elf_Half EM_MANIK = 171; // M2000 Reconfigurable RISC Microprocessor +constexpr Elf_Half EM_CRAYNV2 = 172; // Cray Inc. NV2 vector architecture +constexpr Elf_Half EM_RX = 173; // Renesas RX family +constexpr Elf_Half EM_METAG = 174; // Imagination Technologies META processor architecture +constexpr Elf_Half EM_MCST_ELBRUS = 175; // MCST Elbrus general purpose hardware architecture +constexpr Elf_Half EM_ECOG16 = 176; // Cyan Technology eCOG16 family +constexpr Elf_Half EM_CR16 = 177; // National Semiconductor CompactRISC 16-bit processor +constexpr Elf_Half EM_ETPU = 178; // Freescale Extended Time Processing Unit +constexpr Elf_Half EM_SLE9X = 179; // Infineon Technologies SLE9X core +constexpr Elf_Half EM_L1OM = 180; // Intel L1OM +constexpr Elf_Half EM_INTEL181 = 181; // Reserved by Intel +constexpr Elf_Half EM_INTEL182 = 182; // Reserved by Intel +constexpr Elf_Half EM_AARCH64 = 183; // ARM AArch64 +constexpr Elf_Half EM_res184 = 184; // Reserved by ARM +constexpr Elf_Half EM_AVR32 = 185; // Atmel Corporation 32-bit microprocessor family +constexpr Elf_Half EM_STM8 = 186; // STMicroeletronics STM8 8-bit microcontroller +constexpr Elf_Half EM_TILE64 = 187; // Tilera TILE64 multicore architecture family +constexpr Elf_Half EM_TILEPRO = 188; // Tilera TILEPro multicore architecture family +constexpr Elf_Half EM_MICROBLAZE = 189; // Xilinx MicroBlaze 32-bit RISC soft processor core +constexpr Elf_Half EM_CUDA = 190; // NVIDIA CUDA architecture +constexpr Elf_Half EM_TILEGX = 191; // Tilera TILE-Gx multicore architecture family +constexpr Elf_Half EM_CLOUDSHIELD = 192; // CloudShield architecture family +constexpr Elf_Half EM_COREA_1ST = 193; // KIPO-KAIST Core-A 1st generation processor family +constexpr Elf_Half EM_COREA_2ND = 194; // KIPO-KAIST Core-A 2nd generation processor family +constexpr Elf_Half EM_ARC_COMPACT2 = 195; // Synopsys ARCompact V2 +constexpr Elf_Half EM_OPEN8 = 196; // Open8 8-bit RISC soft processor core +constexpr Elf_Half EM_RL78 = 197; // Renesas RL78 family +constexpr Elf_Half EM_VIDEOCORE5 = 198; // Broadcom VideoCore V processor +constexpr Elf_Half EM_78KOR = 199; // Renesas 78KOR family +constexpr Elf_Half EM_56800EX = 200; // Freescale 56800EX Digital Signal Controller (DSC) +constexpr Elf_Half EM_BA1 = 201; // Beyond BA1 CPU architecture +constexpr Elf_Half EM_BA2 = 202; // Beyond BA2 CPU architecture +constexpr Elf_Half EM_XCORE = 203; // XMOS xCORE processor family +constexpr Elf_Half EM_MCHP_PIC = 204; // Microchip 8-bit PIC(r) family +constexpr Elf_Half EM_INTEL205 = 205; // Reserved by Intel +constexpr Elf_Half EM_INTEL206 = 206; // Reserved by Intel +constexpr Elf_Half EM_INTEL207 = 207; // Reserved by Intel +constexpr Elf_Half EM_INTEL208 = 208; // Reserved by Intel +constexpr Elf_Half EM_INTEL209 = 209; // Reserved by Intel +constexpr Elf_Half EM_KM32 = 210; // KM211 KM32 32-bit processor +constexpr Elf_Half EM_KMX32 = 211; // KM211 KMX32 32-bit processor +constexpr Elf_Half EM_KMX16 = 212; // KM211 KMX16 16-bit processor +constexpr Elf_Half EM_KMX8 = 213; // KM211 KMX8 8-bit processor +constexpr Elf_Half EM_KVARC = 214; // KM211 KVARC processor +constexpr Elf_Half EM_CDP = 215; // Paneve CDP architecture family +constexpr Elf_Half EM_COGE = 216; // Cognitive Smart Memory Processor +constexpr Elf_Half EM_COOL = 217; // iCelero CoolEngine +constexpr Elf_Half EM_NORC = 218; // Nanoradio Optimized RISC +constexpr Elf_Half EM_CSR_KALIMBA = 219; // CSR Kalimba architecture family +constexpr Elf_Half EM_Z80 = 220; // Zilog Z80 +constexpr Elf_Half EM_VISIUM = 221; // Controls and Data Services VISIUMcore processor +constexpr Elf_Half EM_FT32 = 222; // FTDI Chip FT32 high performance 32-bit RISC architecture +constexpr Elf_Half EM_MOXIE = 223; // Moxie processor family +constexpr Elf_Half EM_AMDGPU = 224; // AMD GPU architecture +constexpr Elf_Half EM_RISCV = 243; // RISC-V +constexpr Elf_Half EM_LANAI = 244; // Lanai processor +constexpr Elf_Half EM_CEVA = 245; // CEVA Processor Architecture Family +constexpr Elf_Half EM_CEVA_X2 = 246; // CEVA X2 Processor Family +constexpr Elf_Half EM_BPF = 247; // Linux BPF - in-kernel virtual machine +constexpr Elf_Half EM_GRAPHCORE_IPU = 248; // Graphcore Intelligent Processing Unit +constexpr Elf_Half EM_IMG1 = 249; // Imagination Technologies +constexpr Elf_Half EM_NFP = 250; // Netronome Flow Processor (P) +constexpr Elf_Half EM_CSKY = 252; // C-SKY processor family +constexpr Elf_Half EM_ARC_COMPACT3_64 = 253; // Synopsys ARCv2.3 64-bit +constexpr Elf_Half EM_MCS6502 = 254; // MOS Technology MCS 6502 processor +constexpr Elf_Half EM_ARC_COMPACT3 = 255; // Synopsys ARCv2.3 32-bit +constexpr Elf_Half EM_KVX = 256; // Kalray VLIW core of the MPPA processor family +constexpr Elf_Half EM_65816 = 257; // WDC 65816/65C816 +constexpr Elf_Half EM_LOONGARCH = 258; // Loongson Loongarch +constexpr Elf_Half EM_KF32 = 259; // ChipON KungFu32 + +constexpr Elf_Half EM_MT = 0x2530; // Morpho Techologies MT processor +constexpr Elf_Half EM_ALPHA = 0x9026; // Alpha +constexpr Elf_Half EM_WEBASSEMBLY = 0x4157; // Web Assembly +constexpr Elf_Half EM_DLX = 0x5aa5; // OpenDLX +constexpr Elf_Half EM_XSTORMY16 = 0xad45; // Sanyo XStormy16 CPU core +constexpr Elf_Half EM_IQ2000 = 0xFEBA; // Vitesse IQ2000 +constexpr Elf_Half EM_M32C_OLD = 0xFEB; +constexpr Elf_Half EM_NIOS32 = 0xFEBB; // Altera Nios +constexpr Elf_Half EM_CYGNUS_MEP = 0xF00D; // Toshiba MeP Media Engine +constexpr Elf_Half EM_ADAPTEVA_EPIPHANY = 0x1223; // Adapteva EPIPHANY +constexpr Elf_Half EM_CYGNUS_FRV = 0x5441; // Fujitsu FR-V +constexpr Elf_Half EM_S12Z = 0x4DEF; // Freescale S12Z +// clang-format on + +// File version +constexpr unsigned char EV_NONE = 0; +constexpr unsigned char EV_CURRENT = 1; + +// Identification index +constexpr unsigned char EI_MAG0 = 0; +constexpr unsigned char EI_MAG1 = 1; +constexpr unsigned char EI_MAG2 = 2; +constexpr unsigned char EI_MAG3 = 3; +constexpr unsigned char EI_CLASS = 4; +constexpr unsigned char EI_DATA = 5; +constexpr unsigned char EI_VERSION = 6; +constexpr unsigned char EI_OSABI = 7; +constexpr unsigned char EI_ABIVERSION = 8; +constexpr unsigned char EI_PAD = 9; +constexpr unsigned char EI_NIDENT = 16; + +// Magic number +constexpr unsigned char ELFMAG0 = 0x7F; +constexpr unsigned char ELFMAG1 = 'E'; +constexpr unsigned char ELFMAG2 = 'L'; +constexpr unsigned char ELFMAG3 = 'F'; + +// File class +constexpr unsigned char ELFCLASSNONE = 0; +constexpr unsigned char ELFCLASS32 = 1; +constexpr unsigned char ELFCLASS64 = 2; + +// Encoding +constexpr unsigned char ELFDATANONE = 0; +constexpr unsigned char ELFDATA2LSB = 1; +constexpr unsigned char ELFDATA2MSB = 2; + +// clang-format off +// OS extensions +constexpr unsigned char ELFOSABI_NONE = 0; // No extensions or unspecified +constexpr unsigned char ELFOSABI_HPUX = 1; // Hewlett-Packard HP-UX +constexpr unsigned char ELFOSABI_NETBSD = 2; // NetBSD +constexpr unsigned char ELFOSABI_LINUX = 3; // Linux +constexpr unsigned char ELFOSABI_HURD = 4; // GNU Hurd +constexpr unsigned char ELFOSABI_SOLARIS = 6; // Sun Solaris +constexpr unsigned char ELFOSABI_AIX = 7; // AIX +constexpr unsigned char ELFOSABI_IRIX = 8; // IRIX +constexpr unsigned char ELFOSABI_FREEBSD = 9; // FreeBSD +constexpr unsigned char ELFOSABI_TRU64 = 10; // Compaq TRU64 UNIX +constexpr unsigned char ELFOSABI_MODESTO = 11; // Novell Modesto +constexpr unsigned char ELFOSABI_OPENBSD = 12; // Open BSD +constexpr unsigned char ELFOSABI_OPENVMS = 13; // Open VMS +constexpr unsigned char ELFOSABI_NSK = 14; // Hewlett-Packard Non-Stop Kernel +constexpr unsigned char ELFOSABI_AROS = 15; // Amiga Research OS +constexpr unsigned char ELFOSABI_FENIXOS = 16; // The FenixOS highly scalable multi-core OS +constexpr unsigned char ELFOSABI_NUXI = 17; // Nuxi CloudABI +constexpr unsigned char ELFOSABI_OPENVOS = 18; // Stratus Technologies OpenVOS +constexpr unsigned char ELFOSABI_ARM = 97; // ARM +constexpr unsigned char ELFOSABI_STANDALONE = 255; // Standalone (embedded) application + +// 64-255 Architecture-specific value range +// AMDGPU OS for HSA compatible compute kernels +constexpr unsigned char ELFOSABI_AMDGPU_HSA = 64; +// AMDGPU OS for AMD PAL compatible graphics +// shaders and compute kernels +constexpr unsigned char ELFOSABI_AMDGPU_PAL = 65; +// AMDGPU OS for Mesa3D compatible graphics +// shaders and compute kernels +constexpr unsigned char ELFOSABI_AMDGPU_MESA3D = 66; +// clang-format on + +constexpr unsigned char ELFABIVERSION_AMDGPU_HSA_V2 = 0; +constexpr unsigned char ELFABIVERSION_AMDGPU_HSA_V3 = 1; +constexpr unsigned char ELFABIVERSION_AMDGPU_HSA_V4 = 2; + +// AMDGPU specific e_flags +constexpr Elf_Word EF_AMDGPU_MACH = 0x0ff; // AMDGPU processor selection mask. +// Indicates if the XNACK target feature is +// enabled for all code contained in the ELF. +constexpr Elf_Word EF_AMDGPU_XNACK = 0x100; + +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_V2 = 0x01; +constexpr Elf_Word EF_AMDGPU_FEATURE_TRAP_HANDLER_V2 = 0x02; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_V3 = 0x100; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_V3 = 0x200; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_V4 = 0x300; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_UNSUPPORTED_V4 = 0x000; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_ANY_V4 = 0x100; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_OFF_V4 = 0x200; +constexpr Elf_Word EF_AMDGPU_FEATURE_XNACK_ON_V4 = 0x300; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_V4 = 0xc00; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_UNSUPPORTED_V4 = 0x000; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_ANY_V4 = 0x400; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_OFF_V4 = 0x800; +constexpr Elf_Word EF_AMDGPU_FEATURE_SRAMECC_ON_V4 = 0xc00; + +// AMDGPU processors +constexpr Elf_Word EF_AMDGPU_MACH_NONE = 0x000; // Unspecified processor. +constexpr Elf_Word EF_AMDGPU_MACH_R600_R600 = 0x001; +constexpr Elf_Word EF_AMDGPU_MACH_R600_R630 = 0x002; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RS880 = 0x003; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RV670 = 0x004; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RV710 = 0x005; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RV730 = 0x006; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RV770 = 0x007; +constexpr Elf_Word EF_AMDGPU_MACH_R600_CEDAR = 0x008; +constexpr Elf_Word EF_AMDGPU_MACH_R600_CYPRESS = 0x009; +constexpr Elf_Word EF_AMDGPU_MACH_R600_JUNIPER = 0x00a; +constexpr Elf_Word EF_AMDGPU_MACH_R600_REDWOOD = 0x00b; +constexpr Elf_Word EF_AMDGPU_MACH_R600_SUMO = 0x00c; +constexpr Elf_Word EF_AMDGPU_MACH_R600_BARTS = 0x00d; +constexpr Elf_Word EF_AMDGPU_MACH_R600_CAICOS = 0x00e; +constexpr Elf_Word EF_AMDGPU_MACH_R600_CAYMAN = 0x00f; +constexpr Elf_Word EF_AMDGPU_MACH_R600_TURKS = 0x010; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RESERVED_FIRST = 0x011; +constexpr Elf_Word EF_AMDGPU_MACH_R600_RESERVED_LAST = 0x01f; +constexpr Elf_Word EF_AMDGPU_MACH_R600_FIRST = EF_AMDGPU_MACH_R600_R600; +constexpr Elf_Word EF_AMDGPU_MACH_R600_LAST = EF_AMDGPU_MACH_R600_TURKS; + +// AMDGCN-based processors. +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX600 = 0x020; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX601 = 0x021; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX700 = 0x022; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX701 = 0x023; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX702 = 0x024; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX703 = 0x025; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX704 = 0x026; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_RESERVED_0X27 = 0x027; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX801 = 0x028; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX802 = 0x029; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX803 = 0x02a; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX810 = 0x02b; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX900 = 0x02c; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX902 = 0x02d; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX904 = 0x02e; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX906 = 0x02f; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX908 = 0x030; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX909 = 0x031; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX90C = 0x032; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1010 = 0x033; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1011 = 0x034; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1012 = 0x035; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1030 = 0x036; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1031 = 0x037; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1032 = 0x038; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1033 = 0x039; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX602 = 0x03a; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX705 = 0x03b; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX805 = 0x03c; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_RESERVED_0X3D = 0x03d; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1034 = 0x03e; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX90A = 0x03f; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_RESERVED_0X40 = 0x040; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_RESERVED_0X41 = 0x041; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_GFX1013 = 0x042; +// First/last AMDGCN-based processors. +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_FIRST = EF_AMDGPU_MACH_AMDGCN_GFX600; +constexpr Elf_Word EF_AMDGPU_MACH_AMDGCN_LAST = EF_AMDGPU_MACH_AMDGCN_GFX1013; + +///////////////////// +// Sections constants + +// Section indexes +constexpr Elf_Word SHN_UNDEF = 0; +constexpr Elf_Word SHN_LORESERVE = 0xFF00; +constexpr Elf_Word SHN_LOPROC = 0xFF00; +constexpr Elf_Word SHN_HIPROC = 0xFF1F; +constexpr Elf_Word SHN_LOOS = 0xFF20; +constexpr Elf_Word SHN_HIOS = 0xFF3F; +constexpr Elf_Word SHN_ABS = 0xFFF1; +constexpr Elf_Word SHN_COMMON = 0xFFF2; +constexpr Elf_Word SHN_XINDEX = 0xFFFF; +constexpr Elf_Word SHN_HIRESERVE = 0xFFFF; + +// Section types +constexpr Elf_Word SHT_NULL = 0; +constexpr Elf_Word SHT_PROGBITS = 1; +constexpr Elf_Word SHT_SYMTAB = 2; +constexpr Elf_Word SHT_STRTAB = 3; +constexpr Elf_Word SHT_RELA = 4; +constexpr Elf_Word SHT_HASH = 5; +constexpr Elf_Word SHT_DYNAMIC = 6; +constexpr Elf_Word SHT_NOTE = 7; +constexpr Elf_Word SHT_NOBITS = 8; +constexpr Elf_Word SHT_REL = 9; +constexpr Elf_Word SHT_SHLIB = 10; +constexpr Elf_Word SHT_DYNSYM = 11; +constexpr Elf_Word SHT_INIT_ARRAY = 14; +constexpr Elf_Word SHT_FINI_ARRAY = 15; +constexpr Elf_Word SHT_PREINIT_ARRAY = 16; +constexpr Elf_Word SHT_GROUP = 17; +constexpr Elf_Word SHT_SYMTAB_SHNDX = 18; +constexpr Elf_Word SHT_GNU_ATTRIBUTES = 0x6ffffff5; +constexpr Elf_Word SHT_GNU_HASH = 0x6ffffff6; +constexpr Elf_Word SHT_GNU_LIBLIST = 0x6ffffff7; +constexpr Elf_Word SHT_CHECKSUM = 0x6ffffff8; +constexpr Elf_Word SHT_LOSUNW = 0x6ffffffa; +constexpr Elf_Word SHT_SUNW_move = 0x6ffffffa; +constexpr Elf_Word SHT_SUNW_COMDAT = 0x6ffffffb; +constexpr Elf_Word SHT_SUNW_syminfo = 0x6ffffffc; +constexpr Elf_Word SHT_GNU_verdef = 0x6ffffffd; +constexpr Elf_Word SHT_GNU_verneed = 0x6ffffffe; +constexpr Elf_Word SHT_GNU_versym = 0x6fffffff; +constexpr Elf_Word SHT_LOOS = 0x60000000; +constexpr Elf_Word SHT_HIOS = 0x6fffffff; +constexpr Elf_Word SHT_LOPROC = 0x70000000; +constexpr Elf_Word SHT_ARM_EXIDX = 0x70000001; +constexpr Elf_Word SHT_ARM_PREEMPTMAP = 0x70000002; +constexpr Elf_Word SHT_ARM_ATTRIBUTES = 0x70000003; +constexpr Elf_Word SHT_ARM_DEBUGOVERLAY = 0x70000004; +constexpr Elf_Word SHT_ARM_OVERLAYSECTION = 0x70000005; +constexpr Elf_Word SHT_HIPROC = 0x7FFFFFFF; +constexpr Elf_Word SHT_LOUSER = 0x80000000; +// Used by Nintendo Wii U +constexpr Elf_Word SHT_RPL_EXPORTS = 0x80000001; +constexpr Elf_Word SHT_RPL_IMPORTS = 0x80000002; +constexpr Elf_Word SHT_RPL_CRCS = 0x80000003; +constexpr Elf_Word SHT_RPL_FILEINFO = 0x80000004; +constexpr Elf_Word SHT_HIUSER = 0xFFFFFFFF; + +// Section attribute flags +constexpr Elf_Xword SHF_WRITE = 0x1; +constexpr Elf_Xword SHF_ALLOC = 0x2; +constexpr Elf_Xword SHF_EXECINSTR = 0x4; +constexpr Elf_Xword SHF_MERGE = 0x10; +constexpr Elf_Xword SHF_STRINGS = 0x20; +constexpr Elf_Xword SHF_INFO_LINK = 0x40; +constexpr Elf_Xword SHF_LINK_ORDER = 0x80; +constexpr Elf_Xword SHF_OS_NONCONFORMING = 0x100; +constexpr Elf_Xword SHF_GROUP = 0x200; +constexpr Elf_Xword SHF_TLS = 0x400; +constexpr Elf_Xword SHF_COMPRESSED = 0x800; +constexpr Elf_Xword SHF_GNU_RETAIN = 0x200000; +constexpr Elf_Xword SHF_GNU_MBIND = 0x01000000; +// flag used in Nintendo RPX/RPL to indicate section data is compressed +constexpr Elf_Xword SHF_RPX_DEFLATE = 0x08000000; +constexpr Elf_Xword SHF_MASKOS = 0x0FF00000; +constexpr Elf_Xword SHF_MIPS_GPREL = 0x10000000; +constexpr Elf_Xword SHF_ORDERED = 0x40000000; +constexpr Elf_Xword SHF_EXCLUDE = 0x80000000; +constexpr Elf_Xword SHF_MASKPROC = 0xF0000000; + +// Section group flags +constexpr Elf_Word GRP_COMDAT = 0x1; +constexpr Elf_Word GRP_MASKOS = 0x0ff00000; +constexpr Elf_Word GRP_MASKPROC = 0xf0000000; + +// Symbol binding +constexpr unsigned char STB_LOCAL = 0; +constexpr unsigned char STB_GLOBAL = 1; +constexpr unsigned char STB_WEAK = 2; +constexpr unsigned char STB_LOOS = 10; +constexpr unsigned char STB_HIOS = 12; +constexpr unsigned char STB_MULTIDEF = 13; +constexpr unsigned char STB_LOPROC = 13; +constexpr unsigned char STB_HIPROC = 15; + +// Values of note segment descriptor types for core files +constexpr Elf_Word NT_PRSTATUS = 1; // Contains copy of prstatus struct +constexpr Elf_Word NT_FPREGSET = 2; // Contains copy of fpregset struct +constexpr Elf_Word NT_PRPSINFO = 3; // Contains copy of prpsinfo struct +constexpr Elf_Word NT_TASKSTRUCT = 4; // Contains copy of task struct +constexpr Elf_Word NT_AUXV = 6; // Contains copy of Elfxx_auxv_t +constexpr Elf_Word NT_SIGINFO = 0x53494749; // Fields of siginfo_t. +constexpr Elf_Word NT_FILE = 0x46494c45; // Description of mapped files. + +// Note segments for core files on dir-style procfs systems. +constexpr Elf_Word NT_PSTATUS = 10; // Has a struct pstatus +constexpr Elf_Word NT_FPREGS = 12; // Has a struct fpregset +constexpr Elf_Word NT_PSINFO = 13; // Has a struct psinfo +constexpr Elf_Word NT_LWPSTATUS = 16; // Has a struct lwpstatus_t +constexpr Elf_Word NT_LWPSINFO = 17; // Has a struct lwpsinfo_t +constexpr Elf_Word NT_WIN32PSTATUS = 18; // Has a struct win32_pstatus + +// clang-format off + +// Note name must be "LINUX" +constexpr Elf_Word NT_PRXFPREG = 0x46e62b7f; // Contains a user_xfpregs_struct +constexpr Elf_Word NT_PPC_VMX = 0x100; // PowerPC Altivec/VMX registers +constexpr Elf_Word NT_PPC_VSX = 0x102; // PowerPC VSX registers +constexpr Elf_Word NT_PPC_TAR = 0x103; // PowerPC Target Address Register +constexpr Elf_Word NT_PPC_PPR = 0x104; // PowerPC Program Priority Register +constexpr Elf_Word NT_PPC_DSCR = 0x105; // PowerPC Data Stream Control Register +constexpr Elf_Word NT_PPC_EBB = 0x106; // PowerPC Event Based Branch Registers +constexpr Elf_Word NT_PPC_PMU = 0x107; // PowerPC Performance Monitor Registers +constexpr Elf_Word NT_PPC_TM_CGPR = 0x108; // PowerPC TM checkpointed GPR Registers +constexpr Elf_Word NT_PPC_TM_CFPR = 0x109; // PowerPC TM checkpointed FPR Registers +constexpr Elf_Word NT_PPC_TM_CVMX = 0x10a; // PowerPC TM checkpointed VMX Registers +constexpr Elf_Word NT_PPC_TM_CVSX = 0x10b; // PowerPC TM checkpointed VSX Registers +constexpr Elf_Word NT_PPC_TM_SPR = 0x10c; // PowerPC TM Special Purpose Registers +constexpr Elf_Word NT_PPC_TM_CTAR = 0x10d; // PowerPC TM checkpointed TAR +constexpr Elf_Word NT_PPC_TM_CPPR = 0x10e; // PowerPC TM checkpointed PPR +constexpr Elf_Word NT_PPC_TM_CDSCR = 0x10f; // PowerPC TM checkpointed Data SCR +constexpr Elf_Word NT_386_TLS = 0x200; // x86 TLS information +constexpr Elf_Word NT_386_IOPERM = 0x201; // x86 io permissions +constexpr Elf_Word NT_X86_XSTATE = 0x202; // x86 XSAVE extended state +constexpr Elf_Word NT_X86_CET = 0x203; // x86 CET state. +constexpr Elf_Word NT_S390_HIGH_GPRS = 0x300; // S/390 upper halves of GPRs +constexpr Elf_Word NT_S390_TIMER = 0x301; // S390 timer +constexpr Elf_Word NT_S390_TODCMP = 0x302; // S390 TOD clock comparator +constexpr Elf_Word NT_S390_TODPREG = 0x303; // S390 TOD programmable register +constexpr Elf_Word NT_S390_CTRS = 0x304; // S390 control registers +constexpr Elf_Word NT_S390_PREFIX = 0x305; // S390 prefix register +constexpr Elf_Word NT_S390_LAST_BREAK = 0x306; // S390 breaking event address +constexpr Elf_Word NT_S390_SYSTEM_CALL = 0x307; // S390 system call restart data +constexpr Elf_Word NT_S390_TDB = 0x308; // S390 transaction diagnostic block +constexpr Elf_Word NT_S390_VXRS_LOW = 0x309; // S390 vector registers 0-15 upper half +constexpr Elf_Word NT_S390_VXRS_HIGH = 0x30a; // S390 vector registers 16-31 +constexpr Elf_Word NT_S390_GS_CB = 0x30b; // s390 guarded storage registers +constexpr Elf_Word NT_S390_GS_BC = 0x30c; // s390 guarded storage broadcast control block +constexpr Elf_Word NT_ARM_VFP = 0x400; // ARM VFP registers +constexpr Elf_Word NT_ARM_TLS = 0x401; // AArch TLS registers +constexpr Elf_Word NT_ARM_HW_BREAK = 0x402; // AArch hardware breakpoint registers +constexpr Elf_Word NT_ARM_HW_WATCH = 0x403; // AArch hardware watchpoint registers +constexpr Elf_Word NT_ARM_SVE = 0x405; // AArch SVE registers. +constexpr Elf_Word NT_ARM_PAC_MASK = 0x406; // AArch pointer authentication code masks +constexpr Elf_Word NT_ARM_PACA_KEYS = 0x407; // ARM pointer authentication address keys +constexpr Elf_Word NT_ARM_PACG_KEYS = 0x408; // ARM pointer authentication generic keys +constexpr Elf_Word NT_ARM_TAGGED_ADDR_CTRL = 0x409; // AArch64 tagged address control (prctl()) +constexpr Elf_Word NT_ARM_PAC_ENABLED_KEYS = 0x40a; // AArch64 pointer authentication enabled keys (prctl()) +constexpr Elf_Word NT_ARC_V2 = 0x600; // ARC HS accumulator/extra registers. +constexpr Elf_Word NT_LARCH_CPUCFG = 0xa00; // LoongArch CPU config registers +constexpr Elf_Word NT_LARCH_CSR = 0xa01; // LoongArch Control State Registers +constexpr Elf_Word NT_LARCH_LSX = 0xa02; // LoongArch SIMD eXtension registers +constexpr Elf_Word NT_LARCH_LASX = 0xa03; // LoongArch Advanced SIMD eXtension registers +constexpr Elf_Word NT_RISCV_CSR = 0x900; // RISC-V Control and Status Registers + +// Note name must be "CORE" +constexpr Elf_Word NT_LARCH_LBT = 0xa04; // LoongArch Binary Translation registers + +/* The range 0xff000000 to 0xffffffff is set aside for notes that don't + originate from any particular operating system. */ +constexpr Elf_Word NT_GDB_TDESC = 0xff000000; // Contains copy of GDB's target description XML. +constexpr Elf_Word NT_MEMTAG = 0xff000001; // Contains a copy of the memory tags. +/* ARM-specific NT_MEMTAG types. */ +constexpr Elf_Word NT_MEMTAG_TYPE_AARCH_MTE = 0x400; // MTE memory tags for AArch64. + +constexpr Elf_Word NT_STAPSDT = 3; // Note segment for SystemTap probes. + +// Note name is "FreeBSD" +constexpr Elf_Word NT_FREEBSD_THRMISC = 7; // Thread miscellaneous info. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_PROC = 8; // Procstat proc data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_FILES = 9; // Procstat files data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_VMMAP = 10; // Procstat vmmap data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_GROUPS = 11; // Procstat groups data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_UMASK = 12; // Procstat umask data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_RLIMIT = 13; // Procstat rlimit data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_OSREL = 14; // Procstat osreldate data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_PSSTRINGS = 15; // Procstat ps_strings data. +constexpr Elf_Word NT_FREEBSD_PROCSTAT_AUXV = 16; // Procstat auxv data. +constexpr Elf_Word NT_FREEBSD_PTLWPINFO = 17; // Thread ptrace miscellaneous info. + +// Note name must start with "NetBSD-CORE" +constexpr Elf_Word NT_NETBSDCORE_PROCINFO = 1; // Has a struct procinfo +constexpr Elf_Word NT_NETBSDCORE_AUXV = 2; // Has auxv data +constexpr Elf_Word NT_NETBSDCORE_LWPSTATUS = 24; // Has LWPSTATUS data +constexpr Elf_Word NT_NETBSDCORE_FIRSTMACH = 32; // start of machdep note types + +// Note name is "OpenBSD" +constexpr Elf_Word NT_OPENBSD_PROCINFO = 10; +constexpr Elf_Word NT_OPENBSD_AUXV = 11; +constexpr Elf_Word NT_OPENBSD_REGS = 20; +constexpr Elf_Word NT_OPENBSD_FPREGS = 21; +constexpr Elf_Word NT_OPENBSD_XFPREGS = 22; +constexpr Elf_Word NT_OPENBSD_WCOOKIE = 23; + +// Note name must start with "SPU" +constexpr Elf_Word NT_SPU = 1; + +// Values of note segment descriptor types for object files +constexpr Elf_Word NT_VERSION = 1; // Contains a version string. +constexpr Elf_Word NT_ARCH = 2; // Contains an architecture string. +constexpr Elf_Word NT_GO_BUILDID = 4; // Contains GO buildid data. + +// Values for notes in non-core files using name "GNU" +constexpr Elf_Word NT_GNU_ABI_TAG = 1; +constexpr Elf_Word NT_GNU_HWCAP = 2; // Used by ld.so and kernel vDSO. +constexpr Elf_Word NT_GNU_BUILD_ID = 3; // Generated by ld --build-id. +constexpr Elf_Word NT_GNU_GOLD_VERSION = 4; // Generated by gold. +constexpr Elf_Word NT_GNU_PROPERTY_TYPE_0 = 5; // Generated by gcc. +// clang-format on + +constexpr Elf_Word NT_GNU_BUILD_ATTRIBUTE_OPEN = 0x100; +constexpr Elf_Word NT_GNU_BUILD_ATTRIBUTE_FUNC = 0x101; + +// Symbol types +constexpr Elf_Word STT_NOTYPE = 0; +constexpr Elf_Word STT_OBJECT = 1; +constexpr Elf_Word STT_FUNC = 2; +constexpr Elf_Word STT_SECTION = 3; +constexpr Elf_Word STT_FILE = 4; +constexpr Elf_Word STT_COMMON = 5; +constexpr Elf_Word STT_TLS = 6; +constexpr Elf_Word STT_LOOS = 10; +constexpr Elf_Word STT_AMDGPU_HSA_KERNEL = 10; +constexpr Elf_Word STT_HIOS = 12; +constexpr Elf_Word STT_LOPROC = 13; +constexpr Elf_Word STT_HIPROC = 15; + +// Symbol visibility +constexpr unsigned char STV_DEFAULT = 0; +constexpr unsigned char STV_INTERNAL = 1; +constexpr unsigned char STV_HIDDEN = 2; +constexpr unsigned char STV_PROTECTED = 3; + +// Undefined name +constexpr Elf_Word STN_UNDEF = 0; + +// Relocation types +// X86 +constexpr unsigned R_386_NONE = 0; +constexpr unsigned R_X86_64_NONE = 0; +constexpr unsigned R_AMDGPU_NONE = 0; +constexpr unsigned R_386_32 = 1; +constexpr unsigned R_X86_64_64 = 1; +constexpr unsigned R_AMDGPU_ABS32_LO = 1; +constexpr unsigned R_386_PC32 = 2; +constexpr unsigned R_X86_64_PC32 = 2; +constexpr unsigned R_AMDGPU_ABS32_HI = 2; +constexpr unsigned R_386_GOT32 = 3; +constexpr unsigned R_X86_64_GOT32 = 3; +constexpr unsigned R_AMDGPU_ABS64 = 3; +constexpr unsigned R_386_PLT32 = 4; +constexpr unsigned R_X86_64_PLT32 = 4; +constexpr unsigned R_AMDGPU_REL32 = 4; +constexpr unsigned R_386_COPY = 5; +constexpr unsigned R_X86_64_COPY = 5; +constexpr unsigned R_AMDGPU_REL64 = 5; +constexpr unsigned R_386_GLOB_DAT = 6; +constexpr unsigned R_X86_64_GLOB_DAT = 6; +constexpr unsigned R_AMDGPU_ABS32 = 6; +constexpr unsigned R_386_JMP_SLOT = 7; +constexpr unsigned R_X86_64_JUMP_SLOT = 7; +constexpr unsigned R_AMDGPU_GOTPCREL = 7; +constexpr unsigned R_386_RELATIVE = 8; +constexpr unsigned R_X86_64_RELATIVE = 8; +constexpr unsigned R_AMDGPU_GOTPCREL32_LO = 8; +constexpr unsigned R_386_GOTOFF = 9; +constexpr unsigned R_X86_64_GOTPCREL = 9; +constexpr unsigned R_AMDGPU_GOTPCREL32_HI = 9; +constexpr unsigned R_386_GOTPC = 10; +constexpr unsigned R_X86_64_32 = 10; +constexpr unsigned R_AMDGPU_REL32_LO = 10; +constexpr unsigned R_386_32PLT = 11; +constexpr unsigned R_X86_64_32S = 11; +constexpr unsigned R_AMDGPU_REL32_HI = 11; +constexpr unsigned R_X86_64_16 = 12; +constexpr unsigned R_X86_64_PC16 = 13; +constexpr unsigned R_AMDGPU_RELATIVE64 = 13; +constexpr unsigned R_386_TLS_TPOFF = 14; +constexpr unsigned R_X86_64_8 = 14; +constexpr unsigned R_386_TLS_IE = 15; +constexpr unsigned R_X86_64_PC8 = 15; +constexpr unsigned R_386_TLS_GOTIE = 16; +constexpr unsigned R_X86_64_DTPMOD64 = 16; +constexpr unsigned R_386_TLS_LE = 17; +constexpr unsigned R_X86_64_DTPOFF64 = 17; +constexpr unsigned R_386_TLS_GD = 18; +constexpr unsigned R_X86_64_TPOFF64 = 18; +constexpr unsigned R_386_TLS_LDM = 19; +constexpr unsigned R_X86_64_TLSGD = 19; +constexpr unsigned R_386_16 = 20; +constexpr unsigned R_X86_64_TLSLD = 20; +constexpr unsigned R_386_PC16 = 21; +constexpr unsigned R_X86_64_DTPOFF32 = 21; +constexpr unsigned R_386_8 = 22; +constexpr unsigned R_X86_64_GOTTPOFF = 22; +constexpr unsigned R_386_PC8 = 23; +constexpr unsigned R_X86_64_TPOFF32 = 23; +constexpr unsigned R_386_TLS_GD_32 = 24; +constexpr unsigned R_X86_64_PC64 = 24; +constexpr unsigned R_386_TLS_GD_PUSH = 25; +constexpr unsigned R_X86_64_GOTOFF64 = 25; +constexpr unsigned R_386_TLS_GD_CALL = 26; +constexpr unsigned R_X86_64_GOTPC32 = 26; +constexpr unsigned R_386_TLS_GD_POP = 27; +constexpr unsigned R_X86_64_GOT64 = 27; +constexpr unsigned R_386_TLS_LDM_32 = 28; +constexpr unsigned R_X86_64_GOTPCREL64 = 28; +constexpr unsigned R_386_TLS_LDM_PUSH = 29; +constexpr unsigned R_X86_64_GOTPC64 = 29; +constexpr unsigned R_386_TLS_LDM_CALL = 30; +constexpr unsigned R_X86_64_GOTPLT64 = 30; +constexpr unsigned R_386_TLS_LDM_POP = 31; +constexpr unsigned R_X86_64_PLTOFF64 = 31; +constexpr unsigned R_386_TLS_LDO_32 = 32; +constexpr unsigned R_386_TLS_IE_32 = 33; +constexpr unsigned R_386_TLS_LE_32 = 34; +constexpr unsigned R_X86_64_GOTPC32_TLSDESC = 34; +constexpr unsigned R_386_TLS_DTPMOD32 = 35; +constexpr unsigned R_X86_64_TLSDESC_CALL = 35; +constexpr unsigned R_386_TLS_DTPOFF32 = 36; +constexpr unsigned R_X86_64_TLSDESC = 36; +constexpr unsigned R_386_TLS_TPOFF32 = 37; +constexpr unsigned R_X86_64_IRELATIVE = 37; +constexpr unsigned R_386_SIZE32 = 38; +constexpr unsigned R_386_TLS_GOTDESC = 39; +constexpr unsigned R_386_TLS_DESC_CALL = 40; +constexpr unsigned R_386_TLS_DESC = 41; +constexpr unsigned R_386_IRELATIVE = 42; +constexpr unsigned R_386_GOT32X = 43; +constexpr unsigned R_X86_64_GNU_VTINHERIT = 250; +constexpr unsigned R_X86_64_GNU_VTENTRY = 251; +// Arm +constexpr unsigned R_ARM_NONE = 0; +constexpr unsigned R_ARM_PC24 = 1; +constexpr unsigned R_ARM_ABS32 = 2; +constexpr unsigned R_ARM_REL32 = 3; +constexpr unsigned R_ARM_CALL = 28; +constexpr unsigned R_ARM_JUMP24 = 29; +constexpr unsigned R_ARM_TARGET1 = 38; +constexpr unsigned R_ARM_V4BX = 40; +constexpr unsigned R_ARM_PREL31 = 42; +constexpr unsigned R_ARM_MOVW_ABS_NC = 43; +constexpr unsigned R_ARM_MOVT_ABS = 44; +constexpr unsigned R_ARM_MOVW_PREL_NC = 45; +constexpr unsigned R_ARM_MOVT_PREL = 46; +constexpr unsigned R_ARM_ALU_PC_G0_NC = 57; +constexpr unsigned R_ARM_ALU_PC_G1_NC = 59; +constexpr unsigned R_ARM_LDR_PC_G2 = 63; +// Arm thumb +constexpr unsigned R_ARM_THM_CALL = 10; +constexpr unsigned R_ARM_THM_JUMP24 = 30; +constexpr unsigned R_ARM_THM_MOVW_ABS_NC = 47; +constexpr unsigned R_ARM_THM_MOVT_ABS = 48; +constexpr unsigned R_ARM_THM_MOVW_PREL_NC = 49; +constexpr unsigned R_ARM_THM_MOVT_PREL = 50; +// AArch64 +constexpr unsigned R_AARCH64_NONE = 0; +constexpr unsigned R_AARCH64_P32_ABS32 = 1; +constexpr unsigned R_AARCH64_P32_COPY = 180; +constexpr unsigned R_AARCH64_P32_GLOB_DAT = 181; +constexpr unsigned R_AARCH64_P32_JUMP_SLOT = 182; +constexpr unsigned R_AARCH64_P32_RELATIVE = 183; +constexpr unsigned R_AARCH64_P32_TLS_DTPMOD = 184; +constexpr unsigned R_AARCH64_P32_TLS_DTPREL = 185; +constexpr unsigned R_AARCH64_P32_TLS_TPREL = 186; +constexpr unsigned R_AARCH64_P32_TLSDESC = 187; +constexpr unsigned R_AARCH64_P32_IRELATIVE = 188; +constexpr unsigned R_AARCH64_ABS64 = 257; +constexpr unsigned R_AARCH64_ABS32 = 258; +constexpr unsigned R_AARCH64_ABS16 = 259; +constexpr unsigned R_AARCH64_PREL64 = 260; +constexpr unsigned R_AARCH64_PREL32 = 261; +constexpr unsigned R_AARCH64_PREL16 = 262; +constexpr unsigned R_AARCH64_MOVW_UABS_G0 = 263; +constexpr unsigned R_AARCH64_MOVW_UABS_G0_NC = 264; +constexpr unsigned R_AARCH64_MOVW_UABS_G1 = 265; +constexpr unsigned R_AARCH64_MOVW_UABS_G1_NC = 266; +constexpr unsigned R_AARCH64_MOVW_UABS_G2 = 267; +constexpr unsigned R_AARCH64_MOVW_UABS_G2_NC = 268; +constexpr unsigned R_AARCH64_MOVW_UABS_G3 = 269; +constexpr unsigned R_AARCH64_MOVW_SABS_G0 = 270; +constexpr unsigned R_AARCH64_MOVW_SABS_G1 = 271; +constexpr unsigned R_AARCH64_MOVW_SABS_G2 = 272; +constexpr unsigned R_AARCH64_LD_PREL_LO19 = 273; +constexpr unsigned R_AARCH64_ADR_PREL_LO21 = 274; +constexpr unsigned R_AARCH64_ADR_PREL_PG_HI21 = 275; +constexpr unsigned R_AARCH64_ADR_PREL_PG_HI21_NC = 276; +constexpr unsigned R_AARCH64_ADD_ABS_LO12_NC = 277; +constexpr unsigned R_AARCH64_LDST8_ABS_LO12_NC = 278; +constexpr unsigned R_AARCH64_TSTBR14 = 279; +constexpr unsigned R_AARCH64_CONDBR19 = 280; +constexpr unsigned R_AARCH64_JUMP26 = 282; +constexpr unsigned R_AARCH64_CALL26 = 283; +constexpr unsigned R_AARCH64_LDST16_ABS_LO12_NC = 284; +constexpr unsigned R_AARCH64_LDST32_ABS_LO12_NC = 285; +constexpr unsigned R_AARCH64_LDST64_ABS_LO12_NC = 286; +constexpr unsigned R_AARCH64_MOVW_PREL_G0 = 287; +constexpr unsigned R_AARCH64_MOVW_PREL_G0_NC = 288; +constexpr unsigned R_AARCH64_MOVW_PREL_G1 = 289; +constexpr unsigned R_AARCH64_MOVW_PREL_G1_NC = 290; +constexpr unsigned R_AARCH64_MOVW_PREL_G2 = 291; +constexpr unsigned R_AARCH64_MOVW_PREL_G2_NC = 292; +constexpr unsigned R_AARCH64_MOVW_PREL_G3 = 293; +constexpr unsigned R_AARCH64_LDST128_ABS_LO12_NC = 299; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G0 = 300; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G0_NC = 301; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G1 = 302; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G1_NC = 303; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G2 = 304; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G2_NC = 305; +constexpr unsigned R_AARCH64_MOVW_GOTOFF_G3 = 306; +constexpr unsigned R_AARCH64_GOTREL64 = 307; +constexpr unsigned R_AARCH64_GOTREL32 = 308; +constexpr unsigned R_AARCH64_GOT_LD_PREL19 = 309; +constexpr unsigned R_AARCH64_LD64_GOTOFF_LO15 = 310; +constexpr unsigned R_AARCH64_ADR_GOT_PAGE = 311; +constexpr unsigned R_AARCH64_LD64_GOT_LO12_NC = 312; +constexpr unsigned R_AARCH64_LD64_GOTPAGE_LO15 = 313; +constexpr unsigned R_AARCH64_TLSGD_ADR_PREL21 = 512; +constexpr unsigned R_AARCH64_TLSGD_ADR_PAGE21 = 513; +constexpr unsigned R_AARCH64_TLSGD_ADD_LO12_NC = 514; +constexpr unsigned R_AARCH64_TLSGD_MOVW_G1 = 515; +constexpr unsigned R_AARCH64_TLSGD_MOVW_G0_NC = 516; +constexpr unsigned R_AARCH64_TLSLD_ADR_PREL21 = 517; +constexpr unsigned R_AARCH64_TLSLD_ADR_PAGE21 = 518; +constexpr unsigned R_AARCH64_TLSLD_ADD_LO12_NC = 519; +constexpr unsigned R_AARCH64_TLSLD_MOVW_G1 = 520; +constexpr unsigned R_AARCH64_TLSLD_MOVW_G0_NC = 521; +constexpr unsigned R_AARCH64_TLSLD_LD_PREL19 = 522; +constexpr unsigned R_AARCH64_TLSLD_MOVW_DTPREL_G2 = 523; +constexpr unsigned R_AARCH64_TLSLD_MOVW_DTPREL_G1 = 524; +constexpr unsigned R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC = 525; +constexpr unsigned R_AARCH64_TLSLD_MOVW_DTPREL_G0 = 526; +constexpr unsigned R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC = 527; +constexpr unsigned R_AARCH64_TLSLD_ADD_DTPREL_HI12 = 528; +constexpr unsigned R_AARCH64_TLSLD_ADD_DTPREL_LO12 = 529; +constexpr unsigned R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC = 530; +constexpr unsigned R_AARCH64_TLSLD_LDST8_DTPREL_LO12 = 531; +constexpr unsigned R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC = 532; +constexpr unsigned R_AARCH64_TLSLD_LDST16_DTPREL_LO12 = 533; +constexpr unsigned R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC = 534; +constexpr unsigned R_AARCH64_TLSLD_LDST32_DTPREL_LO12 = 535; +constexpr unsigned R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC = 536; +constexpr unsigned R_AARCH64_TLSLD_LDST64_DTPREL_LO12 = 537; +constexpr unsigned R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC = 538; +constexpr unsigned R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 = 539; +constexpr unsigned R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC = 540; +constexpr unsigned R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 = 541; +constexpr unsigned R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC = 542; +constexpr unsigned R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 = 543; +constexpr unsigned R_AARCH64_TLSLE_MOVW_TPREL_G2 = 544; +constexpr unsigned R_AARCH64_TLSLE_MOVW_TPREL_G1 = 545; +constexpr unsigned R_AARCH64_TLSLE_MOVW_TPREL_G1_NC = 546; +constexpr unsigned R_AARCH64_TLSLE_MOVW_TPREL_G0 = 547; +constexpr unsigned R_AARCH64_TLSLE_MOVW_TPREL_G0_NC = 548; +constexpr unsigned R_AARCH64_TLSLE_ADD_TPREL_HI12 = 549; +constexpr unsigned R_AARCH64_TLSLE_ADD_TPREL_LO12 = 550; +constexpr unsigned R_AARCH64_TLSLE_ADD_TPREL_LO12_NC = 551; +constexpr unsigned R_AARCH64_TLSLE_LDST8_TPREL_LO12 = 552; +constexpr unsigned R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC = 553; +constexpr unsigned R_AARCH64_TLSLE_LDST16_TPREL_LO12 = 554; +constexpr unsigned R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC = 555; +constexpr unsigned R_AARCH64_TLSLE_LDST32_TPREL_LO12 = 556; +constexpr unsigned R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC = 557; +constexpr unsigned R_AARCH64_TLSLE_LDST64_TPREL_LO12 = 558; +constexpr unsigned R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC = 559; +constexpr unsigned R_AARCH64_TLSDESC_LD_PREL19 = 560; +constexpr unsigned R_AARCH64_TLSDESC_ADR_PREL21 = 561; +constexpr unsigned R_AARCH64_TLSDESC_ADR_PAGE21 = 562; +constexpr unsigned R_AARCH64_TLSDESC_LD64_LO12 = 563; +constexpr unsigned R_AARCH64_TLSDESC_ADD_LO12 = 564; +constexpr unsigned R_AARCH64_TLSDESC_OFF_G1 = 565; +constexpr unsigned R_AARCH64_TLSDESC_OFF_G0_NC = 566; +constexpr unsigned R_AARCH64_TLSDESC_LDR = 567; +constexpr unsigned R_AARCH64_TLSDESC_ADD = 568; +constexpr unsigned R_AARCH64_TLSDESC_CALL = 569; +constexpr unsigned R_AARCH64_TLSLE_LDST128_TPREL_LO12 = 570; +constexpr unsigned R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC = 571; +constexpr unsigned R_AARCH64_TLSLD_LDST128_DTPREL_LO12 = 572; +constexpr unsigned R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC = 573; +constexpr unsigned R_AARCH64_COPY = 1024; +constexpr unsigned R_AARCH64_GLOB_DAT = 1025; +constexpr unsigned R_AARCH64_JUMP_SLOT = 1026; +constexpr unsigned R_AARCH64_RELATIVE = 1027; +constexpr unsigned R_AARCH64_TLS_DTPMOD = 1028; +constexpr unsigned R_AARCH64_TLS_DTPMOD64 = 1028; +constexpr unsigned R_AARCH64_TLS_DTPREL = 1029; +constexpr unsigned R_AARCH64_TLS_DTPREL64 = 1029; +constexpr unsigned R_AARCH64_TLS_TPREL = 1030; +constexpr unsigned R_AARCH64_TLS_TPREL64 = 1030; +constexpr unsigned R_AARCH64_TLSDESC = 1031; +// RISC-V +constexpr unsigned R_RISCV_NONE = 0; +constexpr unsigned R_RISCV_32 = 1; +constexpr unsigned R_RISCV_64 = 2; +constexpr unsigned R_RISCV_RELATIVE = 3; +constexpr unsigned R_RISCV_COPY = 4; +constexpr unsigned R_RISCV_JUMP_SLOT = 5; +constexpr unsigned R_RISCV_TLS_DTPMOD32 = 6; +constexpr unsigned R_RISCV_TLS_DTPMOD64 = 7; +constexpr unsigned R_RISCV_TLS_DTPREL32 = 8; +constexpr unsigned R_RISCV_TLS_DTPREL64 = 9; +constexpr unsigned R_RISCV_TLS_TPREL32 = 10; +constexpr unsigned R_RISCV_TLS_TPREL64 = 11; +constexpr unsigned R_RISCV_BRANCH = 16; +constexpr unsigned R_RISCV_JAL = 17; +constexpr unsigned R_RISCV_CALL = 18; +constexpr unsigned R_RISCV_CALL_PLT = 19; +constexpr unsigned R_RISCV_GOT_HI20 = 20; +constexpr unsigned R_RISCV_TLS_GOT_HI20 = 21; +constexpr unsigned R_RISCV_TLS_GD_HI20 = 22; +constexpr unsigned R_RISCV_PCREL_HI20 = 23; +constexpr unsigned R_RISCV_PCREL_LO12_I = 24; +constexpr unsigned R_RISCV_PCREL_LO12_S = 25; +constexpr unsigned R_RISCV_HI20 = 26; +constexpr unsigned R_RISCV_LO12_I = 27; +constexpr unsigned R_RISCV_LO12_S = 28; +constexpr unsigned R_RISCV_TPREL_HI20 = 29; +constexpr unsigned R_RISCV_TPREL_LO12_I = 30; +constexpr unsigned R_RISCV_TPREL_LO12_S = 31; +constexpr unsigned R_RISCV_TPREL_ADD = 32; +constexpr unsigned R_RISCV_ADD8 = 33; +constexpr unsigned R_RISCV_ADD16 = 34; +constexpr unsigned R_RISCV_ADD32 = 35; +constexpr unsigned R_RISCV_ADD64 = 36; +constexpr unsigned R_RISCV_SUB8 = 37; +constexpr unsigned R_RISCV_SUB16 = 38; +constexpr unsigned R_RISCV_SUB32 = 39; +constexpr unsigned R_RISCV_SUB64 = 40; +constexpr unsigned R_RISCV_GNU_VTINHERIT = 41; +constexpr unsigned R_RISCV_GNU_VTENTRY = 42; +constexpr unsigned R_RISCV_ALIGN = 43; +constexpr unsigned R_RISCV_RVC_BRANCH = 44; +constexpr unsigned R_RISCV_RVC_JUMP = 45; +constexpr unsigned R_RISCV_RVC_LUI = 46; +constexpr unsigned R_RISCV_RELAX = 51; +constexpr unsigned R_RISCV_SUB6 = 52; +constexpr unsigned R_RISCV_SET6 = 53; +constexpr unsigned R_RISCV_SET8 = 54; +constexpr unsigned R_RISCV_SET16 = 55; +constexpr unsigned R_RISCV_SET32 = 56; +constexpr unsigned R_RISCV_32_PCREL = 57; +constexpr unsigned R_RISCV_IRELATIVE = 58; + +// Segment types +constexpr Elf_Word PT_NULL = 0; +constexpr Elf_Word PT_LOAD = 1; +constexpr Elf_Word PT_DYNAMIC = 2; +constexpr Elf_Word PT_INTERP = 3; +constexpr Elf_Word PT_NOTE = 4; +constexpr Elf_Word PT_SHLIB = 5; +constexpr Elf_Word PT_PHDR = 6; +constexpr Elf_Word PT_TLS = 7; +constexpr Elf_Word PT_LOOS = 0X60000000; +constexpr Elf_Word PT_GNU_EH_FRAME = 0X6474E550; // Frame unwind information +constexpr Elf_Word PT_GNU_STACK = 0X6474E551; // Stack flags +constexpr Elf_Word PT_GNU_RELRO = 0X6474E552; // Read only after relocs +constexpr Elf_Word PT_GNU_PROPERTY = 0X6474E553; // GNU property +constexpr Elf_Word PT_GNU_MBIND_LO = 0X6474E555; // Mbind segments start +constexpr Elf_Word PT_GNU_MBIND_HI = 0X6474F554; // Mbind segments finish +constexpr Elf_Word PT_PAX_FLAGS = 0X65041580; +constexpr Elf_Word PT_OPENBSD_RANDOMIZE = 0X65A3DBE6; +constexpr Elf_Word PT_OPENBSD_WXNEEDED = 0X65A3DBE7; +constexpr Elf_Word PT_OPENBSD_BOOTDATA = 0X65A41BE6; +constexpr Elf_Word PT_SUNWBSS = 0X6FFFFFFA; +constexpr Elf_Word PT_SUNWSTACK = 0X6FFFFFFB; +constexpr Elf_Word PT_HIOS = 0X6FFFFFFF; +constexpr Elf_Word PT_LOPROC = 0X70000000; +constexpr Elf_Word PT_HIPROC = 0X7FFFFFFF; + +// Segment flags +constexpr Elf_Word PF_X = 1; // Execute +constexpr Elf_Word PF_W = 2; // Write +constexpr Elf_Word PF_R = 4; // Read +constexpr Elf_Word PF_MASKOS = 0x0ff00000; // Unspecified +constexpr Elf_Word PF_MASKPROC = 0xf0000000; // Unspecified + +// Dynamic Array Tags +constexpr Elf_Word DT_NULL = 0; +constexpr Elf_Word DT_NEEDED = 1; +constexpr Elf_Word DT_PLTRELSZ = 2; +constexpr Elf_Word DT_PLTGOT = 3; +constexpr Elf_Word DT_HASH = 4; +constexpr Elf_Word DT_STRTAB = 5; +constexpr Elf_Word DT_SYMTAB = 6; +constexpr Elf_Word DT_RELA = 7; +constexpr Elf_Word DT_RELASZ = 8; +constexpr Elf_Word DT_RELAENT = 9; +constexpr Elf_Word DT_STRSZ = 10; +constexpr Elf_Word DT_SYMENT = 11; +constexpr Elf_Word DT_INIT = 12; +constexpr Elf_Word DT_FINI = 13; +constexpr Elf_Word DT_SONAME = 14; +constexpr Elf_Word DT_RPATH = 15; +constexpr Elf_Word DT_SYMBOLIC = 16; +constexpr Elf_Word DT_REL = 17; +constexpr Elf_Word DT_RELSZ = 18; +constexpr Elf_Word DT_RELENT = 19; +constexpr Elf_Word DT_PLTREL = 20; +constexpr Elf_Word DT_DEBUG = 21; +constexpr Elf_Word DT_TEXTREL = 22; +constexpr Elf_Word DT_JMPREL = 23; +constexpr Elf_Word DT_BIND_NOW = 24; +constexpr Elf_Word DT_INIT_ARRAY = 25; +constexpr Elf_Word DT_FINI_ARRAY = 26; +constexpr Elf_Word DT_INIT_ARRAYSZ = 27; +constexpr Elf_Word DT_FINI_ARRAYSZ = 28; +constexpr Elf_Word DT_RUNPATH = 29; +constexpr Elf_Word DT_FLAGS = 30; +constexpr Elf_Word DT_ENCODING = 32; +constexpr Elf_Word DT_PREINIT_ARRAY = 32; +constexpr Elf_Word DT_PREINIT_ARRAYSZ = 33; +constexpr Elf_Word DT_MAXPOSTAGS = 34; +constexpr Elf_Word DT_LOOS = 0x6000000D; +constexpr Elf_Word DT_HIOS = 0x6ffff000; +constexpr Elf_Word DT_GNU_HASH = 0x6ffffef5; +constexpr Elf_Word DT_TLSDESC_PLT = 0x6ffffef6; +constexpr Elf_Word DT_TLSDESC_GOT = 0x6ffffef7; +constexpr Elf_Word DT_GNU_CONFLICT = 0x6ffffef8; +constexpr Elf_Word DT_GNU_LIBLIST = 0x6ffffef9; +constexpr Elf_Word DT_CONFIG = 0x6ffffefa; +constexpr Elf_Word DT_DEPAUDIT = 0x6ffffefb; +constexpr Elf_Word DT_AUDIT = 0x6ffffefc; +constexpr Elf_Word DT_PLTPAD = 0x6ffffefd; +constexpr Elf_Word DT_MOVETAB = 0x6ffffefe; +constexpr Elf_Word DT_SYMINFO = 0x6ffffeff; +constexpr Elf_Word DT_ADDRRNGHI = 0x6ffffeff; +constexpr Elf_Word DT_VERSYM = 0x6ffffff0; +constexpr Elf_Word DT_RELACOUNT = 0x6ffffff9; +constexpr Elf_Word DT_RELCOUNT = 0x6ffffffa; +constexpr Elf_Word DT_FLAGS_1 = 0x6ffffffb; +constexpr Elf_Word DT_VERDEF = 0x6ffffffc; +constexpr Elf_Word DT_VERDEFNUM = 0x6ffffffd; +constexpr Elf_Word DT_VERNEED = 0x6ffffffe; +constexpr Elf_Word DT_VERNEEDNUM = 0x6fffffff; +constexpr Elf_Word DT_LOPROC = 0x70000000; +constexpr Elf_Word DT_HIPROC = 0x7FFFFFFF; + +// DT_FLAGS values +constexpr Elf_Word DF_ORIGIN = 0x1; +constexpr Elf_Word DF_SYMBOLIC = 0x2; +constexpr Elf_Word DF_TEXTREL = 0x4; +constexpr Elf_Word DF_BIND_NOW = 0x8; +constexpr Elf_Word DF_STATIC_TLS = 0x10; + +// Legal values for d_tag (dynamic entry type). +constexpr Elf_Word AT_NULL = 0; // End of vector +constexpr Elf_Word AT_IGNORE = 1; // Entry should be ignored +constexpr Elf_Word AT_EXECFD = 2; // File descriptor of program +constexpr Elf_Word AT_PHDR = 3; // Program headers for program +constexpr Elf_Word AT_PHENT = 4; // Size of program header entry +constexpr Elf_Word AT_PHNUM = 5; // Number of program headers +constexpr Elf_Word AT_PAGESZ = 6; // System page size +constexpr Elf_Word AT_BASE = 7; // Base address of interpreter +constexpr Elf_Word AT_FLAGS = 8; // Flags +constexpr Elf_Word AT_ENTRY = 9; // Entry point of program +constexpr Elf_Word AT_NOTELF = 10; // Program is not ELF +constexpr Elf_Word AT_UID = 11; // Real uid +constexpr Elf_Word AT_EUID = 12; // Effective uid +constexpr Elf_Word AT_GID = 13; // Real gid +constexpr Elf_Word AT_EGID = 14; // Effective gid +constexpr Elf_Word AT_CLKTCK = 17; // Frequency of times() +constexpr Elf_Word AT_PLATFORM = 15; // String identifying platform. +constexpr Elf_Word AT_HWCAP = 16; // Hints about processor capabilities. +constexpr Elf_Word AT_FPUCW = 18; // Used FPU control word. +constexpr Elf_Word AT_DCACHEBSIZE = 19; // Data cache block size. +constexpr Elf_Word AT_ICACHEBSIZE = 20; // Instruction cache block size. +constexpr Elf_Word AT_UCACHEBSIZE = 21; // Unified cache block size. +constexpr Elf_Word AT_IGNOREPPC = 22; // Entry should be ignored. +constexpr Elf_Word AT_SECURE = 23; // Boolean, was exec setuid-like? +constexpr Elf_Word AT_BASE_PLATFORM = 24; // String identifying real platforms. +constexpr Elf_Word AT_RANDOM = 25; // Address of 16 random bytes. +constexpr Elf_Word AT_HWCAP2 = 26; // More hints about processor capabilities. +constexpr Elf_Word AT_EXECFN = 31; // Filename of executable. +constexpr Elf_Word AT_SYSINFO = 32; // EP to the system call in the vDSO. +constexpr Elf_Word AT_SYSINFO_EHDR = 33; // Start of the ELF header of the vDSO. +constexpr Elf_Word AT_L1I_CACHESHAPE = 34; +constexpr Elf_Word AT_L1D_CACHESHAPE = 35; +constexpr Elf_Word AT_L2_CACHESHAPE = 36; +constexpr Elf_Word AT_L3_CACHESHAPE = 37; +constexpr Elf_Word AT_L1I_CACHESIZE = 40; +constexpr Elf_Word AT_L1I_CACHEGEOMETRY = 41; +constexpr Elf_Word AT_L1D_CACHESIZE = 42; +constexpr Elf_Word AT_L1D_CACHEGEOMETRY = 43; +constexpr Elf_Word AT_L2_CACHESIZE = 44; +constexpr Elf_Word AT_L2_CACHEGEOMETRY = 45; +constexpr Elf_Word AT_L3_CACHESIZE = 46; + +// ELF file header +struct Elf32_Ehdr +{ + // Identification bytes + unsigned char e_ident[EI_NIDENT]; + // Object file type + Elf_Half e_type; + // Architecture + Elf_Half e_machine; + // Object file version + Elf_Word e_version; + // Entry point virtual address + Elf32_Addr e_entry; + // Program header table file offset + Elf32_Off e_phoff; + // Section header table file offset + Elf32_Off e_shoff; + // Processor-specific flags + Elf_Word e_flags; + // ELF header size in bytes + Elf_Half e_ehsize; + // Program header table entry size + Elf_Half e_phentsize; + // Program header table entry count + Elf_Half e_phnum; + // Section header table entry size + Elf_Half e_shentsize; + // Section header table entry count + Elf_Half e_shnum; + // Section header string table index + Elf_Half e_shstrndx; +}; + +struct Elf64_Ehdr +{ + // Identification bytes + unsigned char e_ident[EI_NIDENT]; + // Object file type + Elf_Half e_type; + // Architecture + Elf_Half e_machine; + // Object file version + Elf_Word e_version; + // Entry point virtual address + Elf64_Addr e_entry; + // Program header table file offset + Elf64_Off e_phoff; + // Section header table file offset + Elf64_Off e_shoff; + // Processor-specific flags + Elf_Word e_flags; + // ELF header size in bytes + Elf_Half e_ehsize; + // Program header table entry size + Elf_Half e_phentsize; + // Program header table entry count + Elf_Half e_phnum; + // Section header table entry size + Elf_Half e_shentsize; + // Section header table entry count + Elf_Half e_shnum; + // Section header string table index + Elf_Half e_shstrndx; +}; + +// Section header +struct Elf32_Shdr +{ + // Section name (string table index) + Elf_Word sh_name; + // Section type + Elf_Word sh_type; + // Section flags + Elf_Word sh_flags; + // Section virtual address at execution + Elf32_Addr sh_addr; + // Section file offset + Elf32_Off sh_offset; + // Section size in bytes + Elf_Word sh_size; + // Link to another section + Elf_Word sh_link; + // Additional section information + Elf_Word sh_info; + // Section alignment + Elf_Word sh_addralign; + // Entry size if section holds table + Elf_Word sh_entsize; +}; + +struct Elf64_Shdr +{ + // Section name (string table index) + Elf_Word sh_name; + // Section type + Elf_Word sh_type; + // Section flags + Elf_Xword sh_flags; + // Section virtual address at execution + Elf64_Addr sh_addr; + // Section file offset + Elf64_Off sh_offset; + // Section size in bytes + Elf_Xword sh_size; + // Link to another section + Elf_Word sh_link; + // Additional section information + Elf_Word sh_info; + // Section alignment + Elf_Xword sh_addralign; + // Entry size if section holds table + Elf_Xword sh_entsize; +}; + +// Segment header +struct Elf32_Phdr +{ + // Segment type + Elf_Word p_type; + // Segment file offset + Elf32_Off p_offset; + // Segment virtual address at execution + Elf32_Addr p_vaddr; + // Segment physical address + Elf32_Addr p_paddr; + // Segment size in file + Elf_Word p_filesz; + // Segment size in memory + Elf_Word p_memsz; + // Segment flags + Elf_Word p_flags; + // Segment alignment + Elf_Word p_align; +}; + +struct Elf64_Phdr +{ + // Segment type + Elf_Word p_type; + // Segment flags + Elf_Word p_flags; + // Segment file offset + Elf64_Off p_offset; + // Segment virtual address at execution + Elf64_Addr p_vaddr; + // Segment physical address + Elf64_Addr p_paddr; + // Segment size in file + Elf_Xword p_filesz; + // Segment size in memory + Elf_Xword p_memsz; + // Segment alignment + Elf_Xword p_align; +}; + +// Symbol table entry +struct Elf32_Sym +{ + // Symbol name (string table index) + Elf_Word st_name; + // Symbol value + Elf32_Addr st_value; + // Symbol size + Elf_Word st_size; + // Symbol type and binding attributes + unsigned char st_info; + // Symbol visibility + unsigned char st_other; + // Section index + Elf_Half st_shndx; +}; + +struct Elf64_Sym +{ + // Symbol name (string table index) + Elf_Word st_name; + // Symbol type and binding attributes + unsigned char st_info; + // Symbol visibility + unsigned char st_other; + // Section index + Elf_Half st_shndx; + // Symbol value + Elf64_Addr st_value; + // Symbol size + Elf_Xword st_size; +}; + +#define ELF_ST_BIND( i ) ( ( i ) >> 4 ) +#define ELF_ST_TYPE( i ) ( ( i ) & 0xf ) +#define ELF_ST_INFO( b, t ) ( ( ( b ) << 4 ) + ( ( t ) & 0xf ) ) + +#define ELF_ST_VISIBILITY( o ) ( ( o ) & 0x3 ) + +// Relocation entries +struct Elf32_Rel +{ + // Location to apply the relocation action + Elf32_Addr r_offset; + // Relocation type and symbol index + Elf_Word r_info; +}; + +struct Elf32_Rela +{ + // Location to apply the relocation action + Elf32_Addr r_offset; + // Relocation type and symbol index + Elf_Word r_info; + // Constant addend used to compute the value + Elf_Sword r_addend; +}; + +struct Elf64_Rel +{ + // Location to apply the relocation action + Elf64_Addr r_offset; + // Relocation type and symbol index + Elf_Xword r_info; +}; + +struct Elf64_Rela +{ + // Location to apply the relocation action + Elf64_Addr r_offset; + // Relocation type and symbol index + Elf_Xword r_info; + // Constant addend used to compute the value + Elf_Sxword r_addend; +}; + +#define ELF32_R_SYM( i ) ( ( i ) >> 8 ) +#define ELF32_R_TYPE( i ) ( (unsigned char)( i ) ) +#define ELF32_R_INFO( s, t ) ( ( ( s ) << 8 ) + (unsigned char)( t ) ) + +#define ELF64_R_SYM( i ) ( ( i ) >> 32 ) +#define ELF64_R_TYPE( i ) ( ( i ) & 0xffffffffL ) +#define ELF64_R_INFO( s, t ) \ + ( ( ( ( std::int64_t )( s ) ) << 32 ) + ( ( t ) & 0xffffffffL ) ) + +// Dynamic structure +struct Elf32_Dyn +{ + // Dynamic entry type + Elf_Sword d_tag; + union { + // Integer value + Elf_Word d_val; + // Address value + Elf32_Addr d_ptr; + } d_un; +}; + +struct Elf64_Dyn +{ + // Dynamic entry type + Elf_Sxword d_tag; + union { + // Integer value + Elf_Xword d_val; + // Address value + Elf64_Addr d_ptr; + } d_un; +}; + +struct Elfxx_Verdef +{ + // Version revision + Elf_Half vd_version; + // Version information flags + Elf_Half vd_flags; + // Version index + Elf_Half vd_ndx; + // Number of associated aux entries + Elf_Half vd_cnt; + // Version name hash value + Elf_Word vd_hash; + // Offset to verdaux array + Elf_Word vd_aux; + // Offset to next verdef entry + Elf_Word vd_next; +}; + +struct Elfxx_Verdaux +{ + // Version or dependency name + Elf_Word vda_name; + // Offset to next verdaux entry + Elf_Word vda_next; +}; + +struct Elfxx_Verneed +{ + // Version of structure + Elf_Half vn_version; + // Number of associated aux entries + Elf_Half vn_cnt; + // Offset to file name string + Elf_Word vn_file; + // Offset to vernaux array + Elf_Word vn_aux; + // Offset to next verneed entry + Elf_Word vn_next; +}; + +struct Elfxx_Vernaux +{ + // Hash value of dependency name + Elf_Word vna_hash; + // Dependency information flags + Elf_Half vna_flags; + // Dependency index + Elf_Half vna_other; + // Dependency name string offset + Elf_Word vna_name; + // Offset to next vernaux entry + Elf_Word vna_next; +}; + +// ELF auxiliary vectors, they are usually run-time information +// being passed to program when the kernel is loading it. +// This is now required, +// because in order to initialize the stack cookie +// to protect against buffer overflows, +// most of libc ask us to have a valid pointer for the AT_RANDOM entry. +// glibc for example crashes if you don't. +// https://sourceware.org/git/?p=glibc.git;a=blob;f=csu/libc-start.c;h=543560f36c33b07a1fbe1b7e4578374fe8007b1f;hb=HEAD#l308 +// This is also useful to be able to reconstruct at run-time +// the ELF headers, if ELF headers were erased after loading. +// Although this library is targeted to be parsing files only, +// I assume auxiliary vectors could be also used to get +// more information about the ELF binary at run-time in future. +// The main purpose is also for ELF injectors. +struct Elf32_auxv +{ + // Entry type + std::uint32_t a_type; // Entry type + + union { + // Integer value, usually a pointer + std::uint32_t a_val; // Integer value, usually a pointer + } a_un; +}; + +struct Elf64_auxv +{ + // Entry type + std::uint64_t a_type; // Entry type + + union { + // Integer value, usually a pointer + std::uint64_t a_val; // Integer value, usually a pointer + } a_un; +}; + +struct Elf32_Chdr +{ + // The compression algorithm used + Elf32_Word ch_type; // The compression algorithm used + // The size, in bytes, of the uncompressed section data + Elf32_Word ch_size; //The size, in bytes, of the uncompressed section data + // The address alignment of the uncompressed section data + Elf32_Word + ch_addralign; // The address alignment of the uncompressed section data +}; + +struct Elf64_Chdr +{ + //The compression algorithm used + Elf64_Word ch_type; //The compression algorithm used + // Reserved + Elf64_Word ch_reserved; // Reserved + //The size, in bytes, of the uncompressed section data + Elf_Xword ch_size; //The size, in bytes, of the uncompressed section data + //The address alignment of the uncompressed section data + Elf_Xword + ch_addralign; //The address alignment of the uncompressed section data +}; + +#ifdef __cplusplus +} // namespace ELFIO +#endif + +#endif // ELFTYPES_H diff --git a/vyld/elfio/elfio_array.hpp b/vyld/elfio/elfio_array.hpp new file mode 100644 index 0000000..3e81adc --- /dev/null +++ b/vyld/elfio/elfio_array.hpp @@ -0,0 +1,119 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_ARRAY_HPP +#define ELFIO_ARRAY_HPP + +#include + +namespace ELFIO { + +//------------------------------------------------------------------------------ +// Template class for accessing array sections +template class array_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + // Constructor + explicit array_section_accessor_template( const elfio& elf_file, + S* section ); + + //------------------------------------------------------------------------------ + // Returns the number of entries in the array section + Elf_Xword get_entries_num() const; + + //------------------------------------------------------------------------------ + // Retrieves an entry from the array section + bool get_entry( Elf_Xword index, Elf64_Addr& address ) const; + + //------------------------------------------------------------------------------ + // Adds an entry to the array section + void add_entry( Elf64_Addr address ); + + private: + //------------------------------------------------------------------------------ + // Reference to the ELF file + const elfio& elf_file; + //------------------------------------------------------------------------------ + // Pointer to the array section + S* array_section; +}; + +//------------------------------------------------------------------------------ +// Constructor +template +array_section_accessor_template::array_section_accessor_template( + const elfio& elf_file, S* section ) + : elf_file( elf_file ), array_section( section ) +{ +} + +//------------------------------------------------------------------------------ +// Returns the number of entries in the array section +template +Elf_Xword array_section_accessor_template::get_entries_num() const +{ + Elf_Xword entry_size = sizeof( T ); + return array_section->get_size() / entry_size; +} + +//------------------------------------------------------------------------------ +// Retrieves an entry from the array section +template +bool array_section_accessor_template::get_entry( + Elf_Xword index, Elf64_Addr& address ) const +{ + if ( index >= get_entries_num() ) { // Is index valid + return false; + } + + const auto& convertor = elf_file.get_convertor(); + + const T temp = *reinterpret_cast( array_section->get_data() + + index * sizeof( T ) ); + address = ( *convertor )( temp ); + + return true; +} + +//------------------------------------------------------------------------------ +// Adds an entry to the array section +template +void array_section_accessor_template::add_entry( Elf64_Addr address ) +{ + const auto& convertor = elf_file.get_convertor(); + + T temp = ( *convertor )( (T)address ); + array_section->append_data( reinterpret_cast( &temp ), + sizeof( temp ) ); +} + +// Type aliases for array section accessors +template +using array_section_accessor = array_section_accessor_template; +template +using const_array_section_accessor = + array_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_ARRAY_HPP diff --git a/vyld/elfio/elfio_dump.hpp b/vyld/elfio/elfio_dump.hpp new file mode 100644 index 0000000..0eadda8 --- /dev/null +++ b/vyld/elfio/elfio_dump.hpp @@ -0,0 +1,1388 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_DUMP_HPP +#define ELFIO_DUMP_HPP + +#include +#include +#include +#include +#include +#include + +namespace ELFIO { + +static const struct class_table_t +{ + const char key; + const char* str; +} class_table[] = { + { ELFCLASS32, "ELF32" }, + { ELFCLASS64, "ELF64" }, +}; + +static const struct endian_table_t +{ + const char key; + const char* str; +} endian_table[] = { + { ELFDATANONE, "None" }, + { ELFDATA2LSB, "Little endian" }, + { ELFDATA2MSB, "Big endian" }, +}; + +static const struct os_abi_table_t +{ + const unsigned char key; + const char* str; +} os_abi_table[] = { + { ELFOSABI_NONE, "UNIX System V" }, + { ELFOSABI_HPUX, "Hewlett-Packard HP-UX" }, + { ELFOSABI_NETBSD, "NetBSD" }, + { ELFOSABI_LINUX, "Linux" }, + { ELFOSABI_HURD, "GNU Hurd" }, + { ELFOSABI_SOLARIS, "Sun Solaris" }, + { ELFOSABI_AIX, "AIX" }, + { ELFOSABI_IRIX, "IRIX" }, + { ELFOSABI_FREEBSD, "FreeBSD" }, + { ELFOSABI_TRU64, "Compaq TRU64 UNIX" }, + { ELFOSABI_MODESTO, "Novell Modesto" }, + { ELFOSABI_OPENBSD, "Open BSD" }, + { ELFOSABI_OPENVMS, "Open VMS" }, + { ELFOSABI_NSK, "Hewlett-Packard Non-Stop Kernel" }, + { ELFOSABI_AROS, "Amiga Research OS" }, + { ELFOSABI_FENIXOS, "FenixOS" }, + { ELFOSABI_NUXI, "Nuxi CloudABI" }, + { ELFOSABI_OPENVOS, "OpenVOS" }, + { ELFOSABI_ARM, "ARM" }, + { ELFOSABI_STANDALONE, "Standalone (embedded)" }, +}; + +static const struct version_table_t +{ + const Elf64_Word key; + const char* str; +} version_table[] = { + { EV_NONE, "None" }, + { EV_CURRENT, "Current" }, +}; + +static const struct type_table_t +{ + const Elf32_Half key; + const char* str; +} type_table[] = { + { ET_NONE, "No file type" }, { ET_REL, "Relocatable file" }, + { ET_EXEC, "Executable file" }, { ET_DYN, "Shared object file" }, + { ET_CORE, "Core file" }, +}; + +static const struct machine_table_t +{ + const Elf64_Half key; + const char* str; +} machine_table[] = { + { EM_NONE, "No machine" }, + { EM_M32, "AT&T WE 32100" }, + { EM_SPARC, "SUN SPARC" }, + { EM_386, "Intel 80386" }, + { EM_68K, "Motorola m68k family" }, + { EM_88K, "Motorola m88k family" }, + { EM_486, "Intel 80486// Reserved for future use" }, + { EM_860, "Intel 80860" }, + { EM_MIPS, "MIPS R3000 (officially, big-endian only)" }, + { EM_S370, "IBM System/370" }, + { EM_MIPS_RS3_LE, + "MIPS R3000 little-endian (Oct 4 1999 Draft) Deprecated" }, + { EM_res011, "Reserved" }, + { EM_res012, "Reserved" }, + { EM_res013, "Reserved" }, + { EM_res014, "Reserved" }, + { EM_PARISC, "HPPA" }, + { EM_res016, "Reserved" }, + { EM_VPP550, "Fujitsu VPP500" }, + { EM_SPARC32PLUS, "Sun's v8plus" }, + { EM_960, "Intel 80960" }, + { EM_PPC, "PowerPC" }, + { EM_PPC64, "64-bit PowerPC" }, + { EM_S390, "IBM S/390" }, + { EM_SPU, "Sony/Toshiba/IBM SPU" }, + { EM_res024, "Reserved" }, + { EM_res025, "Reserved" }, + { EM_res026, "Reserved" }, + { EM_res027, "Reserved" }, + { EM_res028, "Reserved" }, + { EM_res029, "Reserved" }, + { EM_res030, "Reserved" }, + { EM_res031, "Reserved" }, + { EM_res032, "Reserved" }, + { EM_res033, "Reserved" }, + { EM_res034, "Reserved" }, + { EM_res035, "Reserved" }, + { EM_V800, "NEC V800 series" }, + { EM_FR20, "Fujitsu FR20" }, + { EM_RH32, "TRW RH32" }, + { EM_MCORE, "Motorola M*Core // May also be taken by Fujitsu MMA" }, + { EM_RCE, "Old name for MCore" }, + { EM_ARM, "ARM" }, + { EM_OLD_ALPHA, "Digital Alpha" }, + { EM_SH, "Renesas (formerly Hitachi) / SuperH SH" }, + { EM_SPARCV9, "SPARC v9 64-bit" }, + { EM_TRICORE, "Siemens Tricore embedded processor" }, + { EM_ARC, "ARC Cores" }, + { EM_H8_300, "Renesas (formerly Hitachi) H8/300" }, + { EM_H8_300H, "Renesas (formerly Hitachi) H8/300H" }, + { EM_H8S, "Renesas (formerly Hitachi) H8S" }, + { EM_H8_500, "Renesas (formerly Hitachi) H8/500" }, + { EM_IA_64, "Intel IA-64 Processor" }, + { EM_MIPS_X, "Stanford MIPS-X" }, + { EM_COLDFIRE, "Motorola Coldfire" }, + { EM_68HC12, "Motorola M68HC12" }, + { EM_MMA, "Fujitsu Multimedia Accelerator" }, + { EM_PCP, "Siemens PCP" }, + { EM_NCPU, "Sony nCPU embedded RISC processor" }, + { EM_NDR1, "Denso NDR1 microprocesspr" }, + { EM_STARCORE, "Motorola Star*Core processor" }, + { EM_ME16, "Toyota ME16 processor" }, + { EM_ST100, "STMicroelectronics ST100 processor" }, + { EM_TINYJ, "Advanced Logic Corp. TinyJ embedded processor" }, + { EM_X86_64, "Advanced Micro Devices X86-64 processor" }, + { EM_PDSP, "Sony DSP Processor" }, + { EM_PDP10, "Digital Equipment Corp. PDP-10" }, + { EM_PDP11, "Digital Equipment Corp. PDP-11" }, + { EM_FX66, "Siemens FX66 microcontroller" }, + { EM_ST9PLUS, "STMicroelectronics ST9+ 8/16 bit microcontroller" }, + { EM_ST7, "STMicroelectronics ST7 8-bit microcontroller" }, + { EM_68HC16, "Motorola MC68HC16 Microcontroller" }, + { EM_68HC11, "Motorola MC68HC11 Microcontroller" }, + { EM_68HC08, "Motorola MC68HC08 Microcontroller" }, + { EM_68HC05, "Motorola MC68HC05 Microcontroller" }, + { EM_SVX, "Silicon Graphics SVx" }, + { EM_ST19, "STMicroelectronics ST19 8-bit cpu" }, + { EM_VAX, "Digital VAX" }, + { EM_CRIS, "Axis Communications 32-bit embedded processor" }, + { EM_JAVELIN, "Infineon Technologies 32-bit embedded cpu" }, + { EM_FIREPATH, "Element 14 64-bit DSP processor" }, + { EM_ZSP, "LSI Logic's 16-bit DSP processor" }, + { EM_MMIX, "Donald Knuth's educational 64-bit processor" }, + { EM_HUANY, "Harvard's machine-independent format" }, + { EM_PRISM, "SiTera Prism" }, + { EM_AVR, "Atmel AVR 8-bit microcontroller" }, + { EM_FR30, "Fujitsu FR30" }, + { EM_D10V, "Mitsubishi D10V" }, + { EM_D30V, "Mitsubishi D30V" }, + { EM_V850, "NEC v850" }, + { EM_M32R, "Renesas M32R (formerly Mitsubishi M32R)" }, + { EM_MN10300, "Matsushita MN10300" }, + { EM_MN10200, "Matsushita MN10200" }, + { EM_PJ, "picoJava" }, + { EM_OPENRISC, "OpenRISC 32-bit embedded processor" }, + { EM_ARC_A5, "ARC Cores Tangent-A5" }, + { EM_XTENSA, "Tensilica Xtensa Architecture" }, + { EM_VIDEOCORE, "Alphamosaic VideoCore processor" }, + { EM_TMM_GPP, "Thompson Multimedia General Purpose Processor" }, + { EM_NS32K, "National Semiconductor 32000 series" }, + { EM_TPC, "Tenor Network TPC processor" }, + { EM_SNP1K, "Trebia SNP 1000 processor" }, + { EM_ST200, "STMicroelectronics ST200 microcontroller" }, + { EM_IP2K, "Ubicom IP2022 micro controller" }, + { EM_MAX, "MAX Processor" }, + { EM_CR, "National Semiconductor CompactRISC" }, + { EM_F2MC16, "Fujitsu F2MC16" }, + { EM_MSP430, "TI msp430 micro controller" }, + { EM_BLACKFIN, "ADI Blackfin" }, + { EM_SE_C33, "S1C33 Family of Seiko Epson processors" }, + { EM_SEP, "Sharp embedded microprocessor" }, + { EM_ARCA, "Arca RISC Microprocessor" }, + { EM_UNICORE, "Microprocessor series from PKU-Unity Ltd. and MPRC of " + "Peking University" }, + { EM_EXCESS, "eXcess: 16/32/64-bit configurable embedded CPU" }, + { EM_DXP, "Icera Semiconductor Inc. Deep Execution Processor" }, + { EM_ALTERA_NIOS2, "Altera Nios II soft-core processor" }, + { EM_CRX, "National Semiconductor CRX" }, + { EM_XGATE, "Motorola XGATE embedded processor" }, + { EM_C166, "Infineon C16x/XC16x processor" }, + { EM_M16C, "Renesas M16C series microprocessors" }, + { EM_DSPIC30F, "Microchip Technology dsPIC30F Digital Signal Controller" }, + { EM_CE, "Freescale Communication Engine RISC core" }, + { EM_M32C, "Renesas M32C series microprocessors" }, + { EM_res121, "Reserved" }, + { EM_res122, "Reserved" }, + { EM_res123, "Reserved" }, + { EM_res124, "Reserved" }, + { EM_res125, "Reserved" }, + { EM_res126, "Reserved" }, + { EM_res127, "Reserved" }, + { EM_res128, "Reserved" }, + { EM_res129, "Reserved" }, + { EM_res130, "Reserved" }, + { EM_TSK3000, "Altium TSK3000 core" }, + { EM_RS08, "Freescale RS08 embedded processor" }, + { EM_res133, "Reserved" }, + { EM_ECOG2, "Cyan Technology eCOG2 microprocessor" }, + { EM_SCORE, "Sunplus Score" }, + { EM_SCORE7, "Sunplus S+core7 RISC processor" }, + { EM_DSP24, "New Japan Radio (NJR) 24-bit DSP Processor" }, + { EM_VIDEOCORE3, "Broadcom VideoCore III processor" }, + { EM_LATTICEMICO32, "RISC processor for Lattice FPGA architecture" }, + { EM_SE_C17, "Seiko Epson C17 family" }, + { EM_TI_C6000, "Texas Instruments TMS320C6000 DSP family" }, + { EM_TI_C2000, "Texas Instruments TMS320C2000 DSP family" }, + { EM_TI_C5500, "Texas Instruments TMS320C55x DSP family" }, + { EM_res143, "Reserved" }, + { EM_res144, "Reserved" }, + { EM_res145, "Reserved" }, + { EM_res146, "Reserved" }, + { EM_res147, "Reserved" }, + { EM_res148, "Reserved" }, + { EM_res149, "Reserved" }, + { EM_res150, "Reserved" }, + { EM_res151, "Reserved" }, + { EM_res152, "Reserved" }, + { EM_res153, "Reserved" }, + { EM_res154, "Reserved" }, + { EM_res155, "Reserved" }, + { EM_res156, "Reserved" }, + { EM_res157, "Reserved" }, + { EM_res158, "Reserved" }, + { EM_res159, "Reserved" }, + { EM_MMDSP_PLUS, "STMicroelectronics 64bit VLIW Data Signal Processor" }, + { EM_CYPRESS_M8C, "Cypress M8C microprocessor" }, + { EM_R32C, "Renesas R32C series microprocessors" }, + { EM_TRIMEDIA, "NXP Semiconductors TriMedia architecture family" }, + { EM_QDSP6, "QUALCOMM DSP6 Processor" }, + { EM_8051, "Intel 8051 and variants" }, + { EM_STXP7X, "STMicroelectronics STxP7x family" }, + { EM_NDS32, + "Andes Technology compact code size embedded RISC processor family" }, + { EM_ECOG1, "Cyan Technology eCOG1X family" }, + { EM_ECOG1X, "Cyan Technology eCOG1X family" }, + { EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core Micro-controllers" }, + { EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor" }, + { EM_MANIK, "M2000 Reconfigurable RISC Microprocessor" }, + { EM_CRAYNV2, "Cray Inc. NV2 vector architecture" }, + { EM_RX, "Renesas RX family" }, + { EM_METAG, "Imagination Technologies META processor architecture" }, + { EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture" }, + { EM_ECOG16, "Cyan Technology eCOG16 family" }, + { EM_CR16, "National Semiconductor CompactRISC 16-bit processor" }, + { EM_ETPU, "Freescale Extended Time Processing Unit" }, + { EM_SLE9X, "Infineon Technologies SLE9X core" }, + { EM_L1OM, "Intel L1OM" }, + { EM_INTEL181, "Reserved by Intel" }, + { EM_INTEL182, "Reserved by Intel" }, + { EM_AARCH64, "ARM AArch64" }, + { EM_res184, "Reserved by ARM" }, + { EM_AVR32, "Atmel Corporation 32-bit microprocessor family" }, + { EM_STM8, "STMicroeletronics STM8 8-bit microcontroller" }, + { EM_TILE64, "Tilera TILE64 multicore architecture family" }, + { EM_TILEPRO, "Tilera TILEPro multicore architecture family" }, + { EM_MICROBLAZE, "Xilinx MicroBlaze 32-bit RISC soft processor core" }, + { EM_CUDA, "NVIDIA CUDA architecture " }, + { EM_TILEGX, "Tilera TILE-Gx multicore architecture family" }, + { EM_CLOUDSHIELD, "CloudShield architecture family" }, + { EM_COREA_1ST, "KIPO-KAIST Core-A 1st generation processor family" }, + { EM_COREA_2ND, "KIPO-KAIST Core-A 2nd generation processor family" }, + { EM_ARC_COMPACT2, "Synopsys ARCompact V2" }, + { EM_OPEN8, "Open8 8-bit RISC soft processor core" }, + { EM_RL78, "Renesas RL78 family" }, + { EM_VIDEOCORE5, "Broadcom VideoCore V processor" }, + { EM_78KOR, "Renesas 78KOR family" }, + { EM_56800EX, "Freescale 56800EX Digital Signal Controller (DSC)" }, + { EM_BA1, "Beyond BA1 CPU architecture" }, + { EM_BA2, "Beyond BA2 CPU architecture" }, + { EM_XCORE, "XMOS xCORE processor family" }, + { EM_MCHP_PIC, "Microchip 8-bit PIC(r) family" }, + { EM_INTEL205, "Reserved by Intel" }, + { EM_INTEL206, "Reserved by Intel" }, + { EM_INTEL207, "Reserved by Intel" }, + { EM_INTEL208, "Reserved by Intel" }, + { EM_INTEL209, "Reserved by Intel" }, + { EM_KM32, "KM211 KM32 32-bit processor" }, + { EM_KMX32, "KM211 KMX32 32-bit processor" }, + { EM_KMX16, "KM211 KMX16 16-bit processor" }, + { EM_KMX8, "KM211 KMX8 8-bit processor" }, + { EM_KVARC, "KM211 KVARC processor" }, + { EM_CDP, "Paneve CDP architecture family" }, + { EM_COGE, "Cognitive Smart Memory Processor" }, + { EM_COOL, "iCelero CoolEngine" }, + { EM_NORC, "Nanoradio Optimized RISC" }, + { EM_CSR_KALIMBA, "CSR Kalimba architecture family" }, + { EM_Z80, "Zilog Z80" }, + { EM_VISIUM, "Controls and Data Services VISIUMcore processor" }, + { EM_FT32, "FTDI Chip FT32 high performance 32-bit RISC architecture" }, + { EM_MOXIE, "Moxie processor family" }, + { EM_AMDGPU, "AMD GPU architecture" }, + { EM_RISCV, "RISC-V" }, + { EM_LANAI, "Lanai processor" }, + { EM_CEVA, "CEVA Processor Architecture Family" }, + { EM_CEVA_X2, "CEVA X2 Processor Family" }, + { EM_BPF, "Linux BPF – in-kernel virtual machine" }, + { EM_GRAPHCORE_IPU, "Graphcore Intelligent Processing Unit" }, + { EM_IMG1, "Imagination Technologies" }, + { EM_NFP, "Netronome Flow Processor (P)" }, + { EM_CSKY, "C-SKY processor family" }, + { EM_ARC_COMPACT3_64, "Synopsys ARCv2.3 64-bit" }, + { EM_MCS6502, "MOS Technology MCS 6502 processor" }, + { EM_ARC_COMPACT3, "Synopsys ARCv2.3 32-bit" }, + { EM_KVX, "Kalray VLIW core of the MPPA processor family" }, + { EM_65816, "WDC 65816/65C816" }, + { EM_LOONGARCH, "Loongson Loongarch" }, + { EM_KF32, "ChipON KungFu32" }, + { EM_MT, "Morpho Techologies MT processor" }, + { EM_ALPHA, "Alpha" }, + { EM_WEBASSEMBLY, "Web Assembly" }, + { EM_DLX, "OpenDLX" }, + { EM_XSTORMY16, "Sanyo XStormy16 CPU core" }, + { EM_IQ2000, "Vitesse IQ2000" }, + { EM_M32C_OLD, "M32C_OLD" }, + { EM_NIOS32, "Altera Nios" }, + { EM_CYGNUS_MEP, "Toshiba MeP Media Engine" }, + { EM_ADAPTEVA_EPIPHANY, "Adapteva EPIPHANY" }, + { EM_CYGNUS_FRV, "Fujitsu FR-V" }, + { EM_S12Z, "Freescale S12Z" }, +}; + +static const struct section_type_table_t +{ + const Elf64_Word key; + const char* str; +} section_type_table[] = { + { SHT_NULL, "NULL" }, + { SHT_PROGBITS, "PROGBITS" }, + { SHT_SYMTAB, "SYMTAB" }, + { SHT_STRTAB, "STRTAB" }, + { SHT_RELA, "RELA" }, + { SHT_HASH, "HASH" }, + { SHT_DYNAMIC, "DYNAMIC" }, + { SHT_NOTE, "NOTE" }, + { SHT_NOBITS, "NOBITS" }, + { SHT_REL, "REL" }, + { SHT_SHLIB, "SHLIB" }, + { SHT_DYNSYM, "DYNSYM" }, + { SHT_INIT_ARRAY, "INIT_ARRAY" }, + { SHT_FINI_ARRAY, "FINI_ARRAY" }, + { SHT_PREINIT_ARRAY, "PREINIT_ARRAY" }, + { SHT_GROUP, "GROUP" }, + { SHT_SYMTAB_SHNDX, "SYMTAB_SHNDX" }, + { SHT_GNU_ATTRIBUTES, "GNU_ATTRIBUTES" }, + { SHT_GNU_HASH, "GNU_HASH" }, + { SHT_GNU_LIBLIST, "GNU_LIBLIST" }, + { SHT_CHECKSUM, "CHECKSUM" }, + { SHT_LOSUNW, "LOSUNW" }, + { SHT_SUNW_move, "SUNW_move" }, + { SHT_SUNW_COMDAT, "SUNW_COMDAT" }, + { SHT_SUNW_syminfo, "SUNW_syminfo" }, + { SHT_GNU_verdef, "GNU_verdef" }, + { SHT_GNU_verneed, "GNU_verneed" }, + { SHT_GNU_versym, "GNU_versym" }, + { SHT_ARM_EXIDX, "ARM_EXIDX" }, + { SHT_ARM_PREEMPTMAP, "ARM_PREEMPTMAP" }, + { SHT_ARM_ATTRIBUTES, "ARM_ATTRIBUTES" }, + { SHT_ARM_DEBUGOVERLAY, "ARM_DEBUGOVERLAY" }, + { SHT_ARM_OVERLAYSECTION, "ARM_OVERLAYSECTION" }, + +}; + +static const struct segment_type_table_t +{ + const Elf_Word key; + const char* str; +} segment_type_table[] = { + { PT_NULL, "NULL" }, + { PT_LOAD, "LOAD" }, + { PT_DYNAMIC, "DYNAMIC" }, + { PT_INTERP, "INTERP" }, + { PT_NOTE, "NOTE" }, + { PT_SHLIB, "SHLIB" }, + { PT_PHDR, "PHDR" }, + { PT_TLS, "TLS" }, + { PT_GNU_EH_FRAME, "GNU_EH_FRAME" }, + { PT_GNU_STACK, "GNU_STACK" }, + { PT_GNU_RELRO, "GNU_RELRO" }, + { PT_GNU_PROPERTY, "GNU_PROPERTY" }, + { PT_GNU_MBIND_LO, "GNU_MBIND_LO" }, + { PT_GNU_MBIND_HI, "GNU_MBIND_HI" }, + { PT_PAX_FLAGS, "PAX_FLAGS" }, + { PT_OPENBSD_RANDOMIZE, "OPENBSD_RANDOMIZE" }, + { PT_OPENBSD_WXNEEDED, "OPENBSD_WXNEEDED " }, + { PT_OPENBSD_BOOTDATA, "OPENBSD_BOOTDATA " }, + { PT_SUNWBSS, "PT_SUNWBSS" }, + { PT_SUNWSTACK, "SUNWSTACK" }, +}; + +static const struct segment_flag_table_t +{ + const Elf_Word key; + const char* str; +} segment_flag_table[] = { + { 0, " " }, { 1, " E" }, { 2, " W " }, { 3, " WE" }, + { 4, "R " }, { 5, "R E" }, { 6, "RW " }, { 7, "RWE" }, +}; + +static const struct symbol_bind_t +{ + const Elf_Word key; + const char* str; +} symbol_bind_table[] = { + { STB_LOCAL, "LOCAL" }, { STB_GLOBAL, "GLOBAL" }, + { STB_WEAK, "WEAK" }, { STB_LOOS, "LOOS" }, + { STB_HIOS, "HIOS" }, { STB_MULTIDEF, "MULTIDEF" }, + { STB_LOPROC, "LOPROC" }, { STB_HIPROC, "HIPROC" }, +}; + +static const struct symbol_type_t +{ + const Elf_Word key; + const char* str; +} symbol_type_table[] = { + { STT_NOTYPE, "NOTYPE" }, { STT_OBJECT, "OBJECT" }, + { STT_FUNC, "FUNC" }, { STT_SECTION, "SECTION" }, + { STT_FILE, "FILE" }, { STT_COMMON, "COMMON" }, + { STT_TLS, "TLS" }, { STT_LOOS, "LOOS" }, + { STT_HIOS, "HIOS" }, { STT_LOPROC, "LOPROC" }, + { STT_HIPROC, "HIPROC" }, +}; + +static const struct dynamic_tag_t +{ + const Elf_Word key; + const char* str; +} dynamic_tag_table[] = { + { DT_NULL, "NULL" }, + { DT_NEEDED, "NEEDED" }, + { DT_PLTRELSZ, "PLTRELSZ" }, + { DT_PLTGOT, "PLTGOT" }, + { DT_HASH, "HASH" }, + { DT_STRTAB, "STRTAB" }, + { DT_SYMTAB, "SYMTAB" }, + { DT_RELA, "RELA" }, + { DT_RELASZ, "RELASZ" }, + { DT_RELAENT, "RELAENT" }, + { DT_STRSZ, "STRSZ" }, + { DT_SYMENT, "SYMENT" }, + { DT_INIT, "INIT" }, + { DT_FINI, "FINI" }, + { DT_SONAME, "SONAME" }, + { DT_RPATH, "RPATH" }, + { DT_SYMBOLIC, "SYMBOLIC" }, + { DT_REL, "REL" }, + { DT_RELSZ, "RELSZ" }, + { DT_RELENT, "RELENT" }, + { DT_PLTREL, "PLTREL" }, + { DT_DEBUG, "DEBUG" }, + { DT_TEXTREL, "TEXTREL" }, + { DT_JMPREL, "JMPREL" }, + { DT_BIND_NOW, "BIND_NOW" }, + { DT_INIT_ARRAY, "INIT_ARRAY" }, + { DT_FINI_ARRAY, "FINI_ARRAY" }, + { DT_INIT_ARRAYSZ, "INIT_ARRAYSZ" }, + { DT_FINI_ARRAYSZ, "FINI_ARRAYSZ" }, + { DT_RUNPATH, "RUNPATH" }, + { DT_FLAGS, "FLAGS" }, + { DT_ENCODING, "ENCODING" }, + { DT_PREINIT_ARRAY, "PREINIT_ARRAY" }, + { DT_PREINIT_ARRAYSZ, "PREINIT_ARRAYSZ" }, + { DT_MAXPOSTAGS, "MAXPOSTAGS" }, + { DT_GNU_HASH, "GNU_HASH" }, + { DT_TLSDESC_PLT, "TLSDESC_PLT" }, + { DT_TLSDESC_GOT, "TLSDESC_GOT" }, + { DT_GNU_CONFLICT, "GNU_CONFLICT" }, + { DT_GNU_LIBLIST, "GNU_LIBLIST" }, + { DT_CONFIG, "CONFIG" }, + { DT_DEPAUDIT, "DEPAUDIT" }, + { DT_AUDIT, "AUDIT" }, + { DT_PLTPAD, "PLTPAD" }, + { DT_MOVETAB, "MOVETAB" }, + { DT_SYMINFO, "SYMINFO" }, + { DT_ADDRRNGHI, "ADDRRNGHI" }, + { DT_VERSYM, "VERSYM" }, + { DT_RELACOUNT, "RELACOUNT" }, + { DT_RELCOUNT, "RELCOUNT" }, + { DT_FLAGS_1, "FLAGS_1" }, + { DT_VERDEF, "VERDEF" }, + { DT_VERDEFNUM, "VERDEFNUM" }, + { DT_VERNEED, "VERNEED" }, + { DT_VERNEEDNUM, "VERNEEDNUM" }, +}; + +// clang-format off +static const struct note_tag_t +{ + struct note_values_t + { + Elf64_Word type; + std::string type_str; + std::string description; + }; + std::string name; + std::vector values; +} note_tag_table[] = { + { "", + { { NT_PRSTATUS, "NT_PRSTATUS", "prstatus struct" }, + { NT_FPREGSET, "NT_FPREGSET", "fpregset struct" }, + { NT_PRPSINFO, "NT_PRPSINFO", "prpsinfo struct" }, + { NT_TASKSTRUCT, "NT_TASKSTRUCT", "task struct" }, + { NT_AUXV, "NT_AUXV", "Elfxx_auxv_t" }, + { NT_PSTATUS, "NT_PSTATUS", "pstatus struct" }, + { NT_FPREGS, "NT_FPREGS", "fpregset struct" }, + { NT_PSINFO, "NT_PSINFO", "psinfo struct" }, + { NT_LWPSTATUS, "NT_LWPSTATUS", "lwpstatus_t struct" }, + { NT_LWPSINFO, "NT_LWPSINFO", "lwpsinfo_t struct" }, + { NT_WIN32PSTATUS, "NT_WIN32PSTATUS", "win32_pstatus struct" }, + } }, + { "LINUX", + { { NT_PRXFPREG, "NT_PRXFPREG", "Contains a user_xfpregs_struct;" }, + { NT_PPC_VMX, "NT_PPC_VMX", "PowerPC Altivec/VMX registers" }, + { NT_PPC_VSX, "NT_PPC_VSX", "PowerPC VSX registers" }, + { NT_PPC_TAR, "NT_PPC_TAR", "PowerPC Target Address Register" }, + { NT_PPC_PPR, "NT_PPC_PPR", "PowerPC Program Priority Register" }, + { NT_PPC_DSCR, "NT_PPC_DSCR", "PowerPC Data Stream Control Register" }, + { NT_PPC_EBB, "NT_PPC_EBB", "PowerPC Event Based Branch Registers" }, + { NT_PPC_PMU, "NT_PPC_PMU", "PowerPC Performance Monitor Registers" }, + { NT_PPC_TM_CGPR, "NT_PPC_TM_CGPR", "PowerPC TM checkpointed GPR Registers" }, + { NT_PPC_TM_CFPR, "NT_PPC_TM_CFPR", "PowerPC TM checkpointed FPR Registers" }, + { NT_PPC_TM_CVMX, "NT_PPC_TM_CVMX", "PowerPC TM checkpointed VMX Registers" }, + { NT_PPC_TM_CVSX, "NT_PPC_TM_CVSX", "PowerPC TM checkpointed VSX Registers" }, + { NT_PPC_TM_SPR, "NT_PPC_TM_SPR", "PowerPC TM Special Purpose Registers" }, + { NT_PPC_TM_CTAR, "NT_PPC_TM_CTAR", "PowerPC TM checkpointed TAR" }, + { NT_PPC_TM_CPPR, "NT_PPC_TM_CPPR", "PowerPC TM checkpointed PPR" }, + { NT_PPC_TM_CDSCR, "NT_PPC_TM_CDSCR", "PowerPC TM checkpointed Data SCR" }, + { NT_386_TLS, "NT_386_TLS", "x86 TLS information" }, + { NT_386_IOPERM, "NT_386_IOPERM", "x86 io permissions" }, + { NT_X86_XSTATE, "NT_X86_XSTATE", "x86 XSAVE extended state" }, + { NT_X86_CET, "NT_X86_CET", "x86 CET state" }, + { NT_S390_HIGH_GPRS, "NT_S390_HIGH_GPRS", "S/390 upper halves of GPRs " }, + { NT_S390_TIMER, "NT_S390_TIMER", "S390 timer" }, + { NT_S390_TODCMP, "NT_S390_TODCMP", "S390 TOD clock comparator" }, + { NT_S390_TODPREG, "NT_S390_TODPREG", "S390 TOD programmable register" }, + { NT_S390_CTRS, "NT_S390_CTRS", "S390 control registers" }, + { NT_S390_PREFIX, "NT_S390_PREFIX", "S390 prefix register" }, + { NT_S390_LAST_BREAK, "NT_S390_LAST_BREAK", "S390 breaking event address" }, + { NT_S390_SYSTEM_CALL, "NT_S390_SYSTEM_CALL", "S390 system call restart data" }, + { NT_S390_TDB, "NT_S390_TDB", "S390 transaction diagnostic block" }, + { NT_S390_VXRS_LOW, "NT_S390_VXRS_LOW", "S390 vector registers 0-15 upper half" }, + { NT_S390_VXRS_HIGH, "NT_S390_VXRS_HIGH", "S390 vector registers 16-31" }, + { NT_S390_GS_CB, "NT_S390_GS_CB", "s390 guarded storage registers" }, + { NT_S390_GS_BC, "NT_S390_GS_BC", "s390 guarded storage broadcast control block" }, + { NT_ARM_VFP, "NT_ARM_VFP", "ARM VFP registers" }, + { NT_ARM_TLS, "NT_ARM_TLS", "AArch TLS registers" }, + { NT_ARM_HW_BREAK, "NT_ARM_HW_BREAK", "AArch hardware breakpoint registers" }, + { NT_ARM_HW_WATCH, "NT_ARM_HW_WATCH", "AArch hardware watchpoint registers" }, + { NT_ARM_SVE, "NT_ARM_SVE", "AArch SVE registers. " }, + { NT_ARM_PAC_MASK, "NT_ARM_PAC_MASK", "AArch pointer authentication code masks" }, + { NT_ARM_PACA_KEYS, "NT_ARM_PACA_KEYS", "ARM pointer authentication address keys" }, + { NT_ARM_PACG_KEYS, "NT_ARM_PACG_KEYS", "ARM pointer authentication generic keys" }, + { NT_ARM_TAGGED_ADDR_CTRL, "NT_ARM_TAGGED_ADDR_CTRL", "AArch64 tagged address control (prctl())" }, + { NT_ARM_PAC_ENABLED_KEYS, "NT_ARM_PAC_ENABLED_KEYS", "AArch64 pointer authentication enabled keys (prctl())" }, + { NT_ARC_V2, "NT_ARC_V2", "ARC HS accumulator/extra registers. " }, + { NT_LARCH_CPUCFG, "NT_LARCH_CPUCFG", "LoongArch CPU config registers" }, + { NT_LARCH_CSR, "NT_LARCH_CSR", "LoongArch Control State Registers" }, + { NT_LARCH_LSX, "NT_LARCH_LSX", "LoongArch SIMD eXtension registers" }, + { NT_LARCH_LASX, "NT_LARCH_LASX", "LoongArch Advanced SIMD eXtension registers" }, + { NT_RISCV_CSR, "NT_RISCV_CSR", "RISC-V Control and Status Registers" }, + } }, + { "CORE", + { { NT_LARCH_LBT, "NT_LARCH_LBT", "LoongArch Binary Translation registers" } + } }, + { "FreeBSD", + { { NT_FREEBSD_THRMISC, "NT_FREEBSD_THRMISC", "Thread miscellaneous info." }, + { NT_FREEBSD_PROCSTAT_PROC, "NT_FREEBSD_PROCSTAT_PROC", "Procstat proc data." }, + { NT_FREEBSD_PROCSTAT_FILES, "NT_FREEBSD_PROCSTAT_FILES", "Procstat files data." }, + { NT_FREEBSD_PROCSTAT_VMMAP, "NT_FREEBSD_PROCSTAT_VMMAP", "Procstat vmmap data." }, + { NT_FREEBSD_PROCSTAT_GROUPS, "NT_FREEBSD_PROCSTAT_GROUPS", "Procstat groups data." }, + { NT_FREEBSD_PROCSTAT_UMASK, "NT_FREEBSD_PROCSTAT_UMASK", "Procstat umask data." }, + { NT_FREEBSD_PROCSTAT_RLIMIT, "NT_FREEBSD_PROCSTAT_RLIMIT", "Procstat rlimit data." }, + { NT_FREEBSD_PROCSTAT_OSREL, "NT_FREEBSD_PROCSTAT_OSREL", "Procstat osreldate data." }, + { NT_FREEBSD_PROCSTAT_PSSTRINGS, "NT_FREEBSD_PROCSTAT_PSSTRINGS", "Procstat ps_strings data." }, + { NT_FREEBSD_PROCSTAT_AUXV, "NT_FREEBSD_PROCSTAT_AUXV", "Procstat auxv data." }, + { NT_FREEBSD_PTLWPINFO, "NT_FREEBSD_PTLWPINFO", "Thread ptrace miscellaneous info." }, + } }, + { "NetBSD-CORE", + { { NT_NETBSDCORE_PROCINFO, "NT_NETBSDCORE_PROCINFO", "Has a struct procinfo" }, + { NT_NETBSDCORE_AUXV, "NT_NETBSDCORE_AUXV", "Has auxv data" }, + { NT_NETBSDCORE_LWPSTATUS, "NT_NETBSDCORE_LWPSTATUS", "Has LWPSTATUS data" }, + { NT_NETBSDCORE_FIRSTMACH, "NT_NETBSDCORE_FIRSTMACH", "start of machdep note types" }, + } }, + { "OpenBSD", + { { NT_OPENBSD_PROCINFO, "NT_OPENBSD_PROCINFO", "" }, + { NT_OPENBSD_AUXV, "NT_OPENBSD_AUXV", "" }, + { NT_OPENBSD_REGS, "NT_OPENBSD_REGS", "" }, + { NT_OPENBSD_FPREGS, "NT_OPENBSD_FPREGS", "" }, + { NT_OPENBSD_XFPREGS, "NT_OPENBSD_XFPREGS", "" }, + { NT_OPENBSD_WCOOKIE, "NT_OPENBSD_WCOOKIE", "" }, + } }, + { "SPU", + { { NT_SPU, "NT_SPU", "" } + } }, + { "GNU", + { + { NT_GNU_ABI_TAG, "NT_GNU_ABI_TAG", "GNU ABI tag" }, + { NT_GNU_HWCAP, "NT_GNU_HWCAP", "Used by ld.so and kernel vDSO" }, + { NT_GNU_BUILD_ID, "NT_GNU_BUILD_ID", "Build ID of the binary" }, + { NT_GNU_GOLD_VERSION, "NT_GNU_GOLD_VERSION", "Version of GNU gold used to link the binary" }, + { NT_GNU_PROPERTY_TYPE_0, "NT_GNU_PROPERTY_TYPE_0", "Property type 0" }, + // { NT_GNU_PROPERTY_TYPE_1, "NT_GNU_PROPERTY_TYPE_1", "Property type 1" }, + // { NT_GNU_PROPERTY_TYPE_2, "NT_GNU_PROPERTY_TYPE_2", "Property type 2" }, + // { NT_GNU_PROPERTY_TYPE_3, "NT_GNU_PROPERTY_TYPE_3", "Property type 3" }, + // { NT_GNU_PROPERTY_TYPE_4, "NT_GNU_PROPERTY_TYPE_4", "Property type 4" }, + // { NT_GNU_PROPERTY_TYPE_5, "NT_GNU_PROPERTY_TYPE_5", "Property type 5" }, + // { NT_GNU_PROPERTY_TYPE_6, "NT_GNU_PROPERTY_TYPE_6", "Property type 6" }, + // { NT_GNU_PROPERTY_TYPE_7, "NT_GNU_PROPERTY_TYPE_7", "Property type 7" }, + // { NT_GNU_PROPERTY_TYPE_8, "NT_GNU_PROPERTY_TYPE_8", "Property type 8" }, + // { NT_GNU_PROPERTY_TYPE_9, "NT_GNU_PROPERTY_TYPE_9", "Property type 9" }, + // { NT_GNU_PROPERTY_TYPE_10, "NT_GNU_PROPERTY_TYPE_10", "Property type 10" }, + // { NT_GNU_PROPERTY_TYPE_11, "NT_GNU_PROPERTY_TYPE_11", "Property type 11" }, + // { NT_GNU_PROPERTY_TYPE_12, "NT_GNU_PROPERTY_TYPE_12", "Property type 12" }, + // { NT_GNU_PROPERTY_TYPE_13, "NT_GNU_PROPERTY_TYPE_13", "Property type 13" }, + // { NT_GNU_PROPERTY_TYPE_14, "NT_GNU_PROPERTY_TYPE_14", "Property type 14" }, + } }, + // { "SOLARIS", + // { { NT_SOLARIS_AUXV, "NT_SOLARIS_AUXV", "" } + // } }, + // { "AIX", + // { { NT_AIX_AUXV, "NT_AIX_AUXV", "" } + // } }, + // { "IRIX", + // { { NT_IRIX_FPREGS, "NT_IRIX_FPREGS", "" } + // } }, +}; +// clang-format on + +static const ELFIO::Elf_Xword MAX_DATA_ENTRIES = 64; + +//------------------------------------------------------------------------------ +// Class representing the ELF dump functionality +class dump +{ +#define DUMP_DEC_FORMAT( width ) \ + std::setw( width ) << std::setfill( ' ' ) << std::dec << std::right +#define DUMP_HEX0x_FORMAT( width ) \ + "0x" << std::setw( width ) << std::setfill( '0' ) << std::hex << std::right +#define DUMP_HEX_FORMAT( width ) \ + std::setw( width ) << std::setfill( '0' ) << std::hex << std::right +#define DUMP_STR_FORMAT( width ) \ + std::setw( width ) << std::setfill( ' ' ) << std::hex << std::left + + public: + //------------------------------------------------------------------------------ + // Dumps the ELF header information + static void header( std::ostream& out, const elfio& reader ) + { + if ( !reader.get_header_size() ) { + return; + } + out << "ELF Header" << std::endl + << std::endl + << " Class: " << str_class( reader.get_class() ) << std::endl + << " Encoding: " << str_endian( reader.get_encoding() ) + << std::endl + << " ELFVersion: " << str_version( reader.get_elf_version() ) + << std::endl + << " OS/ABI: " << str_os_abi( reader.get_os_abi() ) + << std::endl + << " ABI Version:" << (int)reader.get_abi_version() << std::endl + << " Type: " << str_type( reader.get_type() ) << std::endl + << " Machine: " << str_machine( reader.get_machine() ) + << std::endl + << " Version: " << str_version( reader.get_version() ) + << std::endl + << " Entry: " << "0x" << std::hex << reader.get_entry() + << std::endl + << " Flags: " << "0x" << std::hex << reader.get_flags() + << std::endl + << std::endl; + } + + //------------------------------------------------------------------------------ + // Dumps the section headers information + static void section_headers( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.sections.size(); + + if ( n == 0 ) { + return; + } + + out << "Section Headers:" << std::endl; + if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit + out << "[ Nr ] Type Addr Size ES Flg " + "Lk Inf Al Name" + << std::endl; + } + else { // Output for 64-bit + out << "[ Nr ] Type Addr Size " + " Offset Flg" + << std::endl + << " ES Lk Inf Al Name" << std::endl; + } + + for ( Elf_Half i = 0; i < n; ++i ) { // For all sections + const section* sec = reader.sections[i]; + section_header( out, i, sec, reader.get_class() ); + } + + out << "Key to Flags: W (write), A (alloc), X (execute), " << std::endl; + out << " M (merge), S (strings), I (info)," << std::endl; + out << " L (link order), O (extra OS processing required)," + << std::endl; + out << " G (group), T (TLS), C (compressed), E (exclude)" + << std::endl; + } + + //------------------------------------------------------------------------------ + // Dumps a single section header information + static void section_header( std::ostream& out, + Elf_Half no, + const section* sec, + unsigned char elf_class ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + // clang-format off + if ( elf_class == ELFCLASS32 ) { // Output for 32-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) + << " " << DUMP_HEX0x_FORMAT( 8 ) << sec->get_address() << " " + << DUMP_HEX0x_FORMAT( 8 ) << sec->get_size() << " " + << DUMP_HEX0x_FORMAT( 2 ) << sec->get_entry_size() << " " + << DUMP_STR_FORMAT( 3 ) << section_flags( sec->get_flags() ) + << " " << DUMP_HEX0x_FORMAT( 2 ) << sec->get_link() << " " + << DUMP_HEX0x_FORMAT( 3 ) << sec->get_info() << " " + << DUMP_HEX0x_FORMAT( 2 ) << sec->get_addr_align() << " " + << DUMP_STR_FORMAT( 17 ) << sec->get_name() << " " << std::endl; + } + else { // Output for 64-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) << " " + << DUMP_HEX0x_FORMAT( 16 ) << sec->get_address() << " " + << DUMP_HEX0x_FORMAT( 16 ) << sec->get_size() << " " + << DUMP_HEX0x_FORMAT( 8 ) << sec->get_offset() << " " + << DUMP_STR_FORMAT( 3) << section_flags( sec->get_flags() ) + << std::endl + << DUMP_STR_FORMAT( 8 ) << " " + << DUMP_HEX0x_FORMAT( 4 ) << sec->get_entry_size() << " " + << DUMP_HEX0x_FORMAT( 4 ) << sec->get_link() << " " + << DUMP_HEX0x_FORMAT( 4 ) << sec->get_info() << " " + << DUMP_HEX0x_FORMAT( 4 ) << sec->get_addr_align() << " " + << DUMP_STR_FORMAT( 17 ) << sec->get_name() + << std::endl; + } + // clang-format on + + out.flags( original_flags ); + + return; + } + + //------------------------------------------------------------------------------ + // Dumps the segment headers information + static void segment_headers( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.segments.size(); + if ( n == 0 ) { + return; + } + + out << "Program Headers:" << std::endl; + if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit + out << "[ Nr ] Type VirtAddr PhysAddr FileSize " + "Mem.Size Flags Align" + << std::endl; + } + else { // Output for 64-bit + out << "[ Nr ] Type Offset VirtAddr " + " PhysAddr" + + << std::endl + << " FileSize MemSize " + " Flags Align" + << std::endl; + } + + for ( Elf_Half i = 0; i < n; ++i ) { + const segment* seg = reader.segments[i]; + segment_header( out, i, seg, reader.get_class() ); + } + + out << std::endl; + for ( Elf_Half i = 0; i < n; ++i ) { + out << "[" << i << "]" << " "; + const segment* seg = reader.segments[i]; + for ( Elf_Half j = 0; j < seg->get_sections_num(); j++ ) { + const section* sec = + reader.sections[seg->get_section_index_at( j )]; + out << sec->get_name() << " "; + } + out << std::endl; + } + + out << std::endl; + } + + //------------------------------------------------------------------------------ + // Dumps a single segment header information + static void segment_header( std::ostream& out, + Elf_Half no, + const segment* seg, + unsigned int elf_class ) + { + std::ios_base::fmtflags original_flags = out.flags(); + // clang-format off + if ( elf_class == ELFCLASS32 ) { // Output for 32-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) + << " " << DUMP_HEX0x_FORMAT( 8 ) << seg->get_virtual_address() + << " " << DUMP_HEX0x_FORMAT( 8 ) << seg->get_physical_address() + << " " << DUMP_HEX0x_FORMAT( 8 ) << seg->get_file_size() << " " + << DUMP_HEX0x_FORMAT( 8 ) << seg->get_memory_size() << " " + << DUMP_STR_FORMAT( 8 ) << str_segment_flag( seg->get_flags() ) + << " " << DUMP_HEX0x_FORMAT( 8 ) << seg->get_align() << " " + << std::endl; + } + else { // Output for 64-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) << " " + << DUMP_HEX0x_FORMAT( 16 ) << seg->get_offset() << " " + << DUMP_HEX0x_FORMAT( 16 ) << seg->get_virtual_address() << " " + << DUMP_HEX0x_FORMAT( 16 ) << seg->get_physical_address() + << std::endl + << DUMP_STR_FORMAT( 23 ) << " " + << DUMP_HEX0x_FORMAT( 16 ) << seg->get_file_size() << " " + << DUMP_HEX0x_FORMAT( 16 ) << seg->get_memory_size() << " " + << DUMP_STR_FORMAT( 3 ) << str_segment_flag( seg->get_flags() ) << " " + << DUMP_HEX0x_FORMAT( 1 ) << seg->get_align() + << std::endl; + } + // clang-format on + + out.flags( original_flags ); + } + + //------------------------------------------------------------------------------ + // Dumps the symbol tables information + static void symbol_tables( std::ostream& out, const elfio& reader ) + { + for ( const auto& sec : reader.sections ) { // For all sections + if ( SHT_SYMTAB == sec->get_type() || + SHT_DYNSYM == sec->get_type() ) { + const_symbol_section_accessor symbols( reader, sec.get() ); + + Elf_Xword sym_no = symbols.get_symbols_num(); + if ( sym_no == 0 ) { + continue; + } + + out << "Symbol table (" << sec->get_name() << ")" << std::endl; + if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit + out << "[ Nr ] Value Size Type Bind " + " Sect Name" + << std::endl; + } + else { // Output for 64-bit + out << "[ Nr ] Value Size " + "Type Bind Sect" + << std::endl + << " Name" << std::endl; + } + for ( Elf_Xword i = 0; i < sym_no; ++i ) { + std::string name; + Elf64_Addr value = 0; + Elf_Xword size = 0; + unsigned char bind = 0; + unsigned char type = 0; + Elf_Half section = 0; + unsigned char other = 0; + symbols.get_symbol( i, name, value, size, bind, type, + section, other ); + symbol_table( out, i, name, value, size, bind, type, + section, reader.get_class() ); + } + + out << std::endl; + } + } + } + + //------------------------------------------------------------------------------ + // Dumps a single symbol table entry information + static void symbol_table( std::ostream& out, + Elf_Xword no, + const std::string& name, + Elf64_Addr value, + Elf_Xword size, + unsigned char bind, + unsigned char type, + Elf_Half section, + unsigned int elf_class ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + if ( elf_class == ELFCLASS32 ) { // Output for 32-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_HEX0x_FORMAT( 8 ) << value << " " + << DUMP_HEX0x_FORMAT( 8 ) << size << " " << DUMP_STR_FORMAT( 7 ) + << str_symbol_type( type ) << " " << DUMP_STR_FORMAT( 8 ) + << str_symbol_bind( bind ) << " " << DUMP_DEC_FORMAT( 5 ) + << section << " " << DUMP_STR_FORMAT( 1 ) << name << " " + << std::endl; + } + else { // Output for 64-bit + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_HEX0x_FORMAT( 16 ) << value << " " + << DUMP_HEX0x_FORMAT( 16 ) << size << " " + << DUMP_STR_FORMAT( 7 ) << str_symbol_type( type ) << " " + << DUMP_STR_FORMAT( 8 ) << str_symbol_bind( bind ) << " " + << DUMP_DEC_FORMAT( 5 ) << section << " " << std::endl + << " " << DUMP_STR_FORMAT( 1 ) << name << " " + << std::endl; + } + + out.flags( original_flags ); + } + + //------------------------------------------------------------------------------ + // Dumps the notes information + static void notes( std::ostream& out, const elfio& reader ) + { + for ( const auto& sec : reader.sections ) { // For all sections + if ( SHT_NOTE == sec->get_type() ) { // Look at notes + note_section_accessor notes( reader, sec.get() ); + Elf_Word no_notes = notes.get_notes_num(); + + if ( no_notes == 0 ) + continue; + + out << "Note section (" << sec->get_name() << ")" << std::endl + << " No Name Data size Description" + << std::endl; + for ( Elf_Word j = 0; j < no_notes; ++j ) { // For all notes + Elf_Word type; + std::string name; + char* desc; + Elf_Word descsz; + + if ( notes.get_note( j, type, name, desc, descsz ) ) { + // 'name' usually contains \0 at the end. Remove it + name = name.c_str(); + note( out, j, type, name, desc, descsz ); + out << std::endl; + } + } + + out << std::endl; + } + } + + Elf_Half no = reader.segments.size(); + for ( Elf_Half i = 0; i < no; ++i ) { // For all segments + segment* seg = reader.segments[i]; + if ( PT_NOTE == seg->get_type() ) { // Look at notes + note_segment_accessor notes( reader, seg ); + Elf_Word no_notes = notes.get_notes_num(); + + if ( no_notes == 0 ) + continue; + + out << "Note segment (" << i << ")" << std::endl + << " No Name Data size Description" + << std::endl; + for ( Elf_Word j = 0; j < no_notes; ++j ) { // For all notes + Elf_Word type; + std::string name; + char* desc; + Elf_Word descsz; + + if ( notes.get_note( j, type, name, desc, descsz ) ) { + // 'name' usually contains \0 at the end. Remove it + name = name.c_str(); + note( out, j, type, name, desc, descsz ); + out << std::endl; + } + } + + out << std::endl; + } + } + } + + //------------------------------------------------------------------------------ + // Dumps a single note information + static void note( std::ostream& out, + int no, + Elf_Word type, + const std::string& name, + void* desc, + Elf_Word descsz ) + { + out << " [" << DUMP_DEC_FORMAT( 2 ) << no << "] "; + + const auto name_group = std::find_if( + std::begin( note_tag_table ), std::end( note_tag_table ), + [&name]( const note_tag_t& entry ) { return entry.name == name; } ); + + std::vector::const_iterator type_value; + if ( name_group != std::end( note_tag_table ) ) { + type_value = std::find_if( + name_group->values.begin(), name_group->values.end(), + [&type]( const note_tag_t::note_values_t& e ) { + return e.type == type; + } ); + } + + if ( name_group != std::end( note_tag_table ) && + type_value != name_group->values.end() ) { + out << DUMP_STR_FORMAT( 12 ) << name_group->name << " " + << DUMP_HEX0x_FORMAT( 8 ) << descsz << " " + << type_value->type_str << " (" << type_value->description + << ")"; + } + else { + out << DUMP_STR_FORMAT( 12 ) << name << " " + << DUMP_HEX0x_FORMAT( 8 ) << descsz << " " + << DUMP_HEX0x_FORMAT( 8 ) << type; + } + + if ( descsz != 0 ) { + for ( Elf_Word i = 0; i < descsz; ++i ) { + if ( i % 16 == 0 ) { + out << std::endl << " "; + } + out << DUMP_HEX_FORMAT( 2 ) + << ( std::uint32_t )( (std::uint8_t*)( desc ) )[i]; + } + } + } + + //------------------------------------------------------------------------------ + // Dumps the module information + static void modinfo( std::ostream& out, const elfio& reader ) + { + for ( const auto& sec : reader.sections ) { // For all sections + if ( ".modinfo" == sec->get_name() ) { // Look for the section + out << "Section .modinfo" << std::endl; + + const_modinfo_section_accessor modinfo( sec.get() ); + for ( Elf_Word i = 0; i < modinfo.get_attribute_num(); i++ ) { + std::string field; + std::string value; + if ( modinfo.get_attribute( i, field, value ) ) { + out << " " << std::setw( 20 ) << field + << std::setw( 0 ) << " = " << value << std::endl; + } + } + + out << std::endl; + break; + } + } + } + + //------------------------------------------------------------------------------ + // Dumps the dynamic tags information + static void dynamic_tags( std::ostream& out, + const elfio& reader, + bool name_only = false ) + { + for ( const auto& sec : reader.sections ) { // For all sections + if ( SHT_DYNAMIC == sec->get_type() ) { + dynamic_section_accessor dynamic( reader, sec.get() ); + + Elf_Xword dyn_no = dynamic.get_entries_num(); + if ( dyn_no == 0 ) + continue; + if ( !name_only ) { + out << "Dynamic section (" << sec->get_name() << ")" + << std::endl; + out << "[ Nr ] Tag Name/Value" << std::endl; + } + for ( Elf_Xword i = 0; i < dyn_no; ++i ) { + Elf_Xword tag = 0; + Elf_Xword value = 0; + std::string str; + dynamic.get_entry( i, tag, value, str ); + dynamic_tag( out, i, tag, value, str, reader.get_class(), + name_only ); + if ( DT_NULL == tag ) { + break; + } + } + if ( !name_only ) { + out << std::endl; + } + } + } + } + + //------------------------------------------------------------------------------ + // Dumps a single dynamic tag information + static void dynamic_tag( std::ostream& out, + Elf_Xword no, + Elf_Xword tag, + Elf_Xword value, + const std::string& str, + unsigned int /*elf_class*/, + bool name_only = false ) + { + if ( name_only ) { + if ( str.empty() || tag != DT_NEEDED ) { + return; + } + out << str << std::endl; + return; + } + out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " + << DUMP_STR_FORMAT( 16 ) << str_dynamic_tag( tag ) << " "; + if ( str.empty() ) { + out << DUMP_HEX0x_FORMAT( 16 ) << value << " "; + } + else { + out << DUMP_STR_FORMAT( 32 ) << str << " "; + } + out << std::endl; + } + + //------------------------------------------------------------------------------ + // Dumps the section data + static void section_data( std::ostream& out, const section* sec ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + out << sec->get_name() << std::endl; + const char* pdata = sec->get_data(); + if ( pdata ) { + ELFIO::Elf_Xword i; + for ( i = 0; i < std::min( sec->get_size(), MAX_DATA_ENTRIES ); + ++i ) { + if ( i % 16 == 0 ) { + out << "[" << DUMP_HEX0x_FORMAT( 8 ) << i << "]"; + } + + out << " " << DUMP_HEX0x_FORMAT( 2 ) + << ( pdata[i] & 0x000000FF ); + + if ( i % 16 == 15 ) { + out << std::endl; + } + } + if ( i % 16 != 0 ) { + out << std::endl; + } + + out.flags( original_flags ); + } + + return; + } + + //------------------------------------------------------------------------------ + // Dumps all sections data + static void section_datas( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.sections.size(); + + if ( n == 0 ) { + return; + } + + out << "Section Data:" << std::endl; + + for ( Elf_Half i = 1; i < n; ++i ) { // For all sections + const section* sec = reader.sections[i]; + if ( sec->get_type() == SHT_NOBITS ) { + continue; + } + section_data( out, sec ); + } + + out << std::endl; + } + + //------------------------------------------------------------------------------ + // Dumps the segment data + static void + segment_data( std::ostream& out, Elf_Half no, const segment* seg ) + { + std::ios_base::fmtflags original_flags = out.flags(); + + out << "Segment # " << no << std::endl; + const char* pdata = seg->get_data(); + if ( pdata ) { + ELFIO::Elf_Xword i; + for ( i = 0; i < std::min( seg->get_file_size(), MAX_DATA_ENTRIES ); + ++i ) { + if ( i % 16 == 0 ) { + out << "[" << DUMP_HEX0x_FORMAT( 8 ) << i << "]"; + } + + out << " " << DUMP_HEX0x_FORMAT( 2 ) + << ( pdata[i] & 0x000000FF ); + + if ( i % 16 == 15 ) { + out << std::endl; + } + } + if ( i % 16 != 0 ) { + out << std::endl; + } + + out.flags( original_flags ); + } + + return; + } + + //------------------------------------------------------------------------------ + // Dumps all segments data + static void segment_datas( std::ostream& out, const elfio& reader ) + { + Elf_Half n = reader.segments.size(); + + if ( n == 0 ) { + return; + } + + out << "Segment Data:" << std::endl; + + for ( Elf_Half i = 0; i < n; ++i ) { // For all sections + const segment* seg = reader.segments[i]; + segment_data( out, i, seg ); + } + + out << std::endl; + } + +//------------------------------------------------------------------------------ +#define STR_FUNC_TABLE( name ) \ + template static std::string str_##name( const T key ) \ + { \ + return format_assoc( name##_table, key ); \ + } + + STR_FUNC_TABLE( class ) + STR_FUNC_TABLE( endian ) + STR_FUNC_TABLE( version ) + STR_FUNC_TABLE( os_abi ) + STR_FUNC_TABLE( type ) + STR_FUNC_TABLE( machine ) + STR_FUNC_TABLE( section_type ) + STR_FUNC_TABLE( segment_type ) + STR_FUNC_TABLE( segment_flag ) + STR_FUNC_TABLE( symbol_bind ) + STR_FUNC_TABLE( symbol_type ) + STR_FUNC_TABLE( dynamic_tag ) + +#undef STR_FUNC_TABLE + + private: + //------------------------------------------------------------------------------ + template + std::string static find_value_in_table( const T& table, const K& key ) + { + std::string res = "?"; + for ( unsigned int i = 0; i < sizeof( table ) / sizeof( table[0] ); + ++i ) { + if ( table[i].key == key ) { + res = table[i].str; + break; + } + } + + return res; + } + + //------------------------------------------------------------------------------ + template + static std::string format_assoc( const T& table, const K& key ) + { + std::string str = find_value_in_table( table, key ); + if ( str == "?" ) { + std::ostringstream oss; + oss << str << " (0x" << std::hex << key << ")"; + str = oss.str(); + } + + return str; + } + + //------------------------------------------------------------------------------ + template + static std::string format_assoc( const T& table, const char key ) + { + return format_assoc( table, (const int)key ); + } + + //------------------------------------------------------------------------------ + static std::string section_flags( Elf_Xword flags ) + { + std::string ret = ""; + if ( flags & SHF_WRITE ) { + ret += "W"; + } + if ( flags & SHF_ALLOC ) { + ret += "A"; + } + if ( flags & SHF_EXECINSTR ) { + ret += "X"; + } + if ( flags & SHF_MERGE ) { + ret += "M"; + } + if ( flags & SHF_STRINGS ) { + ret += "S"; + } + if ( flags & SHF_INFO_LINK ) { + ret += "I"; + } + if ( flags & SHF_LINK_ORDER ) { + ret += "L"; + } + if ( flags & SHF_OS_NONCONFORMING ) { + ret += "O"; + } + if ( flags & SHF_GROUP ) { + ret += "G"; + } + if ( flags & SHF_TLS ) { + ret += "T"; + } + if ( flags & SHF_COMPRESSED ) { + ret += "C"; + } + if ( flags & SHF_EXCLUDE ) { + ret += "E"; + } + if ( flags & SHF_GNU_MBIND ) { + ret += "D"; + } + + return ret; + } + +#undef DUMP_DEC_FORMAT +#undef DUMP_HEX0x_FORMAT +#undef DUMP_STR_FORMAT +}; // class dump +} // namespace ELFIO + +#endif // ELFIO_DUMP_HPP diff --git a/vyld/elfio/elfio_dynamic.hpp b/vyld/elfio/elfio_dynamic.hpp new file mode 100644 index 0000000..83c55bb --- /dev/null +++ b/vyld/elfio/elfio_dynamic.hpp @@ -0,0 +1,300 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_DYNAMIC_HPP +#define ELFIO_DYNAMIC_HPP + +#include + +namespace ELFIO { + +//------------------------------------------------------------------------------ +// Template class for accessing dynamic sections +template class dynamic_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + // Constructor + explicit dynamic_section_accessor_template( const elfio& elf_file, + S* section ) + : elf_file( elf_file ), dynamic_section( section ), entries_num( 0 ) + { + } + + //------------------------------------------------------------------------------ + // Returns the number of entries in the dynamic section + Elf_Xword get_entries_num() const + { + size_t needed_entry_size = -1; + if ( elf_file.get_class() == ELFCLASS32 ) { + needed_entry_size = sizeof( Elf32_Dyn ); + } + else { + needed_entry_size = sizeof( Elf64_Dyn ); + } + + if ( ( 0 == entries_num ) && + ( 0 != dynamic_section->get_entry_size() && + dynamic_section->get_entry_size() >= needed_entry_size ) ) { + entries_num = + dynamic_section->get_size() / dynamic_section->get_entry_size(); + Elf_Xword i; + Elf_Xword tag = DT_NULL; + Elf_Xword value = 0; + std::string str; + for ( i = 0; i < entries_num; i++ ) { + get_entry( i, tag, value, str ); + if ( tag == DT_NULL ) + break; + } + entries_num = std::min( entries_num, i + 1 ); + } + + return entries_num; + } + + //------------------------------------------------------------------------------ + // Retrieves an entry from the dynamic section + bool get_entry( Elf_Xword index, + Elf_Xword& tag, + Elf_Xword& value, + std::string& str ) const + { + if ( index >= get_entries_num() ) { // Is index valid + return false; + } + + if ( elf_file.get_class() == ELFCLASS32 ) { + generic_get_entry_dyn( index, tag, value ); + } + else { + generic_get_entry_dyn( index, tag, value ); + } + + // If the tag has a string table reference - prepare the string + if ( tag == DT_NEEDED || tag == DT_SONAME || tag == DT_RPATH || + tag == DT_RUNPATH ) { + string_section_accessor strsec( + elf_file.sections[get_string_table_index()] ); + const char* result = strsec.get_string( (Elf_Word)value ); + if ( nullptr == result ) { + str.clear(); + return false; + } + str = result; + } + else { + str.clear(); + } + + return true; + } + + //------------------------------------------------------------------------------ + // Adds an entry to the dynamic section + void add_entry( Elf_Xword tag, Elf_Xword value ) + { + if ( elf_file.get_class() == ELFCLASS32 ) { + generic_add_entry_dyn( tag, value ); + } + else { + generic_add_entry_dyn( tag, value ); + } + } + + //------------------------------------------------------------------------------ + // Adds an entry with a string value to the dynamic section + void add_entry( Elf_Xword tag, const std::string& str ) + { + string_section_accessor strsec( + elf_file.sections[get_string_table_index()] ); + Elf_Xword value = strsec.add_string( str ); + add_entry( tag, value ); + } + + private: + //------------------------------------------------------------------------------ + // Returns the index of the string table + Elf_Half get_string_table_index() const + { + return (Elf_Half)dynamic_section->get_link(); + } + + //------------------------------------------------------------------------------ + // Retrieves a generic entry from the dynamic section + template + void generic_get_entry_dyn( Elf_Xword index, + Elf_Xword& tag, + Elf_Xword& value ) const + { + const auto& convertor = elf_file.get_convertor(); + + // Check unusual case when dynamic section has no data + if ( dynamic_section->get_data() == nullptr || + dynamic_section->get_entry_size() < sizeof( T ) ) { + tag = DT_NULL; + value = 0; + return; + } + + // Check for integer overflow in size calculation + if ( index > ( dynamic_section->get_size() / + dynamic_section->get_entry_size() ) - + 1 ) { + tag = DT_NULL; + value = 0; + return; + } + + // Check for integer overflow in pointer arithmetic + Elf_Xword offset = index * dynamic_section->get_entry_size(); + if ( offset > dynamic_section->get_size() - sizeof( T ) ) { + tag = DT_NULL; + value = 0; + return; + } + + const T* pEntry = + reinterpret_cast( dynamic_section->get_data() + offset ); + tag = ( *convertor )( pEntry->d_tag ); + switch ( tag ) { + case DT_NULL: + case DT_SYMBOLIC: + case DT_TEXTREL: + case DT_BIND_NOW: + value = 0; + break; + case DT_NEEDED: + case DT_PLTRELSZ: + case DT_RELASZ: + case DT_RELAENT: + case DT_STRSZ: + case DT_SYMENT: + case DT_SONAME: + case DT_RPATH: + case DT_RELSZ: + case DT_RELENT: + case DT_PLTREL: + case DT_INIT_ARRAYSZ: + case DT_FINI_ARRAYSZ: + case DT_RUNPATH: + case DT_FLAGS: + case DT_PREINIT_ARRAYSZ: + value = ( *convertor )( pEntry->d_un.d_val ); + break; + case DT_PLTGOT: + case DT_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_RELA: + case DT_INIT: + case DT_FINI: + case DT_REL: + case DT_DEBUG: + case DT_JMPREL: + case DT_INIT_ARRAY: + case DT_FINI_ARRAY: + case DT_PREINIT_ARRAY: + default: + value = ( *convertor )( pEntry->d_un.d_ptr ); + break; + } + } + + //------------------------------------------------------------------------------ + // Adds a generic entry to the dynamic section + template + void generic_add_entry_dyn( Elf_Xword tag, Elf_Xword value ) + { + const auto& convertor = elf_file.get_convertor(); + + T entry; + + switch ( tag ) { + case DT_NULL: + case DT_SYMBOLIC: + case DT_TEXTREL: + case DT_BIND_NOW: + entry.d_un.d_val = + ( *convertor )( decltype( entry.d_un.d_val )( 0 ) ); + break; + case DT_NEEDED: + case DT_PLTRELSZ: + case DT_RELASZ: + case DT_RELAENT: + case DT_STRSZ: + case DT_SYMENT: + case DT_SONAME: + case DT_RPATH: + case DT_RELSZ: + case DT_RELENT: + case DT_PLTREL: + case DT_INIT_ARRAYSZ: + case DT_FINI_ARRAYSZ: + case DT_RUNPATH: + case DT_FLAGS: + case DT_PREINIT_ARRAYSZ: + entry.d_un.d_val = + ( *convertor )( decltype( entry.d_un.d_val )( value ) ); + break; + case DT_PLTGOT: + case DT_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_RELA: + case DT_INIT: + case DT_FINI: + case DT_REL: + case DT_DEBUG: + case DT_JMPREL: + case DT_INIT_ARRAY: + case DT_FINI_ARRAY: + case DT_PREINIT_ARRAY: + default: + entry.d_un.d_ptr = + ( *convertor )( decltype( entry.d_un.d_val )( value ) ); + break; + } + + entry.d_tag = ( *convertor )( decltype( entry.d_tag )( tag ) ); + + dynamic_section->append_data( reinterpret_cast( &entry ), + sizeof( entry ) ); + } + + private: + // Reference to the ELF file + const elfio& elf_file; + // Pointer to the dynamic section + S* dynamic_section; + // Number of entries in the dynamic section + mutable Elf_Xword entries_num; +}; + +// Type aliases for dynamic section accessors +using dynamic_section_accessor = dynamic_section_accessor_template
; +using const_dynamic_section_accessor = + dynamic_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_DYNAMIC_HPP diff --git a/vyld/elfio/elfio_header.hpp b/vyld/elfio/elfio_header.hpp new file mode 100644 index 0000000..80c4b3f --- /dev/null +++ b/vyld/elfio/elfio_header.hpp @@ -0,0 +1,192 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELF_HEADER_HPP +#define ELF_HEADER_HPP + +#include + +namespace ELFIO { + +/** + * @class elf_header + * @brief Abstract base class for ELF header. + */ +class elf_header +{ + public: + /** + * @brief Virtual destructor. + */ + virtual ~elf_header() = default; + + /** + * @brief Load ELF header from stream. + * @param stream Input stream. + * @return True if successful, false otherwise. + */ + virtual bool load( std::istream& stream ) = 0; + + /** + * @brief Save ELF header to stream. + * @param stream Output stream. + * @return True if successful, false otherwise. + */ + virtual bool save( std::ostream& stream ) const = 0; + + // ELF header functions + ELFIO_GET_ACCESS_DECL( unsigned char, class ); + ELFIO_GET_ACCESS_DECL( unsigned char, elf_version ); + ELFIO_GET_ACCESS_DECL( unsigned char, encoding ); + ELFIO_GET_ACCESS_DECL( Elf_Half, header_size ); + ELFIO_GET_ACCESS_DECL( Elf_Half, section_entry_size ); + ELFIO_GET_ACCESS_DECL( Elf_Half, segment_entry_size ); + + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, version ); + ELFIO_GET_SET_ACCESS_DECL( unsigned char, os_abi ); + ELFIO_GET_SET_ACCESS_DECL( unsigned char, abi_version ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Half, type ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Half, machine ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags ); + ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, entry ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Half, sections_num ); + ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, sections_offset ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Half, segments_num ); + ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, segments_offset ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Half, section_name_str_index ); +}; + +/** + * @struct elf_header_impl_types + * @brief Template specialization for ELF header implementation types. + */ +template struct elf_header_impl_types; +template <> struct elf_header_impl_types +{ + using Phdr_type = Elf32_Phdr; + using Shdr_type = Elf32_Shdr; + static const unsigned char file_class = ELFCLASS32; +}; +template <> struct elf_header_impl_types +{ + using Phdr_type = Elf64_Phdr; + using Shdr_type = Elf64_Shdr; + static const unsigned char file_class = ELFCLASS64; +}; + +/** + * @class elf_header_impl + * @brief Template class for ELF header implementation. + */ +template class elf_header_impl : public elf_header +{ + public: + /** + * @brief Constructor. + * @param convertor Endianness convertor. + * @param encoding Encoding type. + * @param translator Address translator. + */ + elf_header_impl( std::shared_ptr convertor, + unsigned char encoding, + std::shared_ptr translator ) + : convertor( convertor ), translator( translator ) + { + header.e_ident[EI_MAG0] = ELFMAG0; + header.e_ident[EI_MAG1] = ELFMAG1; + header.e_ident[EI_MAG2] = ELFMAG2; + header.e_ident[EI_MAG3] = ELFMAG3; + header.e_ident[EI_CLASS] = elf_header_impl_types::file_class; + header.e_ident[EI_DATA] = encoding; + header.e_ident[EI_VERSION] = EV_CURRENT; + header.e_version = ( *convertor )( (Elf_Word)EV_CURRENT ); + header.e_ehsize = ( sizeof( header ) ); + header.e_ehsize = ( *convertor )( header.e_ehsize ); + header.e_shstrndx = ( *convertor )( (Elf_Half)1 ); + header.e_phentsize = + sizeof( typename elf_header_impl_types::Phdr_type ); + header.e_shentsize = + sizeof( typename elf_header_impl_types::Shdr_type ); + header.e_phentsize = ( *convertor )( header.e_phentsize ); + header.e_shentsize = ( *convertor )( header.e_shentsize ); + } + + /** + * @brief Load ELF header from stream. + * @param stream Input stream. + * @return True if successful, false otherwise. + */ + bool load( std::istream& stream ) override + { + stream.seekg( ( *translator )[0] ); + stream.read( reinterpret_cast( &header ), sizeof( header ) ); + + return ( stream.gcount() == sizeof( header ) ); + } + + /** + * @brief Save ELF header to stream. + * @param stream Output stream. + * @return True if successful, false otherwise. + */ + bool save( std::ostream& stream ) const override + { + stream.seekp( ( *translator )[0] ); + stream.write( reinterpret_cast( &header ), + sizeof( header ) ); + + return stream.good(); + } + + //------------------------------------------------------------------------------ + // ELF header functions + ELFIO_GET_ACCESS( unsigned char, class, header.e_ident[EI_CLASS] ); + ELFIO_GET_ACCESS( unsigned char, elf_version, header.e_ident[EI_VERSION] ); + ELFIO_GET_ACCESS( unsigned char, encoding, header.e_ident[EI_DATA] ); + ELFIO_GET_ACCESS( Elf_Half, header_size, header.e_ehsize ); + ELFIO_GET_ACCESS( Elf_Half, section_entry_size, header.e_shentsize ); + ELFIO_GET_ACCESS( Elf_Half, segment_entry_size, header.e_phentsize ); + + ELFIO_GET_SET_ACCESS( Elf_Word, version, header.e_version ); + ELFIO_GET_SET_ACCESS( unsigned char, os_abi, header.e_ident[EI_OSABI] ); + ELFIO_GET_SET_ACCESS( unsigned char, + abi_version, + header.e_ident[EI_ABIVERSION] ); + ELFIO_GET_SET_ACCESS( Elf_Half, type, header.e_type ); + ELFIO_GET_SET_ACCESS( Elf_Half, machine, header.e_machine ); + ELFIO_GET_SET_ACCESS( Elf_Word, flags, header.e_flags ); + ELFIO_GET_SET_ACCESS( Elf_Half, section_name_str_index, header.e_shstrndx ); + ELFIO_GET_SET_ACCESS( Elf64_Addr, entry, header.e_entry ); + ELFIO_GET_SET_ACCESS( Elf_Half, sections_num, header.e_shnum ); + ELFIO_GET_SET_ACCESS( Elf64_Off, sections_offset, header.e_shoff ); + ELFIO_GET_SET_ACCESS( Elf_Half, segments_num, header.e_phnum ); + ELFIO_GET_SET_ACCESS( Elf64_Off, segments_offset, header.e_phoff ); + + private: + T header = {}; + std::shared_ptr convertor = nullptr; + std::shared_ptr translator = nullptr; +}; + +} // namespace ELFIO + +#endif // ELF_HEADER_HPP diff --git a/vyld/elfio/elfio_modinfo.hpp b/vyld/elfio/elfio_modinfo.hpp new file mode 100644 index 0000000..ee7fe2d --- /dev/null +++ b/vyld/elfio/elfio_modinfo.hpp @@ -0,0 +1,168 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_MODINFO_HPP +#define ELFIO_MODINFO_HPP + +#include +#include + +namespace ELFIO { + +//------------------------------------------------------------------------------ +/** + * @class modinfo_section_accessor_template + * @brief A template class to access modinfo section. + * + * @tparam S The section type. + */ +template class modinfo_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + /** + * @brief Construct a new modinfo section accessor template object. + * + * @param section The section to be accessed. + */ + explicit modinfo_section_accessor_template( S* section ) + : modinfo_section( section ) + { + process_section(); + } + + //------------------------------------------------------------------------------ + /** + * @brief Get the number of attributes. + * + * @return Elf_Word The number of attributes. + */ + Elf_Word get_attribute_num() const { return (Elf_Word)content.size(); } + + //------------------------------------------------------------------------------ + /** + * @brief Get the attribute by index. + * + * @param no The index of the attribute. + * @param field The field name of the attribute. + * @param value The value of the attribute. + * @return true If the attribute is found. + * @return false If the attribute is not found. + */ + bool + get_attribute( Elf_Word no, std::string& field, std::string& value ) const + { + if ( no < content.size() ) { + field = content[no].first; + value = content[no].second; + return true; + } + + return false; + } + + //------------------------------------------------------------------------------ + /** + * @brief Get the attribute by field name. + * + * @param field_name The field name of the attribute. + * @param value The value of the attribute. + * @return true If the attribute is found. + * @return false If the attribute is not found. + */ + bool get_attribute( const std::string_view& field_name, + std::string& value ) const + { + for ( const auto& [first, second] : content ) { + if ( field_name == first ) { + value = second; + return true; + } + } + + return false; + } + + //------------------------------------------------------------------------------ + /** + * @brief Add a new attribute. + * + * @param field The field name of the attribute. + * @param value The value of the attribute. + * @return Elf_Word The position of the new attribute. + */ + Elf_Word add_attribute( const std::string& field, const std::string& value ) + { + Elf_Word current_position = 0; + + if ( modinfo_section ) { + // Strings are addeded to the end of the current section data + current_position = (Elf_Word)modinfo_section->get_size(); + + std::string attribute = field + "=" + value; + + modinfo_section->append_data( attribute + '\0' ); + content.emplace_back( field, value ); + } + + return current_position; + } + + //------------------------------------------------------------------------------ + private: + /** + * @brief Process the section to extract attributes. + */ + void process_section() + { + const char* pdata = modinfo_section->get_data(); + if ( pdata ) { + ELFIO::Elf_Xword i = 0; + while ( i < modinfo_section->get_size() ) { + while ( i < modinfo_section->get_size() && !pdata[i] ) + i++; + if ( i < modinfo_section->get_size() ) { + std::string info = pdata + i; + size_t loc = info.find( '=' ); + content.emplace_back( info.substr( 0, loc ), + info.substr( loc + 1 ) ); + + i += info.length(); + } + } + } + } + + //------------------------------------------------------------------------------ + private: + S* modinfo_section; ///< The section to be accessed. + std::vector> + content; ///< The list of attributes. +}; + +using modinfo_section_accessor = modinfo_section_accessor_template
; +using const_modinfo_section_accessor = + modinfo_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_MODINFO_HPP diff --git a/vyld/elfio/elfio_note.hpp b/vyld/elfio/elfio_note.hpp new file mode 100644 index 0000000..c246fb5 --- /dev/null +++ b/vyld/elfio/elfio_note.hpp @@ -0,0 +1,208 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_NOTE_HPP +#define ELFIO_NOTE_HPP + +namespace ELFIO { + +//------------------------------------------------------------------------------ +// There are discrepancies in documentations. SCO documentation +// (http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section) +// requires 8 byte entries alignment for 64-bit ELF file, +// but Oracle's definition uses the same structure +// for 32-bit and 64-bit formats. +// (https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-18048.html) +// +// It looks like EM_X86_64 Linux implementation is similar to Oracle's +// definition. Therefore, the same alignment works for both formats +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +//! \class note_section_accessor_template +//! \brief Class for accessing note section data +template +class note_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + //! \brief Constructor + //! \param elf_file Reference to the ELF file + //! \param section Pointer to the section + explicit note_section_accessor_template( const elfio& elf_file, S* section ) + : elf_file( elf_file ), notes( section ) + { + process_section(); + } + + //------------------------------------------------------------------------------ + //! \brief Get the number of notes + //! \return Number of notes + Elf_Word get_notes_num() const + { + return (Elf_Word)note_start_positions.size(); + } + + //------------------------------------------------------------------------------ + //! \brief Get a note + //! \param index Index of the note + //! \param type Type of the note + //! \param name Name of the note + //! \param desc Pointer to the descriptor + //! \param descSize Size of the descriptor + //! \return True if successful, false otherwise + bool get_note( Elf_Word index, + Elf_Word& type, + std::string& name, + char*& desc, + Elf_Word& descSize ) const + { + if ( index >= ( notes->*F_get_size )() ) { + return false; + } + + const char* pData = notes->get_data() + note_start_positions[index]; + int align = sizeof( Elf_Word ); + + const auto& convertor = elf_file.get_convertor(); + type = + ( *convertor )( *(const Elf_Word*)( pData + 2 * (size_t)align ) ); + Elf_Word namesz = ( *convertor )( *(const Elf_Word*)( pData ) ); + descSize = + ( *convertor )( *(const Elf_Word*)( pData + sizeof( namesz ) ) ); + + Elf_Xword max_name_size = + ( notes->*F_get_size )() - note_start_positions[index]; + if ( namesz < 1 || namesz > max_name_size || + (Elf_Xword)namesz + descSize > max_name_size ) { + return false; + } + name.assign( pData + 3 * (size_t)align, namesz - 1 ); + if ( 0 == descSize ) { + desc = nullptr; + } + else { + desc = const_cast( pData + 3 * (size_t)align + + ( ( namesz + align - 1 ) / align ) * + (size_t)align ); + } + + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Add a note + //! \param type Type of the note + //! \param name Name of the note + //! \param desc Pointer to the descriptor + //! \param descSize Size of the descriptor + void add_note( Elf_Word type, + const std::string& name, + const char* desc, + Elf_Word descSize ) + { + const auto& convertor = elf_file.get_convertor(); + + int align = sizeof( Elf_Word ); + Elf_Word nameLen = (Elf_Word)name.size() + 1; + Elf_Word nameLenConv = ( *convertor )( nameLen ); + std::string buffer( reinterpret_cast( &nameLenConv ), align ); + Elf_Word descSizeConv = ( *convertor )( descSize ); + + buffer.append( reinterpret_cast( &descSizeConv ), align ); + type = ( *convertor )( type ); + buffer.append( reinterpret_cast( &type ), align ); + buffer.append( name ); + buffer.append( 1, '\x00' ); + const char pad[] = { '\0', '\0', '\0', '\0' }; + if ( nameLen % align != 0 ) { + buffer.append( pad, (size_t)align - nameLen % align ); + } + if ( desc != nullptr && descSize != 0 ) { + buffer.append( desc, descSize ); + if ( descSize % align != 0 ) { + buffer.append( pad, (size_t)align - descSize % align ); + } + } + + note_start_positions.emplace_back( ( notes->*F_get_size )() ); + notes->append_data( buffer ); + } + + private: + //------------------------------------------------------------------------------ + //! \brief Process the section to extract note start positions + void process_section() + { + const auto& convertor = elf_file.get_convertor(); + const char* data = notes->get_data(); + Elf_Xword size = ( notes->*F_get_size )(); + Elf_Xword current = 0; + + note_start_positions.clear(); + + // Is it empty? + if ( nullptr == data || 0 == size ) { + return; + } + + Elf_Word align = sizeof( Elf_Word ); + while ( current + (Elf_Xword)3 * align <= size ) { + Elf_Word namesz = + ( *convertor )( *(const Elf_Word*)( data + current ) ); + Elf_Word descsz = ( *convertor )( + *(const Elf_Word*)( data + current + sizeof( namesz ) ) ); + Elf_Word advance = + (Elf_Xword)3 * sizeof( Elf_Word ) + + ( ( namesz + align - 1 ) / align ) * (Elf_Xword)align + + ( ( descsz + align - 1 ) / align ) * (Elf_Xword)align; + if ( namesz < size && descsz < size && current + advance <= size ) { + note_start_positions.emplace_back( current ); + } + else { + break; + } + + current += advance; + } + } + + //------------------------------------------------------------------------------ + private: + const elfio& elf_file; //!< Reference to the ELF file + S* notes; //!< Pointer to the section or segment + std::vector + note_start_positions; //!< Vector of note start positions +}; + +using note_section_accessor = + note_section_accessor_template; +using const_note_section_accessor = + note_section_accessor_template; +using note_segment_accessor = + note_section_accessor_template; +using const_note_segment_accessor = + note_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_NOTE_HPP diff --git a/vyld/elfio/elfio_relocation.hpp b/vyld/elfio/elfio_relocation.hpp new file mode 100644 index 0000000..e6aa963 --- /dev/null +++ b/vyld/elfio/elfio_relocation.hpp @@ -0,0 +1,596 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_RELOCATION_HPP +#define ELFIO_RELOCATION_HPP + +namespace ELFIO { + +template struct get_sym_and_type; +template <> struct get_sym_and_type +{ + //------------------------------------------------------------------------------ + //! \brief Get the symbol from the relocation info + //! \param info Relocation info + //! \return Symbol + static int get_r_sym( Elf_Xword info ) + { + return ELF32_R_SYM( (Elf_Word)info ); + } + //------------------------------------------------------------------------------ + //! \brief Get the type from the relocation info + //! \param info Relocation info + //! \return Type + static int get_r_type( Elf_Xword info ) + { + return ELF32_R_TYPE( (Elf_Word)info ); + } +}; +template <> struct get_sym_and_type +{ + //------------------------------------------------------------------------------ + //! \brief Get the symbol from the relocation info + //! \param info Relocation info + //! \return Symbol + static int get_r_sym( Elf_Xword info ) + { + return ELF32_R_SYM( (Elf_Word)info ); + } + //------------------------------------------------------------------------------ + //! \brief Get the type from the relocation info + //! \param info Relocation info + //! \return Type + static int get_r_type( Elf_Xword info ) + { + return ELF32_R_TYPE( (Elf_Word)info ); + } +}; +template <> struct get_sym_and_type +{ + //------------------------------------------------------------------------------ + //! \brief Get the symbol from the relocation info + //! \param info Relocation info + //! \return Symbol + static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); } + //------------------------------------------------------------------------------ + //! \brief Get the type from the relocation info + //! \param info Relocation info + //! \return Type + static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); } +}; +template <> struct get_sym_and_type +{ + //------------------------------------------------------------------------------ + //! \brief Get the symbol from the relocation info + //! \param info Relocation info + //! \return Symbol + static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); } + //------------------------------------------------------------------------------ + //! \brief Get the type from the relocation info + //! \param info Relocation info + //! \return Type + static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); } +}; + +//------------------------------------------------------------------------------ +//! \class relocation_section_accessor_template +//! \brief Class for accessing relocation section data +template class relocation_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + //! \brief Constructor + //! \param elf_file Reference to the ELF file + //! \param section Pointer to the section + explicit relocation_section_accessor_template( const elfio& elf_file, + S* section ) + : elf_file( elf_file ), relocation_section( section ) + { + } + + //------------------------------------------------------------------------------ + //! \brief Get the number of entries + //! \return Number of entries + Elf_Xword get_entries_num() const + { + Elf_Xword nRet = 0; + + if ( 0 != relocation_section->get_entry_size() ) { + nRet = relocation_section->get_size() / + relocation_section->get_entry_size(); + } + + return nRet; + } + + //------------------------------------------------------------------------------ + //! \brief Get an entry + //! \param index Index of the entry + //! \param offset Offset of the entry + //! \param symbol Symbol of the entry + //! \param type Type of the entry + //! \param addend Addend of the entry + //! \return True if successful, false otherwise + bool get_entry( Elf_Xword index, + Elf64_Addr& offset, + Elf_Word& symbol, + unsigned& type, + Elf_Sxword& addend ) const + { + if ( index >= get_entries_num() ) { // Is index valid + return false; + } + + if ( elf_file.get_class() == ELFCLASS32 ) { + if ( SHT_REL == relocation_section->get_type() ) { + return generic_get_entry_rel( index, offset, symbol, + type, addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + return generic_get_entry_rela( + index, offset, symbol, type, addend ); + } + } + else { + if ( SHT_REL == relocation_section->get_type() ) { + return generic_get_entry_rel( index, offset, symbol, + type, addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + return generic_get_entry_rela( + index, offset, symbol, type, addend ); + } + } + // Unknown relocation section type. + return false; + } + + //------------------------------------------------------------------------------ + //! \brief Get an entry with additional information + //! \param index Index of the entry + //! \param offset Offset of the entry + //! \param symbolValue Value of the symbol + //! \param symbolName Name of the symbol + //! \param type Type of the entry + //! \param addend Addend of the entry + //! \param calcValue Calculated value + //! \return True if successful, false otherwise + bool get_entry( Elf_Xword index, + Elf64_Addr& offset, + Elf64_Addr& symbolValue, + std::string& symbolName, + unsigned& type, + Elf_Sxword& addend, + Elf_Sxword& calcValue ) const + { + // Do regular job + Elf_Word symbol = 0; + bool ret = get_entry( index, offset, symbol, type, addend ); + + // Find the symbol + Elf_Xword size; + unsigned char bind; + unsigned char symbolType; + Elf_Half section; + unsigned char other; + + symbol_section_accessor symbols( + elf_file, elf_file.sections[get_symbol_table_index()] ); + ret = ret && symbols.get_symbol( symbol, symbolName, symbolValue, size, + bind, symbolType, section, other ); + + if ( ret ) { // Was it successful? + switch ( type ) { + case R_386_NONE: // none + calcValue = 0; + break; + case R_386_32: // S + A + calcValue = symbolValue + addend; + break; + case R_386_PC32: // S + A - P + calcValue = symbolValue + addend - offset; + break; + case R_386_GOT32: // G + A - P + calcValue = 0; + break; + case R_386_PLT32: // L + A - P + calcValue = 0; + break; + case R_386_COPY: // none + calcValue = 0; + break; + case R_386_GLOB_DAT: // S + case R_386_JMP_SLOT: // S + calcValue = symbolValue; + break; + case R_386_RELATIVE: // B + A + calcValue = addend; + break; + case R_386_GOTOFF: // S + A - GOT + calcValue = 0; + break; + case R_386_GOTPC: // GOT + A - P + calcValue = 0; + break; + default: // Not recognized symbol! + calcValue = 0; + break; + } + } + + return ret; + } + + //------------------------------------------------------------------------------ + //! \brief Set an entry + //! \param index Index of the entry + //! \param offset Offset of the entry + //! \param symbol Symbol of the entry + //! \param type Type of the entry + //! \param addend Addend of the entry + //! \return True if successful, false otherwise + bool set_entry( Elf_Xword index, + Elf64_Addr offset, + Elf_Word symbol, + unsigned type, + Elf_Sxword addend ) + { + if ( index >= get_entries_num() ) { // Is index valid + return false; + } + + if ( elf_file.get_class() == ELFCLASS32 ) { + if ( SHT_REL == relocation_section->get_type() ) { + generic_set_entry_rel( index, offset, symbol, type, + addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + generic_set_entry_rela( index, offset, symbol, type, + addend ); + } + } + else { + if ( SHT_REL == relocation_section->get_type() ) { + generic_set_entry_rel( index, offset, symbol, type, + addend ); + } + else if ( SHT_RELA == relocation_section->get_type() ) { + generic_set_entry_rela( index, offset, symbol, type, + addend ); + } + } + + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Add an entry + //! \param offset Offset of the entry + //! \param info Information of the entry + void add_entry( Elf64_Addr offset, Elf_Xword info ) + { + if ( elf_file.get_class() == ELFCLASS32 ) { + generic_add_entry( offset, info ); + } + else { + generic_add_entry( offset, info ); + } + } + + //------------------------------------------------------------------------------ + //! \brief Add an entry + //! \param offset Offset of the entry + //! \param symbol Symbol of the entry + //! \param type Type of the entry + void add_entry( Elf64_Addr offset, Elf_Word symbol, unsigned type ) + { + Elf_Xword info; + if ( elf_file.get_class() == ELFCLASS32 ) { + info = ELF32_R_INFO( (Elf_Xword)symbol, type ); + } + else { + info = ELF64_R_INFO( (Elf_Xword)symbol, type ); + } + + add_entry( offset, info ); + } + + //------------------------------------------------------------------------------ + //! \brief Add an entry + //! \param offset Offset of the entry + //! \param info Information of the entry + //! \param addend Addend of the entry + void add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend ) + { + if ( elf_file.get_class() == ELFCLASS32 ) { + generic_add_entry( offset, info, addend ); + } + else { + generic_add_entry( offset, info, addend ); + } + } + + //------------------------------------------------------------------------------ + //! \brief Add an entry + //! \param offset Offset of the entry + //! \param symbol Symbol of the entry + //! \param type Type of the entry + //! \param addend Addend of the entry + void add_entry( Elf64_Addr offset, + Elf_Word symbol, + unsigned type, + Elf_Sxword addend ) + { + Elf_Xword info; + if ( elf_file.get_class() == ELFCLASS32 ) { + info = ELF32_R_INFO( (Elf_Xword)symbol, type ); + } + else { + info = ELF64_R_INFO( (Elf_Xword)symbol, type ); + } + + add_entry( offset, info, addend ); + } + + //------------------------------------------------------------------------------ + //! \brief Add an entry with additional information + //! \param str_writer String section accessor + //! \param str String + //! \param sym_writer Symbol section accessor + //! \param value Value of the symbol + //! \param size Size of the symbol + //! \param sym_info Symbol information + //! \param other Other information + //! \param shndx Section index + //! \param offset Offset of the entry + //! \param type Type of the entry + void add_entry( string_section_accessor str_writer, + const char* str, + symbol_section_accessor sym_writer, + Elf64_Addr value, + Elf_Word size, + unsigned char sym_info, + unsigned char other, + Elf_Half shndx, + Elf64_Addr offset, + unsigned type ) + { + Elf_Word str_index = str_writer.add_string( str ); + Elf_Word sym_index = sym_writer.add_symbol( str_index, value, size, + sym_info, other, shndx ); + add_entry( offset, sym_index, type ); + } + + //------------------------------------------------------------------------------ + //! \brief Swap symbols + //! \param first First symbol + //! \param second Second symbol + void swap_symbols( Elf_Xword first, Elf_Xword second ) + { + Elf64_Addr offset = 0; + Elf_Word symbol = 0; + unsigned rtype = 0; + Elf_Sxword addend = 0; + for ( Elf_Word i = 0; i < get_entries_num(); i++ ) { + get_entry( i, offset, symbol, rtype, addend ); + if ( symbol == first ) { + set_entry( i, offset, (Elf_Word)second, rtype, addend ); + } + if ( symbol == second ) { + set_entry( i, offset, (Elf_Word)first, rtype, addend ); + } + } + } + + //------------------------------------------------------------------------------ + private: + //------------------------------------------------------------------------------ + //! \brief Get the symbol table index + //! \return Symbol table index + Elf_Half get_symbol_table_index() const + { + return (Elf_Half)relocation_section->get_link(); + } + + //------------------------------------------------------------------------------ + //! \brief Get a generic entry for REL type + //! \param index Index of the entry + //! \param offset Offset of the entry + //! \param symbol Symbol of the entry + //! \param type Type of the entry + //! \param addend Addend of the entry + //! \return True if successful, false otherwise + template + bool generic_get_entry_rel( Elf_Xword index, + Elf64_Addr& offset, + Elf_Word& symbol, + unsigned& type, + Elf_Sxword& addend ) const + { + const auto& convertor = elf_file.get_convertor(); + + if ( relocation_section->get_entry_size() < sizeof( T ) ) { + return false; + } + const T* pEntry = reinterpret_cast( + relocation_section->get_data() + + index * relocation_section->get_entry_size() ); + offset = ( *convertor )( pEntry->r_offset ); + Elf_Xword tmp = ( *convertor )( pEntry->r_info ); + symbol = get_sym_and_type::get_r_sym( tmp ); + type = get_sym_and_type::get_r_type( tmp ); + addend = 0; + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Get a generic entry for RELA type + //! \param index Index of the entry + //! \param offset Offset of the entry + //! \param symbol Symbol of the entry + //! \param type Type of the entry + //! \param addend Addend of the entry + //! \return True if successful, false otherwise + template + bool generic_get_entry_rela( Elf_Xword index, + Elf64_Addr& offset, + Elf_Word& symbol, + unsigned& type, + Elf_Sxword& addend ) const + { + const auto& convertor = elf_file.get_convertor(); + + if ( relocation_section->get_entry_size() < sizeof( T ) ) { + return false; + } + + const T* pEntry = reinterpret_cast( + relocation_section->get_data() + + index * relocation_section->get_entry_size() ); + offset = ( *convertor )( pEntry->r_offset ); + Elf_Xword tmp = ( *convertor )( pEntry->r_info ); + symbol = get_sym_and_type::get_r_sym( tmp ); + type = get_sym_and_type::get_r_type( tmp ); + addend = ( *convertor )( pEntry->r_addend ); + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Set a generic entry for REL type + //! \param index Index of the entry + //! \param offset Offset of the entry + //! \param symbol Symbol of the entry + //! \param type Type of the entry + //! \param addend Addend of the entry + template + void generic_set_entry_rel( Elf_Xword index, + Elf64_Addr offset, + Elf_Word symbol, + unsigned type, + Elf_Sxword ) + { + const auto& convertor = elf_file.get_convertor(); + + T* pEntry = const_cast( reinterpret_cast( + relocation_section->get_data() + + index * relocation_section->get_entry_size() ) ); + + if ( elf_file.get_class() == ELFCLASS32 ) { + pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type ); + } + else { + pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type ); + } + pEntry->r_offset = decltype( pEntry->r_offset )( offset ); + pEntry->r_offset = ( *convertor )( pEntry->r_offset ); + pEntry->r_info = ( *convertor )( pEntry->r_info ); + } + + //------------------------------------------------------------------------------ + //! \brief Set a generic entry for RELA type + //! \param index Index of the entry + //! \param offset Offset of the entry + //! \param symbol Symbol of the entry + //! \param type Type of the entry + //! \param addend Addend of the entry + template + void generic_set_entry_rela( Elf_Xword index, + Elf64_Addr offset, + Elf_Word symbol, + unsigned type, + Elf_Sxword addend ) + { + const auto& convertor = elf_file.get_convertor(); + + T* pEntry = const_cast( reinterpret_cast( + relocation_section->get_data() + + index * relocation_section->get_entry_size() ) ); + + if ( elf_file.get_class() == ELFCLASS32 ) { + pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type ); + } + else { + pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type ); + } + pEntry->r_offset = decltype( pEntry->r_offset )( offset ); + pEntry->r_addend = decltype( pEntry->r_addend )( addend ); + pEntry->r_offset = ( *convertor )( pEntry->r_offset ); + pEntry->r_info = ( *convertor )( pEntry->r_info ); + pEntry->r_addend = ( *convertor )( pEntry->r_addend ); + } + + //------------------------------------------------------------------------------ + //! \brief Add a generic entry for REL type + //! \param offset Offset of the entry + //! \param info Information of the entry + template + void generic_add_entry( Elf64_Addr offset, Elf_Xword info ) + { + const auto& convertor = elf_file.get_convertor(); + + T entry; + entry.r_offset = decltype( entry.r_offset )( offset ); + entry.r_info = decltype( entry.r_info )( info ); + entry.r_offset = ( *convertor )( entry.r_offset ); + entry.r_info = ( *convertor )( entry.r_info ); + + relocation_section->append_data( reinterpret_cast( &entry ), + sizeof( entry ) ); + } + + //------------------------------------------------------------------------------ + //! \brief Add a generic entry for RELA type + //! \param offset Offset of the entry + //! \param info Information of the entry + //! \param addend Addend of the entry + template + void + generic_add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend ) + { + const auto& convertor = elf_file.get_convertor(); + + T entry; + entry.r_offset = offset; + entry.r_info = info; + entry.r_addend = addend; + entry.r_offset = ( *convertor )( entry.r_offset ); + entry.r_info = ( *convertor )( entry.r_info ); + entry.r_addend = ( *convertor )( entry.r_addend ); + + relocation_section->append_data( reinterpret_cast( &entry ), + sizeof( entry ) ); + } + + //------------------------------------------------------------------------------ + private: + const elfio& elf_file; + S* relocation_section = nullptr; +}; + +using relocation_section_accessor = + relocation_section_accessor_template
; +using const_relocation_section_accessor = + relocation_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_RELOCATION_HPP diff --git a/vyld/elfio/elfio_section.hpp b/vyld/elfio/elfio_section.hpp new file mode 100644 index 0000000..c5e5bd9 --- /dev/null +++ b/vyld/elfio/elfio_section.hpp @@ -0,0 +1,611 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_SECTION_HPP +#define ELFIO_SECTION_HPP + +#include +#include +#include +#include + +namespace ELFIO { + +/** + * @class section + * @brief Represents a section in an ELF file. + */ +class section +{ + friend class elfio; + + public: + virtual ~section() = default; + + ELFIO_GET_ACCESS_DECL( Elf_Half, index ); + ELFIO_GET_SET_ACCESS_DECL( std::string, name ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, flags ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, info ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, link ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, addr_align ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, entry_size ); + ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, address ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, size ); + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, name_string_offset ); + ELFIO_GET_ACCESS_DECL( Elf64_Off, offset ); + + /** + * @brief Get the data of the section. + * @return Pointer to the data. + */ + virtual const char* get_data() const = 0; + + /** + * @brief Free the data of the section. + */ + virtual void free_data() const = 0; + + /** + * @brief Set the data of the section. + * @param raw_data Pointer to the raw data. + * @param size Size of the data. + */ + virtual void set_data( const char* raw_data, Elf_Xword size ) = 0; + + /** + * @brief Set the data of the section. + * @param data String containing the data. + */ + virtual void set_data( const std::string& data ) = 0; + + /** + * @brief Append data to the section. + * @param raw_data Pointer to the raw data. + * @param size Size of the data. + */ + virtual void append_data( const char* raw_data, Elf_Xword size ) = 0; + + /** + * @brief Append data to the section. + * @param data String containing the data. + */ + virtual void append_data( const std::string& data ) = 0; + + /** + * @brief Insert data into the section at a specific position. + * @param pos Position to insert the data. + * @param raw_data Pointer to the raw data. + * @param size Size of the data. + */ + virtual void + insert_data( Elf_Xword pos, const char* raw_data, Elf_Xword size ) = 0; + + /** + * @brief Insert data into the section at a specific position. + * @param pos Position to insert the data. + * @param data String containing the data. + */ + virtual void insert_data( Elf_Xword pos, const std::string& data ) = 0; + + /** + * @brief Get the size of the stream. + * @return Size of the stream. + */ + virtual size_t get_stream_size() const = 0; + + /** + * @brief Set the size of the stream. + * @param value Size of the stream. + */ + virtual void set_stream_size( size_t value ) = 0; + + protected: + ELFIO_SET_ACCESS_DECL( Elf64_Off, offset ); + ELFIO_SET_ACCESS_DECL( Elf_Half, index ); + + /** + * @brief Load the section from a stream. + * @param stream Input stream. + * @param header_offset Offset of the header. + * @param is_lazy Whether to load lazily. + * @return True if successful, false otherwise. + */ + virtual bool load( std::istream& stream, + std::streampos header_offset, + bool is_lazy ) = 0; + + /** + * @brief Save the section to a stream. + * @param stream Output stream. + * @param header_offset Offset of the header. + * @param data_offset Offset of the data. + */ + virtual void save( std::ostream& stream, + std::streampos header_offset, + std::streampos data_offset ) = 0; + + /** + * @brief Check if the address is initialized. + * @return True if initialized, false otherwise. + */ + virtual bool is_address_initialized() const = 0; +}; + +/** + * @class section_impl + * @brief Implementation of the section class. + * @tparam T Type of the section header. + */ +template class section_impl : public section +{ + public: + /** + * @brief Constructor. + * @param convertor Pointer to the endianness convertor. + * @param translator Pointer to the address translator. + * @param compression Shared pointer to the compression interface. + */ + section_impl( std::shared_ptr convertor, + std::shared_ptr translator, + std::shared_ptr compression ) + : convertor( convertor ), translator( translator ), + compression( compression ) + { + } + + // Section info functions + ELFIO_GET_SET_ACCESS( Elf_Word, type, header.sh_type ); + ELFIO_GET_SET_ACCESS( Elf_Xword, flags, header.sh_flags ); + ELFIO_GET_SET_ACCESS( Elf_Xword, size, header.sh_size ); + ELFIO_GET_SET_ACCESS( Elf_Word, link, header.sh_link ); + ELFIO_GET_SET_ACCESS( Elf_Word, info, header.sh_info ); + ELFIO_GET_SET_ACCESS( Elf_Xword, addr_align, header.sh_addralign ); + ELFIO_GET_SET_ACCESS( Elf_Xword, entry_size, header.sh_entsize ); + ELFIO_GET_SET_ACCESS( Elf_Word, name_string_offset, header.sh_name ); + ELFIO_GET_ACCESS( Elf64_Addr, address, header.sh_addr ); + + /** + * @brief Get the index of the section. + * @return Index of the section. + */ + Elf_Half get_index() const override { return index; } + + /** + * @brief Get the name of the section. + * @return Name of the section. + */ + std::string get_name() const override { return name; } + + /** + * @brief Set the name of the section. + * @param name_prm Name of the section. + */ + void set_name( const std::string& name_prm ) override + { + this->name = name_prm; + } + + /** + * @brief Set the address of the section. + * @param value Address of the section. + */ + void set_address( const Elf64_Addr& value ) override + { + header.sh_addr = decltype( header.sh_addr )( value ); + header.sh_addr = ( *convertor )( header.sh_addr ); + is_address_set = true; + } + + /** + * @brief Check if the address is initialized. + * @return True if initialized, false otherwise. + */ + bool is_address_initialized() const override { return is_address_set; } + + /** + * @brief Get the data of the section. + * @return Pointer to the data. + */ + const char* get_data() const override + { + // If data load failed, the stream is corrupt + // When lazy loading, attempts to call get_data() on it after initial load are useless + // When loading non-lazily, that load_data() will attempt to read data from + // the stream specified on load() call, which might be freed by this point + if ( !is_loaded && can_be_loaded ) { + bool res = load_data(); + + if ( !res ) { + can_be_loaded = false; + } + } + return data.get(); + } + + /** + * @brief Free the data of the section. + */ + void free_data() const override + { + if ( is_lazy ) { + data.reset( nullptr ); + is_loaded = false; + } + } + + /** + * @brief Set the data of the section. + * @param raw_data Pointer to the raw data. + * @param size Size of the data. + */ + void set_data( const char* raw_data, Elf_Xword size ) override + { + if ( get_type() != SHT_NOBITS ) { + data = std::unique_ptr( + new ( std::nothrow ) char[(size_t)size] ); + if ( nullptr != data.get() && nullptr != raw_data ) { + data_size = size; + std::copy( raw_data, raw_data + size, data.get() ); + } + else { + data_size = 0; + } + } + + set_size( data_size ); + if ( translator->empty() ) { + set_stream_size( (size_t)data_size ); + } + } + + /** + * @brief Set the data of the section. + * @param str_data String containing the data. + */ + void set_data( const std::string& str_data ) override + { + return set_data( str_data.c_str(), (Elf_Word)str_data.size() ); + } + + /** + * @brief Append data to the section. + * @param raw_data Pointer to the raw data. + * @param size Size of the data. + */ + void append_data( const char* raw_data, Elf_Xword size ) override + { + insert_data( get_size(), raw_data, size ); + } + + /** + * @brief Append data to the section. + * @param str_data String containing the data. + */ + void append_data( const std::string& str_data ) override + { + return append_data( str_data.c_str(), (Elf_Word)str_data.size() ); + } + + /** + * @brief Insert data into the section at a specific position. + * @param pos Position to insert the data. + * @param raw_data Pointer to the raw data. + * @param size Size of the data. + */ + void + insert_data( Elf_Xword pos, const char* raw_data, Elf_Xword size ) override + { + if ( get_type() != SHT_NOBITS ) { + // Check for valid position + if ( pos > get_size() ) { + return; // Invalid position + } + + // Check for integer overflow in size calculation + Elf_Xword new_size = get_size(); + if ( size > std::numeric_limits::max() - new_size ) { + return; // Size would overflow + } + new_size += size; + + if ( new_size <= data_size ) { + char* d = data.get(); + std::copy_backward( d + pos, d + get_size(), + d + get_size() + size ); + std::copy( raw_data, raw_data + size, d + pos ); + } + else { + // Calculate new size with overflow check + Elf_Xword new_data_size = data_size; + if ( new_data_size > + std::numeric_limits::max() / 2 ) { + return; // Multiplication would overflow + } + new_data_size *= 2; + if ( size > + std::numeric_limits::max() - new_data_size ) { + return; // Addition would overflow + } + new_data_size += size; + + // Check if the size would overflow size_t + if ( new_data_size > std::numeric_limits::max() ) { + return; // Size would overflow size_t + } + + std::unique_ptr new_data( + new ( std::nothrow ) char[(size_t)new_data_size] ); + + if ( nullptr != new_data ) { + char* d = data.get(); + std::copy( d, d + pos, new_data.get() ); + std::copy( raw_data, raw_data + size, + new_data.get() + pos ); + std::copy( d + pos, d + get_size(), + new_data.get() + pos + size ); + data = std::move( new_data ); + data_size = new_data_size; + } + else { + return; // Allocation failed + } + } + set_size( new_size ); + if ( translator->empty() ) { + set_stream_size( get_stream_size() + (size_t)size ); + } + } + } + + /** + * @brief Insert data into the section at a specific position. + * @param pos Position to insert the data. + * @param str_data String containing the data. + */ + void insert_data( Elf_Xword pos, const std::string& str_data ) override + { + return insert_data( pos, str_data.c_str(), (Elf_Word)str_data.size() ); + } + + /** + * @brief Get the size of the stream. + * @return Size of the stream. + */ + size_t get_stream_size() const override { return stream_size; } + + /** + * @brief Set the size of the stream. + * @param value Size of the stream. + */ + void set_stream_size( size_t value ) override { stream_size = value; } + + protected: + ELFIO_GET_SET_ACCESS( Elf64_Off, offset, header.sh_offset ); + + /** + * @brief Set the index of the section. + * @param value Index of the section. + */ + void set_index( const Elf_Half& value ) override { index = value; } + + /** + * @brief Check if the section is compressed. + * @return True if compressed, false otherwise. + */ + bool is_compressed() const + { + return ( ( get_flags() & SHF_RPX_DEFLATE ) || + ( get_flags() & SHF_COMPRESSED ) ) && + compression != nullptr; + } + + /** + * @brief Load the section from a stream. + * @param stream Input stream. + * @param header_offset Offset of the header. + * @param is_lazy_ Whether to load lazily. + * @return True if successful, false otherwise. + */ + bool load( std::istream& stream, + std::streampos header_offset, + bool is_lazy_ ) override + { + pstream = &stream; + is_lazy = is_lazy_; + + if ( translator->empty() ) { + stream.seekg( 0, std::istream::end ); + set_stream_size( size_t( stream.tellg() ) ); + } + else { + set_stream_size( std::numeric_limits::max() ); + } + + stream.seekg( ( *translator )[header_offset] ); + stream.read( reinterpret_cast( &header ), sizeof( header ) ); + + if ( !( is_lazy || is_loaded ) ) { + bool ret = get_data(); + + if ( is_compressed() ) { + Elf_Xword size = get_size(); + Elf_Xword uncompressed_size = 0; + auto decompressed_data = compression->inflate( + data.get(), convertor, size, uncompressed_size ); + if ( decompressed_data != nullptr ) { + set_size( uncompressed_size ); + data = std::move( decompressed_data ); + } + } + + return ret; + } + + return true; + } + + /** + * @brief Load the data of the section. + * @return True if successful, false otherwise. + */ + bool load_data() const + { + Elf_Xword sh_offset = + ( *translator )[( *convertor )( header.sh_offset )]; + Elf_Xword size = get_size(); + + // Check for integer overflow in offset calculation + if ( sh_offset > get_stream_size() ) { + return false; + } + + // Check for integer overflow in size calculation + if ( size > get_stream_size() || + size > ( get_stream_size() - sh_offset ) ) { + return false; + } + + // Check if we need to load data + if ( nullptr == data && SHT_NULL != get_type() && + SHT_NOBITS != get_type() ) { + // Check if size can be safely converted to size_t + if ( size > std::numeric_limits::max() - 1 ) { + return false; + } + + data.reset( new ( std::nothrow ) char[size_t( size ) + 1] ); + + if ( ( 0 != size ) && ( nullptr != data ) ) { + pstream->seekg( sh_offset ); + pstream->read( data.get(), size ); + if ( static_cast( pstream->gcount() ) != size ) { + data.reset( nullptr ); + data_size = 0; + return false; + } + + data_size = size; + data.get()[size] = 0; // Safe now as we allocated size + 1 + } + else { + data_size = 0; + if ( size != 0 ) { + return false; // Failed to allocate required memory + } + } + + is_loaded = true; + return true; + } + + // Data already loaded or doesn't need loading + is_loaded = ( nullptr != data ) || ( SHT_NULL == get_type() ) || + ( SHT_NOBITS == get_type() ); + return is_loaded; + } + + /** + * @brief Save the section to a stream. + * @param stream Output stream. + * @param header_offset Offset of the header. + * @param data_offset Offset of the data. + */ + void save( std::ostream& stream, + std::streampos header_offset, + std::streampos data_offset ) override + { + if ( 0 != get_index() ) { + header.sh_offset = decltype( header.sh_offset )( data_offset ); + header.sh_offset = ( *convertor )( header.sh_offset ); + } + + save_header( stream, header_offset ); + if ( get_type() != SHT_NOBITS && get_type() != SHT_NULL && + get_size() != 0 && data != nullptr ) { + save_data( stream, data_offset ); + } + } + + private: + /** + * @brief Save the header of the section to a stream. + * @param stream Output stream. + * @param header_offset Offset of the header. + */ + void save_header( std::ostream& stream, std::streampos header_offset ) const + { + adjust_stream_size( stream, header_offset ); + stream.write( reinterpret_cast( &header ), + sizeof( header ) ); + } + + /** + * @brief Save the data of the section to a stream. + * @param stream Output stream. + * @param data_offset Offset of the data. + */ + void save_data( std::ostream& stream, std::streampos data_offset ) + { + adjust_stream_size( stream, data_offset ); + + if ( ( ( get_flags() & SHF_COMPRESSED ) || + ( get_flags() & SHF_RPX_DEFLATE ) ) && + compression != nullptr ) { + Elf_Xword decompressed_size = get_size(); + Elf_Xword compressed_size = 0; + auto compressed_ptr = compression->deflate( + data.get(), convertor, decompressed_size, compressed_size ); + stream.write( compressed_ptr.get(), compressed_size ); + } + else { + stream.write( get_data(), get_size() ); + } + } + + private: + mutable std::istream* pstream = + nullptr; /**< Pointer to the input stream. */ + T header = {}; /**< Section header. */ + Elf_Half index = 0; /**< Index of the section. */ + std::string name; /**< Name of the section. */ + mutable std::unique_ptr data; /**< Pointer to the data. */ + mutable Elf_Xword data_size = 0; /**< Size of the data. */ + std::shared_ptr convertor = + nullptr; /**< Pointer to the endianness convertor. */ + std::shared_ptr translator = + nullptr; /**< Pointer to the address translator. */ + std::shared_ptr compression = + nullptr; /**< Shared pointer to the compression interface. */ + bool is_address_set = false; /**< Flag indicating if the address is set. */ + size_t stream_size = 0; /**< Size of the stream. */ + mutable bool is_lazy = + false; /**< Flag indicating if lazy loading is enabled. */ + mutable bool is_loaded = + false; /**< Flag indicating if the data is loaded. */ + mutable bool can_be_loaded = + true; /**< Flag indicating if the data can loaded. This is not the case if the section is corrupted. */ +}; + +} // namespace ELFIO + +#endif // ELFIO_SECTION_HPP diff --git a/vyld/elfio/elfio_segment.hpp b/vyld/elfio/elfio_segment.hpp new file mode 100644 index 0000000..2c7b08c --- /dev/null +++ b/vyld/elfio/elfio_segment.hpp @@ -0,0 +1,405 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_SEGMENT_HPP +#define ELFIO_SEGMENT_HPP + +#include +#include +#include +#include + +namespace ELFIO { + +//------------------------------------------------------------------------------ +//! \class segment +//! \brief Class for accessing segment data +class segment +{ + friend class elfio; + + public: + virtual ~segment() = default; + + //------------------------------------------------------------------------------ + //! \brief Get the index of the segment + //! \return Index of the segment + ELFIO_GET_ACCESS_DECL( Elf_Half, index ); + //------------------------------------------------------------------------------ + //! \brief Get the type of the segment + //! \return Type of the segment + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type ); + //------------------------------------------------------------------------------ + //! \brief Get the flags of the segment + //! \return Flags of the segment + ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags ); + //------------------------------------------------------------------------------ + //! \brief Get the alignment of the segment + //! \return Alignment of the segment + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, align ); + //------------------------------------------------------------------------------ + //! \brief Get the virtual address of the segment + //! \return Virtual address of the segment + ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, virtual_address ); + //------------------------------------------------------------------------------ + //! \brief Get the physical address of the segment + //! \return Physical address of the segment + ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, physical_address ); + //------------------------------------------------------------------------------ + //! \brief Get the file size of the segment + //! \return File size of the segment + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, file_size ); + //------------------------------------------------------------------------------ + //! \brief Get the memory size of the segment + //! \return Memory size of the segment + ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, memory_size ); + //------------------------------------------------------------------------------ + //! \brief Get the offset of the segment + //! \return Offset of the segment + ELFIO_GET_ACCESS_DECL( Elf64_Off, offset ); + + //------------------------------------------------------------------------------ + //! \brief Get the data of the segment + //! \return Pointer to the data + virtual const char* get_data() const = 0; + //------------------------------------------------------------------------------ + //! \brief Free the data of the segment + virtual void free_data() const = 0; + + //------------------------------------------------------------------------------ + //! \brief Add a section to the segment + //! \param psec Pointer to the section + //! \param addr_align Alignment of the section + //! \return Index of the added section + virtual Elf_Half add_section( section* psec, Elf_Xword addr_align ) = 0; + //------------------------------------------------------------------------------ + //! \brief Add a section index to the segment + //! \param index Index of the section + //! \param addr_align Alignment of the section + //! \return Index of the added section + virtual Elf_Half add_section_index( Elf_Half index, + Elf_Xword addr_align ) = 0; + //------------------------------------------------------------------------------ + //! \brief Get the number of sections in the segment + //! \return Number of sections in the segment + virtual Elf_Half get_sections_num() const = 0; + //------------------------------------------------------------------------------ + //! \brief Get the index of a section at a given position + //! \param num Position of the section + //! \return Index of the section + virtual Elf_Half get_section_index_at( Elf_Half num ) const = 0; + //------------------------------------------------------------------------------ + //! \brief Check if the offset is initialized + //! \return True if the offset is initialized, false otherwise + virtual bool is_offset_initialized() const = 0; + + protected: + //------------------------------------------------------------------------------ + //! \brief Set the offset of the segment + //! \param offset Offset of the segment + ELFIO_SET_ACCESS_DECL( Elf64_Off, offset ); + //------------------------------------------------------------------------------ + //! \brief Set the index of the segment + //! \param index Index of the segment + ELFIO_SET_ACCESS_DECL( Elf_Half, index ); + + //------------------------------------------------------------------------------ + //! \brief Get the sections of the segment + //! \return Vector of section indices + virtual const std::vector& get_sections() const = 0; + + //------------------------------------------------------------------------------ + //! \brief Load the segment from a stream + //! \param stream Input stream + //! \param header_offset Offset of the segment header + //! \param is_lazy Whether to load the segment lazily + //! \return True if successful, false otherwise + virtual bool load( std::istream& stream, + std::streampos header_offset, + bool is_lazy ) = 0; + //------------------------------------------------------------------------------ + //! \brief Save the segment to a stream + //! \param stream Output stream + //! \param header_offset Offset of the segment header + //! \param data_offset Offset of the segment data + virtual void save( std::ostream& stream, + std::streampos header_offset, + std::streampos data_offset ) = 0; +}; + +//------------------------------------------------------------------------------ +//! \class segment_impl +//! \brief Implementation of the segment class +template class segment_impl : public segment +{ + public: + //------------------------------------------------------------------------------ + //! \brief Constructor + //! \param convertor Pointer to the endianness convertor + //! \param translator Pointer to the address translator + segment_impl( std::shared_ptr convertor, + std::shared_ptr translator ) + : convertor( convertor ), translator( translator ) + { + } + + //------------------------------------------------------------------------------ + // Section info functions + ELFIO_GET_SET_ACCESS( Elf_Word, type, ph.p_type ); + ELFIO_GET_SET_ACCESS( Elf_Word, flags, ph.p_flags ); + ELFIO_GET_SET_ACCESS( Elf_Xword, align, ph.p_align ); + ELFIO_GET_SET_ACCESS( Elf64_Addr, virtual_address, ph.p_vaddr ); + ELFIO_GET_SET_ACCESS( Elf64_Addr, physical_address, ph.p_paddr ); + ELFIO_GET_SET_ACCESS( Elf_Xword, file_size, ph.p_filesz ); + ELFIO_GET_SET_ACCESS( Elf_Xword, memory_size, ph.p_memsz ); + ELFIO_GET_ACCESS( Elf64_Off, offset, ph.p_offset ); + + //------------------------------------------------------------------------------ + //! \brief Get the index of the segment + //! \return Index of the segment + Elf_Half get_index() const override { return index; } + + //------------------------------------------------------------------------------ + //! \brief Get the data of the segment + //! \return Pointer to the data + const char* get_data() const override + { + if ( !is_loaded ) { + load_data(); + } + return data.get(); + } + + //------------------------------------------------------------------------------ + //! \brief Free the data of the segment + void free_data() const override + { + if ( is_lazy ) { + data.reset( nullptr ); + is_loaded = false; + } + } + + //------------------------------------------------------------------------------ + //! \brief Add a section index to the segment + //! \param sec_index Index of the section + //! \param addr_align Alignment of the section + //! \return Index of the added section + Elf_Half add_section_index( Elf_Half sec_index, + Elf_Xword addr_align ) override + { + sections.emplace_back( sec_index ); + if ( addr_align > get_align() ) { + set_align( addr_align ); + } + + return (Elf_Half)sections.size(); + } + + //------------------------------------------------------------------------------ + //! \brief Add a section to the segment + //! \param psec Pointer to the section + //! \param addr_align Alignment of the section + //! \return Index of the added section + Elf_Half add_section( section* psec, Elf_Xword addr_align ) override + { + return add_section_index( psec->get_index(), addr_align ); + } + + //------------------------------------------------------------------------------ + //! \brief Get the number of sections in the segment + //! \return Number of sections in the segment + Elf_Half get_sections_num() const override + { + return (Elf_Half)sections.size(); + } + + //------------------------------------------------------------------------------ + //! \brief Get the index of a section at a given position + //! \param num Position of the section + //! \return Index of the section + Elf_Half get_section_index_at( Elf_Half num ) const override + { + if ( num < sections.size() ) { + return sections[num]; + } + + return Elf_Half( -1 ); + } + + //------------------------------------------------------------------------------ + protected: + //------------------------------------------------------------------------------ + //! \brief Set the offset of the segment + //! \param value Offset of the segment + void set_offset( const Elf64_Off& value ) override + { + ph.p_offset = decltype( ph.p_offset )( value ); + ph.p_offset = ( *convertor )( ph.p_offset ); + is_offset_set = true; + } + + //------------------------------------------------------------------------------ + //! \brief Check if the offset is initialized + //! \return True if the offset is initialized, false otherwise + bool is_offset_initialized() const override { return is_offset_set; } + + //------------------------------------------------------------------------------ + //! \brief Get the sections of the segment + //! \return Vector of section indices + const std::vector& get_sections() const override + { + return sections; + } + + //------------------------------------------------------------------------------ + //! \brief Set the index of the segment + //! \param value Index of the segment + void set_index( const Elf_Half& value ) override { index = value; } + + //------------------------------------------------------------------------------ + //! \brief Load the segment from a stream + //! \param stream Input stream + //! \param header_offset Offset of the segment header + //! \param is_lazy_ Whether to load the segment lazily + //! \return True if successful, false otherwise + bool load( std::istream& stream, + std::streampos header_offset, + bool is_lazy_ ) override + { + pstream = &stream; + is_lazy = is_lazy_; + + if ( translator->empty() ) { + stream.seekg( 0, std::istream::end ); + set_stream_size( size_t( stream.tellg() ) ); + } + else { + set_stream_size( std::numeric_limits::max() ); + } + + stream.seekg( ( *translator )[header_offset] ); + stream.read( reinterpret_cast( &ph ), sizeof( ph ) ); + + is_offset_set = true; + + if ( !( is_lazy || is_loaded ) ) { + return load_data(); + } + + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Load the data of the segment + //! \return True if successful, false otherwise + bool load_data() const + { + if ( PT_NULL == get_type() || 0 == get_file_size() ) { + return true; + } + + Elf_Xword p_offset = ( *translator )[( *convertor )( ph.p_offset )]; + Elf_Xword size = get_file_size(); + + // Check for integer overflow in offset calculation + if ( p_offset > get_stream_size() ) { + data = nullptr; + return false; + } + + // Check for integer overflow in size calculation + if ( size > get_stream_size() || + size > ( get_stream_size() - p_offset ) ) { + data = nullptr; + return false; + } + + // Check if size can be safely converted to size_t + if ( size > std::numeric_limits::max() - 1 ) { + data = nullptr; + return false; + } + + data.reset( new ( std::nothrow ) char[(size_t)size + 1] ); + + pstream->seekg( p_offset ); + if ( nullptr != data.get() && pstream->read( data.get(), size ) ) { + data.get()[size] = 0; + } + else { + data = nullptr; + return false; + } + + is_loaded = true; + + return true; + } + + //------------------------------------------------------------------------------ + //! \brief Save the segment to a stream + //! \param stream Output stream + //! \param header_offset Offset of the segment header + //! \param data_offset Offset of the segment data + void save( std::ostream& stream, + std::streampos header_offset, + std::streampos data_offset ) override + { + ph.p_offset = decltype( ph.p_offset )( data_offset ); + ph.p_offset = ( *convertor )( ph.p_offset ); + adjust_stream_size( stream, header_offset ); + stream.write( reinterpret_cast( &ph ), sizeof( ph ) ); + } + + //------------------------------------------------------------------------------ + //! \brief Get the stream size + //! \return Stream size + size_t get_stream_size() const { return stream_size; } + + //------------------------------------------------------------------------------ + //! \brief Set the stream size + //! \param value Stream size + void set_stream_size( size_t value ) { stream_size = value; } + + //------------------------------------------------------------------------------ + private: + mutable std::istream* pstream = nullptr; //!< Pointer to the input stream + T ph = {}; //!< Segment header + Elf_Half index = 0; //!< Index of the segment + mutable std::unique_ptr data; //!< Pointer to the segment data + std::vector sections; //!< Vector of section indices + std::shared_ptr convertor = + nullptr; //!< Pointer to the endianness convertor + std::shared_ptr translator = + nullptr; //!< Pointer to the address translator + size_t stream_size = 0; //!< Stream size + bool is_offset_set = false; //!< Flag indicating if the offset is set + mutable bool is_lazy = + false; //!< Flag indicating if the segment is loaded lazily + mutable bool is_loaded = + false; //!< Flag indicating if the segment is loaded +}; + +} // namespace ELFIO + +#endif // ELFIO_SEGMENT_HPP diff --git a/vyld/elfio/elfio_strings.hpp b/vyld/elfio/elfio_strings.hpp new file mode 100644 index 0000000..d56f24b --- /dev/null +++ b/vyld/elfio/elfio_strings.hpp @@ -0,0 +1,143 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_STRINGS_HPP +#define ELFIO_STRINGS_HPP + +#include +#include +#include +#include + +namespace ELFIO { + +//------------------------------------------------------------------------------ +//! \class string_section_accessor_template +//! \brief Class for accessing string section data +template class string_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + //! \brief Constructor + //! \param section Pointer to the section + explicit string_section_accessor_template( S* section ) + : string_section( section ) + { + } + + //------------------------------------------------------------------------------ + //! \brief Get a string from the section + //! \param index Index of the string + //! \return Pointer to the string, or nullptr if not found + const char* get_string( Elf_Word index ) const + { + if ( string_section ) { + const char* data = string_section->get_data(); + size_t section_size = + static_cast( string_section->get_size() ); + + // Check if index is within bounds + if ( index >= section_size || nullptr == data ) { + return nullptr; + } + + // Check for integer overflow in size calculation + size_t remaining_size = section_size - index; + if ( remaining_size > section_size ) { // Check for underflow + return nullptr; + } + + // Use standard C++ functions to find string length + const char* str = data + index; + const char* end = + (const char*)std::memchr( str, '\0', remaining_size ); + if ( end != nullptr && end < str + remaining_size ) { + return str; + } + } + + return nullptr; + } + + //------------------------------------------------------------------------------ + //! \brief Add a string to the section + //! \param str Pointer to the string + //! \return Index of the added string + Elf_Word add_string( const char* str ) + { + if ( !str ) { + return 0; // Return index of empty string for null input + } + + Elf_Word current_position = 0; + + if ( string_section ) { + // Strings are added to the end of the current section data + current_position = + static_cast( string_section->get_size() ); + + if ( current_position == 0 ) { + char empty_string = '\0'; + string_section->append_data( &empty_string, 1 ); + current_position++; + } + + // Calculate string length and check for overflow + size_t str_len = std::strlen( str ); + if ( str_len > std::numeric_limits::max() - 1 ) { + return 0; // String too long + } + + // Check if appending would overflow section size + Elf_Word append_size = static_cast( str_len + 1 ); + if ( append_size > + std::numeric_limits::max() - current_position ) { + return 0; // Would overflow section size + } + + string_section->append_data( str, append_size ); + } + + return current_position; + } + + //------------------------------------------------------------------------------ + //! \brief Add a string to the section + //! \param str The string to add + //! \return Index of the added string + Elf_Word add_string( const std::string& str ) + { + return add_string( str.c_str() ); + } + + //------------------------------------------------------------------------------ + private: + S* string_section; //!< Pointer to the section +}; + +using string_section_accessor = string_section_accessor_template
; +using const_string_section_accessor = + string_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_STRINGS_HPP diff --git a/vyld/elfio/elfio_symbols.hpp b/vyld/elfio/elfio_symbols.hpp new file mode 100644 index 0000000..aeb3a7a --- /dev/null +++ b/vyld/elfio/elfio_symbols.hpp @@ -0,0 +1,716 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_SYMBOLS_HPP +#define ELFIO_SYMBOLS_HPP + +namespace ELFIO { + +//------------------------------------------------------------------------------ +// @class symbol_section_accessor_template +// @brief A template class for accessing symbol sections in an ELF file. +//------------------------------------------------------------------------------ +template class symbol_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + // @brief Constructor + // @param elf_file Reference to the ELF file + // @param symbol_section Pointer to the symbol section + //------------------------------------------------------------------------------ + explicit symbol_section_accessor_template( const elfio& elf_file, + S* symbol_section ) + : elf_file( elf_file ), symbol_section( symbol_section ) + { + find_hash_section(); + } + + //------------------------------------------------------------------------------ + // @brief Get the number of symbols in the section + // @return Number of symbols + //------------------------------------------------------------------------------ + Elf_Xword get_symbols_num() const + { + Elf_Xword nRet = 0; + + size_t minimum_symbol_size; + switch ( elf_file.get_class() ) { + case ELFCLASS32: + minimum_symbol_size = sizeof( Elf32_Sym ); + break; + case ELFCLASS64: + minimum_symbol_size = sizeof( Elf64_Sym ); + break; + default: + return nRet; + } + + if ( symbol_section->get_entry_size() >= minimum_symbol_size && + symbol_section->get_size() <= symbol_section->get_stream_size() ) { + nRet = + symbol_section->get_size() / symbol_section->get_entry_size(); + } + + return nRet; + } + + //------------------------------------------------------------------------------ + // @brief Get the symbol at the specified index + // @param index Index of the symbol + // @param name Name of the symbol + // @param value Value of the symbol + // @param size Size of the symbol + // @param bind Binding of the symbol + // @param type Type of the symbol + // @param section_index Section index of the symbol + // @param other Other attributes of the symbol + // @return True if the symbol is found, false otherwise + //------------------------------------------------------------------------------ + bool get_symbol( Elf_Xword index, + std::string& name, + Elf64_Addr& value, + Elf_Xword& size, + unsigned char& bind, + unsigned char& type, + Elf_Half& section_index, + unsigned char& other ) const + { + bool ret = false; + + if ( elf_file.get_class() == ELFCLASS32 ) { + ret = generic_get_symbol( index, name, value, size, bind, + type, section_index, other ); + } + else { + ret = generic_get_symbol( index, name, value, size, bind, + type, section_index, other ); + } + + return ret; + } + + //------------------------------------------------------------------------------ + // @brief Get the symbol with the specified name + // @param name Name of the symbol + // @param value Value of the symbol + // @param size Size of the symbol + // @param bind Binding of the symbol + // @param type Type of the symbol + // @param section_index Section index of the symbol + // @param other Other attributes of the symbol + // @return True if the symbol is found, false otherwise + //------------------------------------------------------------------------------ + bool get_symbol( const std::string& name, + Elf64_Addr& value, + Elf_Xword& size, + unsigned char& bind, + unsigned char& type, + Elf_Half& section_index, + unsigned char& other ) const + { + bool ret = false; + + if ( 0 != get_hash_table_index() ) { + if ( hash_section->get_type() == SHT_HASH ) { + ret = hash_lookup( name, value, size, bind, type, section_index, + other ); + } + if ( hash_section->get_type() == SHT_GNU_HASH || + hash_section->get_type() == DT_GNU_HASH ) { + if ( elf_file.get_class() == ELFCLASS32 ) { + ret = gnu_hash_lookup( + name, value, size, bind, type, section_index, other ); + } + else { + ret = gnu_hash_lookup( + name, value, size, bind, type, section_index, other ); + } + } + } + + if ( !ret ) { + for ( Elf_Xword i = 0; !ret && i < get_symbols_num(); i++ ) { + std::string symbol_name; + if ( get_symbol( i, symbol_name, value, size, bind, type, + section_index, other ) && + ( symbol_name == name ) ) { + ret = true; + } + } + } + + return ret; + } + + //------------------------------------------------------------------------------ + // @brief Get the symbol with the specified value + // @param value Value of the symbol + // @param name Name of the symbol + // @param size Size of the symbol + // @param bind Binding of the symbol + // @param type Type of the symbol + // @param section_index Section index of the symbol + // @param other Other attributes of the symbol + // @return True if the symbol is found, false otherwise + //------------------------------------------------------------------------------ + bool get_symbol( const Elf64_Addr& value, + std::string& name, + Elf_Xword& size, + unsigned char& bind, + unsigned char& type, + Elf_Half& section_index, + unsigned char& other ) const + { + + const auto& convertor = elf_file.get_convertor(); + + Elf_Xword idx = 0; + bool match = false; + Elf64_Addr v = 0; + + if ( elf_file.get_class() == ELFCLASS32 ) { + match = generic_search_symbols( + [&]( const Elf32_Sym* sym ) { + return ( *convertor )( sym->st_value ) == value; + }, + idx ); + } + else { + match = generic_search_symbols( + [&]( const Elf64_Sym* sym ) { + return ( *convertor )( sym->st_value ) == value; + }, + idx ); + } + + if ( match ) { + return get_symbol( idx, name, v, size, bind, type, section_index, + other ); + } + + return false; + } + + //------------------------------------------------------------------------------ + // @brief Add a symbol to the section + // @param name Name of the symbol + // @param value Value of the symbol + // @param size Size of the symbol + // @param info Info of the symbol + // @param other Other attributes of the symbol + // @param shndx Section index of the symbol + // @return Index of the added symbol + //------------------------------------------------------------------------------ + Elf_Word add_symbol( Elf_Word name, + Elf64_Addr value, + Elf_Xword size, + unsigned char info, + unsigned char other, + Elf_Half shndx ) + { + Elf_Word nRet; + + if ( symbol_section->get_size() == 0 ) { + if ( elf_file.get_class() == ELFCLASS32 ) { + nRet = generic_add_symbol( 0, 0, 0, 0, 0, 0 ); + } + else { + nRet = generic_add_symbol( 0, 0, 0, 0, 0, 0 ); + } + } + + if ( elf_file.get_class() == ELFCLASS32 ) { + nRet = generic_add_symbol( name, value, size, info, + other, shndx ); + } + else { + nRet = generic_add_symbol( name, value, size, info, + other, shndx ); + } + + return nRet; + } + + //------------------------------------------------------------------------------ + // @brief Add a symbol to the section + // @param name Name of the symbol + // @param value Value of the symbol + // @param size Size of the symbol + // @param bind Binding of the symbol + // @param type Type of the symbol + // @param other Other attributes of the symbol + // @param shndx Section index of the symbol + // @return Index of the added symbol + //------------------------------------------------------------------------------ + Elf_Word add_symbol( Elf_Word name, + Elf64_Addr value, + Elf_Xword size, + unsigned char bind, + unsigned char type, + unsigned char other, + Elf_Half shndx ) + { + return add_symbol( name, value, size, ELF_ST_INFO( bind, type ), other, + shndx ); + } + + //------------------------------------------------------------------------------ + // @brief Add a symbol to the section + // @param pStrWriter String section accessor + // @param str Name of the symbol + // @param value Value of the symbol + // @param size Size of the symbol + // @param info Info of the symbol + // @param other Other attributes of the symbol + // @param shndx Section index of the symbol + // @return Index of the added symbol + //------------------------------------------------------------------------------ + Elf_Word add_symbol( string_section_accessor& pStrWriter, + const char* str, + Elf64_Addr value, + Elf_Xword size, + unsigned char info, + unsigned char other, + Elf_Half shndx ) + { + Elf_Word index = pStrWriter.add_string( str ); + return add_symbol( index, value, size, info, other, shndx ); + } + + //------------------------------------------------------------------------------ + // @brief Add a symbol to the section + // @param pStrWriter String section accessor + // @param str Name of the symbol + // @param value Value of the symbol + // @param size Size of the symbol + // @param bind Binding of the symbol + // @param type Type of the symbol + // @param other Other attributes of the symbol + // @param shndx Section index of the symbol + // @return Index of the added symbol + //------------------------------------------------------------------------------ + Elf_Word add_symbol( string_section_accessor& pStrWriter, + const char* str, + Elf64_Addr value, + Elf_Xword size, + unsigned char bind, + unsigned char type, + unsigned char other, + Elf_Half shndx ) + { + return add_symbol( pStrWriter, str, value, size, + ELF_ST_INFO( bind, type ), other, shndx ); + } + + //------------------------------------------------------------------------------ + // @brief Arrange local symbols in the section + // @param func Function to be called for each pair of symbols + // @return Number of local symbols + //------------------------------------------------------------------------------ + Elf_Xword arrange_local_symbols( + std::function func = + nullptr ) + { + Elf_Xword nRet = 0; + + if ( elf_file.get_class() == ELFCLASS32 ) { + nRet = generic_arrange_local_symbols( func ); + } + else { + nRet = generic_arrange_local_symbols( func ); + } + + return nRet; + } + + //------------------------------------------------------------------------------ + private: + //------------------------------------------------------------------------------ + // @brief Find the hash section + //------------------------------------------------------------------------------ + void find_hash_section() + { + Elf_Half nSecNo = elf_file.sections.size(); + for ( Elf_Half i = 0; i < nSecNo; ++i ) { + const section* sec = elf_file.sections[i]; + if ( sec->get_link() == symbol_section->get_index() && + ( sec->get_type() == SHT_HASH || + sec->get_type() == SHT_GNU_HASH || + sec->get_type() == DT_GNU_HASH ) ) { + hash_section = sec; + hash_section_index = i; + break; + } + } + } + + //------------------------------------------------------------------------------ + // @brief Get the index of the string table + // @return Index of the string table + //------------------------------------------------------------------------------ + Elf_Half get_string_table_index() const + { + return (Elf_Half)symbol_section->get_link(); + } + + //------------------------------------------------------------------------------ + // @brief Get the index of the hash table + // @return Index of the hash table + //------------------------------------------------------------------------------ + Elf_Half get_hash_table_index() const { return hash_section_index; } + + //------------------------------------------------------------------------------ + // @brief Lookup a symbol in the hash table + // @param name Name of the symbol + // @param value Value of the symbol + // @param size Size of the symbol + // @param bind Binding of the symbol + // @param type Type of the symbol + // @param section_index Section index of the symbol + // @param other Other attributes of the symbol + // @return True if the symbol is found, false otherwise + //------------------------------------------------------------------------------ + bool hash_lookup( const std::string& name, + Elf64_Addr& value, + Elf_Xword& size, + unsigned char& bind, + unsigned char& type, + Elf_Half& section_index, + unsigned char& other ) const + { + bool ret = false; + const auto& convertor = elf_file.get_convertor(); + + Elf_Word nbucket = *(const Elf_Word*)hash_section->get_data(); + nbucket = ( *convertor )( nbucket ); + Elf_Word nchain = + *(const Elf_Word*)( hash_section->get_data() + sizeof( Elf_Word ) ); + nchain = ( *convertor )( nchain ); + Elf_Word val = elf_hash( (const unsigned char*)name.c_str() ); + Elf_Word y = + *(const Elf_Word*)( hash_section->get_data() + + ( 2 + val % nbucket ) * sizeof( Elf_Word ) ); + y = ( *convertor )( y ); + std::string str; + get_symbol( y, str, value, size, bind, type, section_index, other ); + while ( str != name && STN_UNDEF != y && y < nchain ) { + y = *(const Elf_Word*)( hash_section->get_data() + + ( 2 + nbucket + y ) * sizeof( Elf_Word ) ); + y = ( *convertor )( y ); + get_symbol( y, str, value, size, bind, type, section_index, other ); + } + + if ( str == name ) { + ret = true; + } + + return ret; + } + + //------------------------------------------------------------------------------ + // @brief Lookup a symbol in the GNU hash table + // @param name Name of the symbol + // @param value Value of the symbol + // @param size Size of the symbol + // @param bind Binding of the symbol + // @param type Type of the symbol + // @param section_index Section index of the symbol + // @param other Other attributes of the symbol + // @return True if the symbol is found, false otherwise + //------------------------------------------------------------------------------ + template + bool gnu_hash_lookup( const std::string& name, + Elf64_Addr& value, + Elf_Xword& size, + unsigned char& bind, + unsigned char& type, + Elf_Half& section_index, + unsigned char& other ) const + { + bool ret = false; + const auto& convertor = elf_file.get_convertor(); + + std::uint32_t nbuckets = + *( (std::uint32_t*)hash_section->get_data() + 0 ); + std::uint32_t symoffset = + *( (std::uint32_t*)hash_section->get_data() + 1 ); + std::uint32_t bloom_size = + *( (std::uint32_t*)hash_section->get_data() + 2 ); + std::uint32_t bloom_shift = + *( (std::uint32_t*)hash_section->get_data() + 3 ); + nbuckets = ( *convertor )( nbuckets ); + symoffset = ( *convertor )( symoffset ); + bloom_size = ( *convertor )( bloom_size ); + bloom_shift = ( *convertor )( bloom_shift ); + + auto* bloom_filter = + (T*)( hash_section->get_data() + 4 * sizeof( std::uint32_t ) ); + + std::uint32_t hash = elf_gnu_hash( (const unsigned char*)name.c_str() ); + std::uint32_t bloom_index = ( hash / ( 8 * sizeof( T ) ) ) % bloom_size; + T bloom_bits = + ( (T)1 << ( hash % ( 8 * sizeof( T ) ) ) ) | + ( (T)1 << ( ( hash >> bloom_shift ) % ( 8 * sizeof( T ) ) ) ); + + if ( ( ( *convertor )( bloom_filter[bloom_index] ) & bloom_bits ) != + bloom_bits ) + return ret; + + std::uint32_t bucket = hash % nbuckets; + auto* buckets = (std::uint32_t*)( hash_section->get_data() + + 4 * sizeof( std::uint32_t ) + + bloom_size * sizeof( T ) ); + auto* chains = (std::uint32_t*)( hash_section->get_data() + + 4 * sizeof( std::uint32_t ) + + bloom_size * sizeof( T ) + + nbuckets * sizeof( std::uint32_t ) ); + + if ( ( *convertor )( buckets[bucket] ) >= symoffset ) { + std::uint32_t chain_index = + ( *convertor )( buckets[bucket] ) - symoffset; + std::uint32_t chain_hash = ( *convertor )( chains[chain_index] ); + std::string symname; + + while ( true ) { + if ( ( chain_hash >> 1 ) == ( hash >> 1 ) && + get_symbol( chain_index + symoffset, symname, value, size, + bind, type, section_index, other ) && + ( name == symname ) ) { + ret = true; + break; + } + + if ( chain_hash & 1 ) + break; + + chain_hash = ( *convertor )( chains[++chain_index] ); + } + } + + return ret; + } + + //------------------------------------------------------------------------------ + // @brief Get the symbol at the specified index + // @param index Index of the symbol + // @return Pointer to the symbol + //------------------------------------------------------------------------------ + template const T* generic_get_symbol_ptr( Elf_Xword index ) const + { + if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) { + if ( symbol_section->get_entry_size() < sizeof( T ) ) { + return nullptr; + } + const auto* pSym = reinterpret_cast( + symbol_section->get_data() + + index * symbol_section->get_entry_size() ); + + return pSym; + } + + return nullptr; + } + + //------------------------------------------------------------------------------ + // @brief Search for a symbol in the section + // @param match Function to be called for each symbol + // @param idx Index of the found symbol + // @return True if the symbol is found, false otherwise + //------------------------------------------------------------------------------ + template + bool generic_search_symbols( std::function match, + Elf_Xword& idx ) const + { + for ( Elf_Xword i = 0; i < get_symbols_num(); i++ ) { + const T* symPtr = generic_get_symbol_ptr( i ); + + if ( symPtr == nullptr ) + return false; + + if ( match( symPtr ) ) { + idx = i; + return true; + } + } + + return false; + } + + //------------------------------------------------------------------------------ + // @brief Get the symbol at the specified index + // @param index Index of the symbol + // @param name Name of the symbol + // @param value Value of the symbol + // @param size Size of the symbol + // @param bind Binding of the symbol + // @param type Type of the symbol + // @param section_index Section index of the symbol + // @param other Other attributes of the symbol + // @return True if the symbol is found, false otherwise + //------------------------------------------------------------------------------ + template + bool generic_get_symbol( Elf_Xword index, + std::string& name, + Elf64_Addr& value, + Elf_Xword& size, + unsigned char& bind, + unsigned char& type, + Elf_Half& section_index, + unsigned char& other ) const + { + bool ret = false; + + if ( nullptr != symbol_section->get_data() && + index < get_symbols_num() ) { + const auto* pSym = reinterpret_cast( + symbol_section->get_data() + + index * symbol_section->get_entry_size() ); + + const auto& convertor = elf_file.get_convertor(); + + section* string_section = + elf_file.sections[get_string_table_index()]; + string_section_accessor str_reader( string_section ); + const char* pStr = + str_reader.get_string( ( *convertor )( pSym->st_name ) ); + if ( nullptr != pStr ) { + name = pStr; + } + value = ( *convertor )( pSym->st_value ); + size = ( *convertor )( pSym->st_size ); + bind = ELF_ST_BIND( pSym->st_info ); + type = ELF_ST_TYPE( pSym->st_info ); + section_index = ( *convertor )( pSym->st_shndx ); + other = pSym->st_other; + + ret = true; + } + + return ret; + } + + //------------------------------------------------------------------------------ + // @brief Add a symbol to the section + // @param name Name of the symbol + // @param value Value of the symbol + // @param size Size of the symbol + // @param info Info of the symbol + // @param other Other attributes of the symbol + // @param shndx Section index of the symbol + // @return Index of the added symbol + //------------------------------------------------------------------------------ + template + Elf_Word generic_add_symbol( Elf_Word name, + Elf64_Addr value, + Elf_Xword size, + unsigned char info, + unsigned char other, + Elf_Half shndx ) + { + const auto& convertor = elf_file.get_convertor(); + + T entry; + entry.st_name = ( *convertor )( name ); + entry.st_value = decltype( entry.st_value )( value ); + entry.st_value = ( *convertor )( entry.st_value ); + entry.st_size = decltype( entry.st_size )( size ); + entry.st_size = ( *convertor )( entry.st_size ); + entry.st_info = ( *convertor )( info ); + entry.st_other = ( *convertor )( other ); + entry.st_shndx = ( *convertor )( shndx ); + + symbol_section->append_data( reinterpret_cast( &entry ), + sizeof( entry ) ); + + Elf_Word nRet = + Elf_Word( symbol_section->get_size() / sizeof( entry ) - 1 ); + + return nRet; + } + + //------------------------------------------------------------------------------ + // @brief Arrange local symbols in the section + // @param func Function to be called for each pair of symbols + // @return Number of local symbols + //------------------------------------------------------------------------------ + template + Elf_Xword generic_arrange_local_symbols( + std::function func ) + { + const auto& convertor = elf_file.get_convertor(); + + Elf_Word first_not_local = + 1; // Skip the first entry. It is always NOTYPE + Elf_Xword current = 0; + Elf_Xword count = get_symbols_num(); + + while ( true ) { + T* p1 = nullptr; + T* p2 = nullptr; + + while ( first_not_local < count ) { + p1 = const_cast( + generic_get_symbol_ptr( first_not_local ) ); + if ( ELF_ST_BIND( ( *convertor )( p1->st_info ) ) != STB_LOCAL ) + break; + ++first_not_local; + } + + current = first_not_local + 1; + while ( current < count ) { + p2 = const_cast( generic_get_symbol_ptr( current ) ); + if ( ELF_ST_BIND( ( *convertor )( p2->st_info ) ) == STB_LOCAL ) + break; + ++current; + } + + if ( first_not_local < count && current < count ) { + if ( func ) + func( first_not_local, current ); + + std::swap( *p1, *p2 ); + } + else { + // Update 'info' field of the section + symbol_section->set_info( first_not_local ); + break; + } + } + + return first_not_local; + } + + //------------------------------------------------------------------------------ + private: + const elfio& elf_file; ///< Reference to the ELF file + S* symbol_section; ///< Pointer to the symbol section + Elf_Half hash_section_index{ 0 }; ///< Index of the hash section + const section* hash_section{ nullptr }; ///< Pointer to the hash section +}; + +using symbol_section_accessor = symbol_section_accessor_template
; +using const_symbol_section_accessor = + symbol_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_SYMBOLS_HPP diff --git a/vyld/elfio/elfio_utils.hpp b/vyld/elfio/elfio_utils.hpp new file mode 100644 index 0000000..d24555e --- /dev/null +++ b/vyld/elfio/elfio_utils.hpp @@ -0,0 +1,373 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_UTILS_HPP +#define ELFIO_UTILS_HPP + +#include +#include +#include + +#define ELFIO_GET_ACCESS_DECL( TYPE, NAME ) virtual TYPE get_##NAME() const = 0 + +#define ELFIO_SET_ACCESS_DECL( TYPE, NAME ) \ + virtual void set_##NAME( const TYPE& value ) = 0 + +#define ELFIO_GET_SET_ACCESS_DECL( TYPE, NAME ) \ + virtual TYPE get_##NAME() const = 0; \ + virtual void set_##NAME( const TYPE& value ) = 0 + +#define ELFIO_GET_ACCESS( TYPE, NAME, FIELD ) \ + TYPE get_##NAME() const override { return ( *convertor )( FIELD ); } + +#define ELFIO_SET_ACCESS( TYPE, NAME, FIELD ) \ + void set_##NAME( const TYPE& value ) override \ + { \ + FIELD = decltype( FIELD )( value ); \ + FIELD = ( *convertor )( FIELD ); \ + } +#define ELFIO_GET_SET_ACCESS( TYPE, NAME, FIELD ) \ + TYPE get_##NAME() const override { return ( *convertor )( FIELD ); } \ + void set_##NAME( const TYPE& value ) override \ + { \ + FIELD = decltype( FIELD )( value ); \ + FIELD = ( *convertor )( FIELD ); \ + } + +namespace ELFIO { + +//------------------------------------------------------------------------------ +//! \class endianness_convertor +//! \brief Class for converting endianness of data +class endianness_convertor +{ + public: + //------------------------------------------------------------------------------ + //! \brief Setup the endianness convertor + //! \param elf_file_encoding The encoding of the ELF file + void setup( unsigned char elf_file_encoding ) + { + need_conversion = ( elf_file_encoding != get_host_encoding() ); + } + + //------------------------------------------------------------------------------ + //! \brief Convert a 64-bit unsigned integer + //! \param value The value to convert + //! \return The converted value + std::uint64_t operator()( std::uint64_t value ) const + { + if ( !need_conversion ) { + return value; + } + value = ( ( value & 0x00000000000000FFuLL ) << 56 ) | + ( ( value & 0x000000000000FF00uLL ) << 40 ) | + ( ( value & 0x0000000000FF0000uLL ) << 24 ) | + ( ( value & 0x00000000FF000000uLL ) << 8 ) | + ( ( value & 0x000000FF00000000uLL ) >> 8 ) | + ( ( value & 0x0000FF0000000000uLL ) >> 24 ) | + ( ( value & 0x00FF000000000000uLL ) >> 40 ) | + ( ( value & 0xFF00000000000000uLL ) >> 56 ); + + return value; + } + + //------------------------------------------------------------------------------ + //! \brief Convert a 64-bit signed integer + //! \param value The value to convert + //! \return The converted value + std::int64_t operator()( std::int64_t value ) const + { + if ( !need_conversion ) { + return value; + } + return ( std::int64_t )( *this )( (std::uint64_t)value ); + } + + //------------------------------------------------------------------------------ + //! \brief Convert a 32-bit unsigned integer + //! \param value The value to convert + //! \return The converted value + std::uint32_t operator()( std::uint32_t value ) const + { + if ( !need_conversion ) { + return value; + } + value = + ( ( value & 0x000000FF ) << 24 ) | ( ( value & 0x0000FF00 ) << 8 ) | + ( ( value & 0x00FF0000 ) >> 8 ) | ( ( value & 0xFF000000 ) >> 24 ); + + return value; + } + + //------------------------------------------------------------------------------ + //! \brief Convert a 32-bit signed integer + //! \param value The value to convert + //! \return The converted value + std::int32_t operator()( std::int32_t value ) const + { + if ( !need_conversion ) { + return value; + } + return ( std::int32_t )( *this )( (std::uint32_t)value ); + } + + //------------------------------------------------------------------------------ + //! \brief Convert a 16-bit unsigned integer + //! \param value The value to convert + //! \return The converted value + std::uint16_t operator()( std::uint16_t value ) const + { + if ( !need_conversion ) { + return value; + } + value = ( std::uint16_t )( ( value & 0x00FF ) << 8 ) | + ( ( value & 0xFF00 ) >> 8 ); + + return value; + } + + //------------------------------------------------------------------------------ + //! \brief Convert a 16-bit signed integer + //! \param value The value to convert + //! \return The converted value + std::int16_t operator()( std::int16_t value ) const + { + if ( !need_conversion ) { + return value; + } + return ( std::int16_t )( *this )( (std::uint16_t)value ); + } + + //------------------------------------------------------------------------------ + //! \brief Convert an 8-bit signed integer + //! \param value The value to convert + //! \return The converted value + std::int8_t operator()( std::int8_t value ) const { return value; } + + //------------------------------------------------------------------------------ + //! \brief Convert an 8-bit unsigned integer + //! \param value The value to convert + //! \return The converted value + std::uint8_t operator()( std::uint8_t value ) const { return value; } + + //------------------------------------------------------------------------------ + private: + //------------------------------------------------------------------------------ + //! \brief Get the host encoding + //! \return The host encoding + unsigned char get_host_encoding() const + { + static const int tmp = 1; + if ( 1 == *reinterpret_cast( &tmp ) ) { + return ELFDATA2LSB; + } + else { + return ELFDATA2MSB; + } + } + + //------------------------------------------------------------------------------ + bool need_conversion = false; //!< Flag indicating if conversion is needed +}; + +//------------------------------------------------------------------------------ +//! \struct address_translation +//! \brief Structure for address translation +struct address_translation +{ + //------------------------------------------------------------------------------ + //! \brief Constructor + //! \param start The start address + //! \param size The size of the address range + //! \param mapped_to The mapped address + address_translation( std::uint64_t start, + std::uint64_t size, + std::uint64_t mapped_to ) + : start( start ), size( size ), mapped_to( mapped_to ){}; + std::streampos start; //!< Start address + std::streampos size; //!< Size of the address range + std::streampos mapped_to; //!< Mapped address +}; + +//------------------------------------------------------------------------------ +//! \class address_translator +//! \brief Class for translating addresses +class address_translator +{ + public: + //------------------------------------------------------------------------------ + //! \brief Set address translation + //! \param addr_trans Vector of address translations + void set_address_translation( std::vector& addr_trans ) + { + addr_translations = addr_trans; + + std::sort( addr_translations.begin(), addr_translations.end(), + []( const address_translation& a, + const address_translation& b ) -> bool { + return a.start < b.start; + } ); + } + + //------------------------------------------------------------------------------ + //! \brief Translate an address + //! \param value The address to translate + //! \return The translated address + std::streampos operator[]( std::streampos value ) const + { + if ( addr_translations.empty() ) { + return value; + } + + for ( auto& t : addr_translations ) { + if ( ( t.start <= value ) && ( ( value - t.start ) < t.size ) ) { + return value - t.start + t.mapped_to; + } + } + + return value; + } + + //------------------------------------------------------------------------------ + //! \brief Check if the address translator is empty + //! \return True if empty, false otherwise + bool empty() const { return addr_translations.empty(); } + + private: + std::vector + addr_translations; //!< Vector of address translations +}; + +//------------------------------------------------------------------------------ +//! \brief Calculate the ELF hash of a name +//! \param name The name to hash +//! \return The ELF hash +inline std::uint32_t elf_hash( const unsigned char* name ) +{ + std::uint32_t h = 0; + std::uint32_t g = 0; + while ( *name != '\0' ) { + h = ( h << 4 ) + *name++; + g = h & 0xf0000000; + if ( g != 0 ) + h ^= g >> 24; + h &= ~g; + } + return h; +} + +//------------------------------------------------------------------------------ +//! \brief Calculate the GNU hash of a name +//! \param s The name to hash +//! \return The GNU hash +inline std::uint32_t elf_gnu_hash( const unsigned char* s ) +{ + std::uint32_t h = 0x1505; + for ( unsigned char c = *s; c != '\0'; c = *++s ) + h = ( h << 5 ) + h + c; + return h; +} + +//------------------------------------------------------------------------------ +//! \brief Convert a value to a hexadecimal string +//! \param value The value to convert +//! \return The hexadecimal string +inline std::string to_hex_string( std::uint64_t value ) +{ + std::string str; + + while ( value ) { + if ( auto digit = value & 0xF; digit < 0xA ) { + str = char( '0' + digit ) + str; + } + else { + str = char( 'A' + digit - 0xA ) + str; + } + value >>= 4; + } + + return "0x" + str; +} + +//------------------------------------------------------------------------------ +//! \brief Adjust the size of a stream +//! \param stream The stream to adjust +//! \param offset The offset to adjust to +inline void adjust_stream_size( std::ostream& stream, std::streamsize offset ) +{ + stream.seekp( 0, std::ios_base::end ); + if ( stream.tellp() < offset ) { + std::streamsize size = offset - stream.tellp(); + stream.write( std::string( size_t( size ), '\0' ).c_str(), size ); + } + stream.seekp( offset ); +} + +//------------------------------------------------------------------------------ +//! \brief Get the length of a string with a maximum length +//! \param s The string +//! \param n The maximum length +//! \return The length of the string +inline static size_t strnlength( const char* s, size_t n ) +{ + auto found = (const char*)std::memchr( s, '\0', n ); + return found ? (size_t)( found - s ) : n; +} + +//------------------------------------------------------------------------------ +//! \class compression_interface +//! \brief Interface for compression and decompression +class compression_interface +{ + public: + virtual ~compression_interface() = default; + + //------------------------------------------------------------------------------ + //! \brief Decompress a compressed section + //! \param data The buffer of compressed data + //! \param convertor Pointer to an endianness convertor instance + //! \param compressed_size The size of the compressed data buffer + //! \param uncompressed_size Reference to a variable to store the decompressed buffer size + //! \return A smart pointer to the decompressed data + virtual std::unique_ptr + inflate( const char* data, + std::shared_ptr convertor, + Elf_Xword compressed_size, + Elf_Xword& uncompressed_size ) const = 0; + + //------------------------------------------------------------------------------ + //! \brief Compress a section + //! \param data The buffer of uncompressed data + //! \param convertor Pointer to an endianness convertor instance + //! \param decompressed_size The size of the uncompressed data buffer + //! \param compressed_size Reference to a variable to store the compressed buffer size + //! \return A smart pointer to the compressed data + virtual std::unique_ptr + deflate( const char* data, + std::shared_ptr convertor, + Elf_Xword decompressed_size, + Elf_Xword& compressed_size ) const = 0; +}; + +} // namespace ELFIO + +#endif // ELFIO_UTILS_HPP diff --git a/vyld/elfio/elfio_version.hpp b/vyld/elfio/elfio_version.hpp new file mode 100644 index 0000000..0a1426d --- /dev/null +++ b/vyld/elfio/elfio_version.hpp @@ -0,0 +1,4 @@ +//------------------------------------------------------------------------------ +//! \def ELFIO_VERSION +//! \brief Defines the version of the ELFIO library +#define ELFIO_VERSION "3.14" diff --git a/vyld/elfio/elfio_versym.hpp b/vyld/elfio/elfio_versym.hpp new file mode 100644 index 0000000..16f735f --- /dev/null +++ b/vyld/elfio/elfio_versym.hpp @@ -0,0 +1,310 @@ +/* +Copyright (C) 2001-present by Serge Lamikhov-Center + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef ELFIO_VERSYM_HPP +#define ELFIO_VERSYM_HPP + +namespace ELFIO { + +//------------------------------------------------------------------------------ +//! \class versym_section_accessor_template +//! \brief Class for accessing version symbol section data +template class versym_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + //! \brief Constructor + //! \param section Pointer to the section + explicit versym_section_accessor_template( S* section ) + : versym_section( section ) + { + if ( section != nullptr ) { + entries_num = decltype( entries_num )( section->get_size() / + sizeof( Elf_Half ) ); + } + } + + //------------------------------------------------------------------------------ + //! \brief Get the number of entries + //! \return Number of entries + Elf_Word get_entries_num() const + { + if ( versym_section ) { + return entries_num; + } + return 0; + } + + //------------------------------------------------------------------------------ + //! \brief Get an entry + //! \param no Index of the entry + //! \param value Value of the entry + //! \return True if successful, false otherwise + bool get_entry( Elf_Word no, Elf_Half& value ) const + { + if ( versym_section && ( no < get_entries_num() ) ) { + value = ( (Elf_Half*)versym_section->get_data() )[no]; + return true; + } + + return false; + } + + //------------------------------------------------------------------------------ + //! \brief Modify an entry + //! \param no Index of the entry + //! \param value New value of the entry + //! \return True if successful, false otherwise + bool modify_entry( Elf_Word no, Elf_Half value ) + { + if ( versym_section && ( no < get_entries_num() ) ) { + ( (Elf_Half*)versym_section->get_data() )[no] = value; + return true; + } + + return false; + } + + //------------------------------------------------------------------------------ + //! \brief Add an entry + //! \param value Value of the entry + //! \return True if successful, false otherwise + bool add_entry( Elf_Half value ) + { + if ( !versym_section ) { + return false; + } + + versym_section->append_data( (const char*)&value, sizeof( Elf_Half ) ); + ++entries_num; + + return true; + } + + //------------------------------------------------------------------------------ + private: + S* versym_section = nullptr; //!< Pointer to the section + Elf_Word entries_num = 0; //!< Number of entries +}; + +using versym_section_accessor = versym_section_accessor_template
; +using const_versym_section_accessor = + versym_section_accessor_template; + +//------------------------------------------------------------------------------ +//! \class versym_r_section_accessor_template +//! \brief Class for accessing version requirement section data +template class versym_r_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + //! \brief Constructor + //! \param elf_file Reference to the ELF file + //! \param versym_r_section Pointer to the version requirement section + versym_r_section_accessor_template( const elfio& elf_file, + S* versym_r_section ) + : elf_file( elf_file ), versym_r_section( versym_r_section ), + entries_num( 0 ) + { + // Find .dynamic section + const section* dynamic_section = elf_file.sections[".dynamic"]; + + if ( dynamic_section == nullptr ) { + return; + } + + const_dynamic_section_accessor dynamic_section_acc( elf_file, + dynamic_section ); + Elf_Xword dyn_sec_num = dynamic_section_acc.get_entries_num(); + for ( Elf_Xword i = 0; i < dyn_sec_num; ++i ) { + Elf_Xword tag; + Elf_Xword value; + std::string str; + + if ( dynamic_section_acc.get_entry( i, tag, value, str ) && + tag == DT_VERNEEDNUM ) { + entries_num = (Elf_Word)value; + break; + } + } + } + + //------------------------------------------------------------------------------ + //! \brief Get the number of entries + //! \return Number of entries + Elf_Word get_entries_num() const { return entries_num; } + + //------------------------------------------------------------------------------ + //! \brief Get an entry + //! \param no Index of the entry + //! \param version Version of the entry + //! \param file_name File name of the entry + //! \param hash Hash of the entry + //! \param flags Flags of the entry + //! \param other Other information of the entry + //! \param dep_name Dependency name of the entry + //! \return True if successful, false otherwise + bool get_entry( Elf_Word no, + Elf_Half& version, + std::string& file_name, + Elf_Word& hash, + Elf_Half& flags, + Elf_Half& other, + std::string& dep_name ) const + { + if ( versym_r_section == nullptr || ( no >= get_entries_num() ) ) { + return false; + } + + const_string_section_accessor string_section_acc( + elf_file.sections[versym_r_section->get_link()] ); + + Elfxx_Verneed* verneed = (Elfxx_Verneed*)versym_r_section->get_data(); + Elfxx_Vernaux* veraux = + (Elfxx_Vernaux*)( (char*)verneed + verneed->vn_aux ); + for ( Elf_Word i = 0; i < no; ++i ) { + verneed = (Elfxx_Verneed*)( (char*)verneed + verneed->vn_next ); + veraux = (Elfxx_Vernaux*)( (char*)verneed + verneed->vn_aux ); + } + + version = verneed->vn_version; + file_name = string_section_acc.get_string( verneed->vn_file ); + hash = veraux->vna_hash; + flags = veraux->vna_flags; + other = veraux->vna_other; + dep_name = string_section_acc.get_string( veraux->vna_name ); + + return true; + } + + //------------------------------------------------------------------------------ + private: + const elfio& elf_file; + S* versym_r_section = + nullptr; //!< Pointer to the version requirement section + Elf_Word entries_num = 0; //!< Number of entries +}; + +using versym_r_section_accessor = versym_r_section_accessor_template
; +using const_versym_r_section_accessor = + versym_r_section_accessor_template; + +//------------------------------------------------------------------------------ +//! \class versym_d_section_accessor_template +//! \brief Class for accessing version definition section data +template class versym_d_section_accessor_template +{ + public: + //------------------------------------------------------------------------------ + //! \brief Constructor + //! \param elf_file Reference to the ELF file + //! \param versym_d_section Pointer to the version definition section + versym_d_section_accessor_template( const elfio& elf_file, + S* versym_d_section ) + : elf_file( elf_file ), versym_d_section( versym_d_section ), + entries_num( 0 ) + { + // Find .dynamic section + const section* dynamic_section = elf_file.sections[".dynamic"]; + + if ( dynamic_section == nullptr ) { + return; + } + + const_dynamic_section_accessor dynamic_section_acc( elf_file, + dynamic_section ); + Elf_Xword dyn_sec_num = dynamic_section_acc.get_entries_num(); + for ( Elf_Xword i = 0; i < dyn_sec_num; ++i ) { + Elf_Xword tag; + Elf_Xword value; + std::string str; + + if ( dynamic_section_acc.get_entry( i, tag, value, str ) && + tag == DT_VERDEFNUM ) { + entries_num = (Elf_Word)value; + break; + } + } + } + + //------------------------------------------------------------------------------ + //! \brief Get the number of entries + //! \return Number of entries + Elf_Word get_entries_num() const { return entries_num; } + + //------------------------------------------------------------------------------ + //! \brief Get an entry + //! \param no Index of the entry + //! \param flags Flags of the entry + //! \param version_index Version index of the entry + //! \param hash Hash of the entry + //! \param dep_name Dependency name of the entry + //! \return True if successful, false otherwise + bool get_entry( Elf_Word no, + Elf_Half& flags, + Elf_Half& version_index, + Elf_Word& hash, + std::string& dep_name ) const + { + if ( versym_d_section == nullptr || ( no >= get_entries_num() ) ) { + return false; + } + + const_string_section_accessor string_section_acc( + elf_file.sections[versym_d_section->get_link()] ); + + Elfxx_Verdef* verdef = (Elfxx_Verdef*)versym_d_section->get_data(); + Elfxx_Verdaux* verdaux = + (Elfxx_Verdaux*)( (char*)verdef + verdef->vd_aux ); + for ( Elf_Word i = 0; i < no; ++i ) { + verdef = (Elfxx_Verdef*)( (char*)verdef + verdef->vd_next ); + verdaux = (Elfxx_Verdaux*)( (char*)verdef + verdef->vd_aux ); + } + + // verdef->vd_version should always be 1 + // see https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA.junk/symversion.html#VERDEFENTRIES + // verdef->vd_cnt should always be 1. + // see https://maskray.me/blog/2020-11-26-all-about-symbol-versioning + + flags = verdef->vd_flags; + version_index = verdef->vd_ndx; + hash = verdef->vd_hash; + dep_name = string_section_acc.get_string( verdaux->vda_name ); + + return true; + } + + //------------------------------------------------------------------------------ + private: + const elfio& elf_file; + S* versym_d_section = + nullptr; //!< Pointer to the version definition section + Elf_Word entries_num = 0; //!< Number of entries +}; + +using versym_d_section_accessor = versym_d_section_accessor_template
; +using const_versym_d_section_accessor = + versym_d_section_accessor_template; + +} // namespace ELFIO + +#endif // ELFIO_VERSYM_HPP diff --git a/vyld/vyld.cpp b/vyld/vyld.cpp new file mode 100644 index 0000000..98cb126 --- /dev/null +++ b/vyld/vyld.cpp @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: MPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "elfio.hpp" +#include "elfio/elf_types.hpp" +using namespace std; +namespace fs=filesystem; +const string entry_flags="-ffreestanding -nostdlib -nostartfiles -Wall -Wextra -fno-stack-protector -fno-builtin -fno-pic -m64 -mcmodel=large"; +struct vyx_addresses { + uint64_t text_offset; + uint64_t stack_base; +}; +uint64_t rand_in_range(uint64_t min, uint64_t max) { + static random_device rd; + static mt19937_64 gen(rd()); + std::uniform_int_distribution dis(min, max); + return dis(gen); +} +uint64_t random_canonical(uint64_t start,uint64_t end) { + uint64_t addr=rand_in_range(start/4096,end/4096)*4096; + return addr; +} +vyx_addresses generate_vyx_addresses() { + vyx_addresses addr; + addr.text_offset=random_canonical(0xFFFF800000000000ULL,0xFFFF900000000000ULL-1); + uint64_t stack_window_start=0xFFFFF00000000000ULL; + uint64_t stack_window_end=0xFFFFFF8000000000ULL-0x100000ULL; + addr.stack_base=random_canonical(stack_window_start,stack_window_end); + return addr; +} +size_t pad_size(size_t value,size_t multiple) { + if (multiple==0) return value; + size_t remainder=value%multiple; + if (remainder==0) return value; + return value+multiple-remainder; +} +int main(int argc,char **argv) { + cout<<"[Vyld] Vystem Executable Linker v0.1"< "+string(res_file)).c_str()); + if (ret!=0) { + cout<<"[Vyld] Error: gcc didn't returned success. Error code: "< c_files(argv+1,argv+argc-1); + vector o_files; + cout<<"[Vyld] Finding provided files:"< "+string(res_file)).c_str()); + if (ret!=0) { + cout<<"[Vyld] Error: gcc didn't returned success. File \""< "+string(res_file); + ret=system(command_line_gcc.c_str()); + if (ret!=0) { + cout<get_size()==0) { + cout<get_size(); + size_t text_size_padded=pad_size(text_size,4096); + cout<<"[Vyld] text_size=0x"<get_size(); + data_size_padded=pad_size(data_size,4096); + cout<<"[Vyld] data_size=0x"<get_size(); + for (const auto &s:reader.sections) { + if (s->get_name().size()>7 && s->get_name().substr(0,7)==".rodata" && s->get_name()!=".rodata") { + rodata_size+=s->get_size(); + } + } + rodata_size_padded=pad_size(rodata_size,4096); + cout<<"[Vyld] rodata_size=0x"<get_size(); + bss_size_padded=pad_size(bss_size,4096); + cout<<"[Vyld] bss_size=0x"< TEXT\n\n"; + ld<<" .data :\n"; + ld<<" {\n"; + ld<<" . = ORIGIN(DATA);\n"; + ld<<" *(.data)\n"; + ld<<" *(.data.*)\n"; + ld<<" . = ALIGN(0x1000);\n"; + ld<<" } > DATA\n\n"; + ld<<" .rodata :\n"; + ld<<" {\n"; + ld<<" . = ORIGIN(RODATA);\n"; + ld<<" *(.rodata)\n"; + ld<<" *(.rodata.*)\n"; + ld<<" . = ALIGN(0x1000);\n"; + ld<<" } > RODATA\n\n"; + ld<<" .bss :\n"; + ld<<" {\n"; + ld<<" . = ORIGIN(BSS);\n"; + ld<<" *(.bss)\n"; + ld<<" *(.bss.*)\n"; + ld<<" . = ALIGN(0x1000);\n"; + ld<<" } > BSS\n\n"; + ld<<" /DISCARD/ :\n"; + ld<<" {\n"; + ld<<" *(.note.*)\n"; + ld<<" *(.comment)\n"; + ld<<" *(.eh_frame)\n"; + ld<<" *(.eh_frame_hdr)\n"; + ld<<" }\n}"; + ld.close(); + cout<<"[Vyld] Linking relocatable object."<get_size()==0) { + cout<<"[Vyld] Error: .text is empty."< elf_text_bin(text_size_padded,0); + vector elf_data_bin(data_size_padded,0); + vector elf_rodata_bin(rodata_size_padded,0); + memcpy(elf_text_bin.data(),elf_text_sec->get_data(),elf_text_sec->get_size()); + if (elf_data_sec->get_size()!=0) { + memcpy(elf_data_bin.data(),elf_data_sec->get_data(),elf_data_sec->get_size()); + } + if (elf_rodata_sec->get_size()!=0) { + memcpy(elf_rodata_bin.data(),elf_rodata_sec->get_data(),elf_rodata_sec->get_size()); + } + uint8_t vyx_sig[3]={'V','y','X'}; + uint16_t vyx_ver=0x0001; + string outfile=string(argv[argc-1]); + ofstream out(outfile,ios::binary); + out.write(reinterpret_cast(vyx_sig),3); + out.write(reinterpret_cast(&vyx_ver),2); + out.write(reinterpret_cast(&va.text_offset),8); + out.write(reinterpret_cast(&va.stack_base),8); + out.write(reinterpret_cast(&text_size_padded),8); + out.write(reinterpret_cast(&data_size_padded),8); + out.write(reinterpret_cast(&rodata_size_padded),8); + uint64_t bss_size_elf=elf_bss_sec->get_size(); + out.write(reinterpret_cast(&bss_size_elf),8); + out.write(reinterpret_cast(elf_text_bin.data()),elf_text_bin.size()); + if (data_size_padded!=0) out.write(reinterpret_cast(elf_data_bin.data()),elf_data_bin.size()); + if (rodata_size_padded!=0) out.write(reinterpret_cast(elf_rodata_bin.data()),elf_rodata_bin.size()); + out.close(); + cout<<"[Vyld] Succesfully compiled "<