summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2024-10-03 16:08:46 +0200
committerAnton Kling <anton@kling.gg>2024-10-03 16:09:12 +0200
commit372c633aa22d826bc515213ad4211791440f60f8 (patch)
treeb2de66c9bf612a0a149c65786335a6cebde37840
parent226d861c9ebb7f09f95665d07d9ab5c6b7ed7d6f (diff)
libc: Add sendfile and queue
-rw-r--r--kernel/cpu/syscall.c79
-rw-r--r--userland/libc/Makefile2
-rw-r--r--userland/libc/fcntl/fcntl.c15
-rw-r--r--userland/libc/include/queue.h21
-rw-r--r--userland/libc/include/sys/sendfile.h7
-rw-r--r--userland/libc/include/syscall.h1
-rw-r--r--userland/libc/include/tb/sha1.h26
-rw-r--r--userland/libc/queue.c15
-rw-r--r--userland/libc/sys/sendfile.c12
-rw-r--r--userland/libc/tb/sha1.c249
10 files changed, 425 insertions, 2 deletions
diff --git a/kernel/cpu/syscall.c b/kernel/cpu/syscall.c
index 0dd2356..d31835d 100644
--- a/kernel/cpu/syscall.c
+++ b/kernel/cpu/syscall.c
@@ -567,6 +567,83 @@ int syscall_queue_wait(int fd, struct queue_entry *events, int num_events) {
return queue_wait(fd, events, num_events);
}
+u32 syscall_sendfile(int out_fd, int in_fd, off_t *offset, size_t count,
+ int *error_rc) {
+ int is_inf = (0 == count);
+
+ if (offset) {
+ if (!mmu_is_valid_userpointer(offset, sizeof(off_t))) {
+ return -EFAULT;
+ }
+ }
+ if (error_rc) {
+ if (!mmu_is_valid_userpointer(error_rc, sizeof(int))) {
+ return -EFAULT;
+ }
+ }
+
+ vfs_fd_t *in_fd_ptr = get_vfs_fd(in_fd, NULL);
+ if (!in_fd_ptr) {
+ return -EBADF;
+ }
+
+ vfs_fd_t *out_fd_ptr = get_vfs_fd(out_fd, NULL);
+ if (!out_fd_ptr) {
+ return -EBADF;
+ }
+
+ int real_rc = 0;
+
+ off_t read_offset = (offset) ? (*offset) : in_fd_ptr->offset;
+
+ for (; count > 0 || is_inf;) {
+ u8 buffer[8192];
+ int read_amount = min(sizeof(buffer), count);
+ if (is_inf) {
+ read_amount = sizeof(buffer);
+ }
+ int rc = vfs_pread(in_fd, buffer, read_amount, read_offset);
+ if (0 > rc) {
+ if (error_rc) {
+ *error_rc = rc;
+ }
+ return real_rc;
+ }
+
+ if (0 == rc) {
+ break;
+ }
+
+ int rc2 = vfs_pwrite(out_fd, buffer, rc, out_fd_ptr->offset);
+ if (0 > rc2) {
+ if (error_rc) {
+ *error_rc = rc2;
+ }
+ return real_rc;
+ }
+ real_rc += rc2;
+ if (!is_inf) {
+ count -= rc2;
+ }
+ out_fd_ptr->offset += rc2;
+
+ read_offset += rc2;
+ if (offset) {
+ *offset = read_offset;
+ } else {
+ in_fd_ptr->offset = read_offset;
+ }
+
+ if (rc < read_amount) {
+ break;
+ }
+ }
+ if (error_rc) {
+ *error_rc = 0;
+ }
+ return real_rc;
+}
+
int (*syscall_functions[])() = {
(void(*))syscall_open,
(void(*))syscall_read,
@@ -614,7 +691,7 @@ int (*syscall_functions[])() = {
(void(*))syscall_queue_create,
(void(*))syscall_queue_mod_entries,
(void(*))syscall_queue_wait,
-
+ (void(*))syscall_sendfile,
};
void int_syscall(reg_t *r);
diff --git a/userland/libc/Makefile b/userland/libc/Makefile
index 34e9016..a41e8e4 100644
--- a/userland/libc/Makefile
+++ b/userland/libc/Makefile
@@ -2,7 +2,7 @@ CC="i686-sb-gcc"
AR="i686-sb-ar"
AS="i686-sb-as"
CFLAGS = -ggdb -ffreestanding -nostdlib -Ofast -Wall -Wextra -pedantic -Werror -Wimplicit-fallthrough -I./include/ -static -I../../include/ -Wno-int-conversion -Wno-unused-parameter
-OBJ=crt0.o libc.o malloc/malloc.o pty.o sys/mman/mmap.o sys/mman/munmap.o memset.o assert.o stdio/snprintf.o stdio/vfprintf.o string/memcpy.o string/memcmp.o string/strcmp.o ubsan.o string/strcpy.o isspace.o stdio/puts.o stdio/putchar.o dirent/opendir.o dirent/readdir.o dirent/closedir.o unistd/getopt.o dirent/scandir.o dirent/alphasort.o stdio/printf.o stdio/vdprintf.o stdio/vprintf.o stdio/dprintf.o stdio/vprintf.o string/strlen.o string/strnlen.o stdio/stdin.o stdio/getchar.o stdio/fgetc.o arpa/inet/htons.o arpa/inet/htonl.o stdio/fread.o stdio/fwrite.o stdio/fopen.o stdio/fclose.o stdio/fseek.o ctype/isascii.o stdio/fprintf.o stdlib/atoi.o stdlib/strtol.o ctype/toupper.o ctype/tolower.o string/strcat.o string/strchr.o string/sscanf.o sys/stat/stat.o stdlib/getenv.o string/strrchr.o stdio/ftell.o stdio/tmpfile.o stdio/fgets.o stdio/feof.o stdio/fscanf.o stdio/ungetc.o string/strncmp.o stdio/fputc.o string/strncpy.o stdio/remove.o stdio/ferror.o stdio/fputs.o stdlib/rand.o stdlib/srand.o unistd/getpid.o stdlib/strtoul.o stdio/fflush.o stdlib/abort.o string/strcspn.o time/localtime.o time/time.o time/clock_gettime.o time/gmtime.o time/strftime.o string/strpbrk.o ctype/isdigit.o ctype/isalpha.o ctype/isxdigit.o ctype/ispunct.o stdio/setvbuf.o stdio/fileno.o stdio/putc.o stdio/sprintf.o stdlib/abs.o string/strspn.o stdlib/qsort.o string/memmove.o setjmp/longjmp.o setjmp/setjmp.o libgen/basename.o string/strdup.o string/strndup.o string/strlcpy.o stdlib/atexit.o stdio/open_memstream.o libgen/dirname.o unistd/unlink.o string/strstr.o string/strcasecmp.o string/strncasecmp.o stdlib/mkstemp.o string/strtok.o unistd/execvp.o unistd/_exit.o ctype/isalnum.o time/ctime_r.o stdlib/strtold.o sys/time/gettimeofday.o stdio/fgetpos.o stdio/fsetpos.o ctype/isprint.o stdlib/system.o stdio/tmpnam.o unistd/msleep.o stdlib/atof.o stdlib/strtod.o stdio/rename.o sys/stat/mkdir.o unistd/uptime.o unistd/ftruncate.o sys/socket/recvfrom.o sys/socket/sendto.o signal/kill.o signal/sigaction.o unistd/chdir.o unistd/getcwd.o stdio/getdelim.o stdio/getline.o unistd/isatty.o sys/socket/listen.o stdlib/realpath.o systemcall.o sys/random/randomfill.o fcntl/open.o unistd/write.o unistd/pwrite.o fcntl/open_process.o tb/sb.o tb/sv.o string/memchr.o stdlib/atol.o stdlib/atoll.o stdlib/strtoll.o sys/stat/fstat.o unistd/lseek.o ctype/isupper.o ctype/islower.o ctype/isblank.o ctype/isgraph.o ctype/iscntrl.o math/ldexp.o sys/socket/connect.o sys/socket/setsockopt.o arpa/inet/ntohl.o arpa/inet/ntohs.o netdb/getaddrinfo.o tb/sha1.o sys/socket/getpeername.o fcntl/fcntl.o queue.o
+OBJ=crt0.o libc.o malloc/malloc.o pty.o sys/mman/mmap.o sys/mman/munmap.o memset.o assert.o stdio/snprintf.o stdio/vfprintf.o string/memcpy.o string/memcmp.o string/strcmp.o ubsan.o string/strcpy.o isspace.o stdio/puts.o stdio/putchar.o dirent/opendir.o dirent/readdir.o dirent/closedir.o unistd/getopt.o dirent/scandir.o dirent/alphasort.o stdio/printf.o stdio/vdprintf.o stdio/vprintf.o stdio/dprintf.o stdio/vprintf.o string/strlen.o string/strnlen.o stdio/stdin.o stdio/getchar.o stdio/fgetc.o arpa/inet/htons.o arpa/inet/htonl.o stdio/fread.o stdio/fwrite.o stdio/fopen.o stdio/fclose.o stdio/fseek.o ctype/isascii.o stdio/fprintf.o stdlib/atoi.o stdlib/strtol.o ctype/toupper.o ctype/tolower.o string/strcat.o string/strchr.o string/sscanf.o sys/stat/stat.o stdlib/getenv.o string/strrchr.o stdio/ftell.o stdio/tmpfile.o stdio/fgets.o stdio/feof.o stdio/fscanf.o stdio/ungetc.o string/strncmp.o stdio/fputc.o string/strncpy.o stdio/remove.o stdio/ferror.o stdio/fputs.o stdlib/rand.o stdlib/srand.o unistd/getpid.o stdlib/strtoul.o stdio/fflush.o stdlib/abort.o string/strcspn.o time/localtime.o time/time.o time/clock_gettime.o time/gmtime.o time/strftime.o string/strpbrk.o ctype/isdigit.o ctype/isalpha.o ctype/isxdigit.o ctype/ispunct.o stdio/setvbuf.o stdio/fileno.o stdio/putc.o stdio/sprintf.o stdlib/abs.o string/strspn.o stdlib/qsort.o string/memmove.o setjmp/longjmp.o setjmp/setjmp.o libgen/basename.o string/strdup.o string/strndup.o string/strlcpy.o stdlib/atexit.o stdio/open_memstream.o libgen/dirname.o unistd/unlink.o string/strstr.o string/strcasecmp.o string/strncasecmp.o stdlib/mkstemp.o string/strtok.o unistd/execvp.o unistd/_exit.o ctype/isalnum.o time/ctime_r.o stdlib/strtold.o sys/time/gettimeofday.o stdio/fgetpos.o stdio/fsetpos.o ctype/isprint.o stdlib/system.o stdio/tmpnam.o unistd/msleep.o stdlib/atof.o stdlib/strtod.o stdio/rename.o sys/stat/mkdir.o unistd/uptime.o unistd/ftruncate.o sys/socket/recvfrom.o sys/socket/sendto.o signal/kill.o signal/sigaction.o unistd/chdir.o unistd/getcwd.o stdio/getdelim.o stdio/getline.o unistd/isatty.o sys/socket/listen.o stdlib/realpath.o systemcall.o sys/random/randomfill.o fcntl/open.o unistd/write.o unistd/pwrite.o fcntl/open_process.o tb/sb.o tb/sv.o string/memchr.o stdlib/atol.o stdlib/atoll.o stdlib/strtoll.o sys/stat/fstat.o unistd/lseek.o ctype/isupper.o ctype/islower.o ctype/isblank.o ctype/isgraph.o ctype/iscntrl.o math/ldexp.o sys/socket/connect.o sys/socket/setsockopt.o arpa/inet/ntohl.o arpa/inet/ntohs.o netdb/getaddrinfo.o tb/sha1.o sys/socket/getpeername.o fcntl/fcntl.o queue.o sys/sendfile.o
all: libc.a
%.o: %.c
diff --git a/userland/libc/fcntl/fcntl.c b/userland/libc/fcntl/fcntl.c
new file mode 100644
index 0000000..0a4be9e
--- /dev/null
+++ b/userland/libc/fcntl/fcntl.c
@@ -0,0 +1,15 @@
+#include <fcntl.h>
+#include <stdarg.h>
+#include <syscall.h>
+
+int fcntl(int fd, int cmd, ...) {
+ int arg = 0;
+
+ if ((F_GETFL == cmd) || (F_SETFL == cmd)) {
+ va_list ap;
+ va_start(ap, cmd);
+ arg = va_arg(ap, int);
+ va_end(ap);
+ }
+ RC_ERRNO(syscall(SYS_FCNTL, fd, cmd, arg, 0, 0));
+}
diff --git a/userland/libc/include/queue.h b/userland/libc/include/queue.h
new file mode 100644
index 0000000..4883871
--- /dev/null
+++ b/userland/libc/include/queue.h
@@ -0,0 +1,21 @@
+#include <stdint.h>
+
+#define QUEUE_MOD_ADD 0
+#define QUEUE_MOD_CHANGE 1
+#define QUEUE_MOD_DELETE 2
+
+#define QUEUE_WAIT_READ (1 << 0)
+#define QUEUE_WAIT_WRITE (1 << 1)
+#define QUEUE_WAIT_CLOSE (1 << 2)
+
+struct queue_entry {
+ int fd;
+ uint8_t listen;
+ int data_type;
+ void *data;
+};
+
+int queue_create(void);
+int queue_mod_entries(int fd, int flag, struct queue_entry *entries,
+ int num_entries);
+int queue_wait(int fd, struct queue_entry *events, int num_events);
diff --git a/userland/libc/include/sys/sendfile.h b/userland/libc/include/sys/sendfile.h
new file mode 100644
index 0000000..5e83999
--- /dev/null
+++ b/userland/libc/include/sys/sendfile.h
@@ -0,0 +1,7 @@
+#include <stdint.h>
+#include <typedefs.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+u32 sendfile(int out_fd, int in_fd, off_t *offset, size_t count,
+ int *error_rc);
diff --git a/userland/libc/include/syscall.h b/userland/libc/include/syscall.h
index 87622c5..dda4b82 100644
--- a/userland/libc/include/syscall.h
+++ b/userland/libc/include/syscall.h
@@ -51,6 +51,7 @@
#define SYS_QUEUE_CREATE 43
#define SYS_QUEUE_MOD_ENTRIES 44
#define SYS_QUEUE_WAIT 45
+#define SYS_SENDFILE 46
int syscall(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx,
uint32_t esi, uint32_t edi);
diff --git a/userland/libc/include/tb/sha1.h b/userland/libc/include/tb/sha1.h
new file mode 100644
index 0000000..fe84196
--- /dev/null
+++ b/userland/libc/include/tb/sha1.h
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2022-2023 by Anton Kling <anton@kling.gg>
+//
+// SPDX-License-Identifier: 0BSD
+//
+#ifndef SHA1
+#define SHA1
+#include <stddef.h>
+#include <stdint.h>
+
+#define BLOCK_BYTES (64) /* 512/8 */
+#define SHA1_LEN (20)
+
+typedef struct SHA1_CTX {
+ uint32_t h[5];
+ uint8_t block[BLOCK_BYTES];
+ uint64_t active_len;
+ uint64_t len;
+} SHA1_CTX;
+
+void SHA1_Init(SHA1_CTX *ctx);
+void SHA1_Update(SHA1_CTX *ctx, const void *data, uint64_t len);
+void SHA1_Final(SHA1_CTX *ctx, unsigned char *message_digest);
+void SHA1_HMAC(unsigned char *message, uint64_t message_len, unsigned char *key,
+ uint64_t key_len, uint8_t output[SHA1_LEN]);
+#endif
diff --git a/userland/libc/queue.c b/userland/libc/queue.c
new file mode 100644
index 0000000..6d4d380
--- /dev/null
+++ b/userland/libc/queue.c
@@ -0,0 +1,15 @@
+#include <queue.h>
+#include <syscall.h>
+
+int queue_create(void) {
+ RC_ERRNO(syscall(SYS_QUEUE_CREATE, 0, 0, 0, 0, 0));
+}
+
+int queue_mod_entries(int fd, int flag, struct queue_entry *entries,
+ int num_entries) {
+ RC_ERRNO(syscall(SYS_QUEUE_MOD_ENTRIES, fd, flag, entries, num_entries, 0));
+}
+
+int queue_wait(int fd, struct queue_entry *events, int num_events) {
+ RC_ERRNO(syscall(SYS_QUEUE_WAIT, fd, events, num_events, 0, 0));
+}
diff --git a/userland/libc/sys/sendfile.c b/userland/libc/sys/sendfile.c
new file mode 100644
index 0000000..818883b
--- /dev/null
+++ b/userland/libc/sys/sendfile.c
@@ -0,0 +1,12 @@
+#include <sys/sendfile.h>
+#include <syscall.h>
+#include <errno.h>
+
+u32 sendfile(int out_fd, int in_fd, off_t *offset, size_t count,
+ int *error_rc) {
+ u32 r = syscall(SYS_SENDFILE, out_fd, in_fd, offset, count, error_rc);
+ if (error_rc) {
+ errno = -1 * *error_rc;
+ }
+ return r;
+}
diff --git a/userland/libc/tb/sha1.c b/userland/libc/tb/sha1.c
new file mode 100644
index 0000000..90e4e2a
--- /dev/null
+++ b/userland/libc/tb/sha1.c
@@ -0,0 +1,249 @@
+//
+// Copyright (C) 2022-2023 by Anton Kling <anton@kling.gg>
+//
+// SPDX-License-Identifier: 0BSD
+//
+#include <tb/sha1.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#define SHA1_CONSTANT_H0 0x67452301
+#define SHA1_CONSTANT_H1 0xefcdab89
+#define SHA1_CONSTANT_H2 0x98badcfe
+#define SHA1_CONSTANT_H3 0x10325476
+#define SHA1_CONSTANT_H4 0xc3d2e1f0
+
+#define SHA1_CONSTANT_K1 0x5a827999
+#define SHA1_CONSTANT_K2 0x6ed9eba1
+#define SHA1_CONSTANT_K3 0x8f1bbcdc
+#define SHA1_CONSTANT_K4 0xca62c1d6
+
+void SHA1_Init(SHA1_CTX *ctx) {
+ // 6.1.1 SHA-1 Preprocessing
+
+ // 1. Set the initial hash value, H(0), as specified in Sec. 5.3.1
+ ctx->h[0] = SHA1_CONSTANT_H0;
+ ctx->h[1] = SHA1_CONSTANT_H1;
+ ctx->h[2] = SHA1_CONSTANT_H2;
+ ctx->h[3] = SHA1_CONSTANT_H3;
+ ctx->h[4] = SHA1_CONSTANT_H4;
+
+ ctx->len = ctx->active_len = 0;
+
+ memset(ctx->block, 0, BLOCK_BYTES);
+}
+
+uint32_t reverse_32(uint32_t _value) {
+ return (((_value & 0x000000FF) << 24) | ((_value & 0x0000FF00) << 8) |
+ ((_value & 0x00FF0000) >> 8) | ((_value & 0xFF000000) >> 24));
+}
+
+void pad_sha1_message(uint8_t *M, uint64_t l, uint64_t active_l,
+ uint8_t *block) {
+ memset(block, 0, 1024 / 8);
+ memcpy(block, M, active_l / 8);
+
+#define ARRAY_NUM(_loc) (((_loc) / 8) + !(0 == ((_loc) % 8)))
+
+ // 5. PREPROCESSING
+
+ // 5.1.1 SHA-1, SHA-224 and SHA-256
+
+ // Append the bit “1” to the end of the message
+ block[ARRAY_NUM(active_l)] = 1 << 7;
+
+ // followed by k zero bits, where k is the smallest, non-negative
+ // solution to the equation 'l + 1 + k \cong 448 mod 512'
+ int k;
+ uint64_t rest = (active_l + 1) % 512;
+ if (rest < 448)
+ k = 448 - rest;
+ else
+ k = 512 - rest + 448;
+
+ memset(block + ARRAY_NUM(active_l) + 2, 0x0, k / 8);
+
+ // Then append the 64-bit block that is equal to the number 'l' expressed
+ // using a binary representatio
+ uint32_t *final_32bit_block;
+ final_32bit_block = (uint32_t *)(block + active_l / 8 + k / 8 + 1);
+
+ *(final_32bit_block) = reverse_32(l >> 32);
+ *(final_32bit_block + 1) = reverse_32(l & 0xFFFFFFFF);
+}
+
+uint32_t sha1_f(uint8_t t, uint32_t x, uint32_t y, uint32_t z) {
+ if (t <= 19) {
+ // Ch(x,y,z)
+ return (x & y) ^ ((~x) & z);
+ }
+ if (t <= 39 || t >= 60) {
+ // Parity(x,y,z)
+ return x ^ y ^ z;
+ }
+ if (t <= 59) {
+ // Maj(x,y,z)
+ return (x & y) ^ (x & z) ^ (y & z);
+ }
+ return 0;
+}
+
+uint32_t sha1_get_k(uint8_t t) {
+ if (t <= 19)
+ return SHA1_CONSTANT_K1;
+ if (t <= 39)
+ return SHA1_CONSTANT_K2;
+ if (t <= 59)
+ return SHA1_CONSTANT_K3;
+ if (t <= 79)
+ return SHA1_CONSTANT_K4;
+ return 0;
+}
+
+uint32_t ROTL(uint32_t value, uint8_t shift) {
+ uint32_t rotated = value << shift;
+ uint32_t did_overflow = value >> (32 - shift);
+ return (rotated | did_overflow);
+}
+
+void add_block(SHA1_CTX *ctx, uint8_t *_block) {
+ uint32_t *block = (uint32_t *)_block;
+ for (size_t i = 0; i < 16; i++)
+ block[i] = reverse_32(block[i]);
+
+ uint32_t W[80];
+ uint32_t a, b, c, d, e;
+ uint32_t *M = block;
+
+ // 1. Prepare the message schedule, {Wt}:
+ for (size_t t = 0; t < 16; t++)
+ W[t] = M[t];
+
+ for (size_t t = 16; t < 80; t++) {
+ // ROTL(1)(W(t-3) ^ W(t-8) ^ W(t-16))
+ W[t] = ROTL((W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]), 1);
+ }
+
+ // 2. Initialize the five working variables, a, b, c, d, and e, with the
+ // (i-1)^(st) hash value:
+ a = ctx->h[0];
+ b = ctx->h[1];
+ c = ctx->h[2];
+ d = ctx->h[3];
+ e = ctx->h[4];
+
+ // 3. For t=0 to 79:
+ for (size_t t = 0; t < 80; t++) {
+ uint32_t T = ROTL(a, 5) + sha1_f(t, b, c, d) + e + sha1_get_k(t) + W[t];
+ e = d;
+ d = c;
+ c = ROTL(b, 30);
+ b = a;
+ a = T;
+ }
+
+ // 4. Compute the ith intermediate hash value H(i):
+ ctx->h[0] = a + ctx->h[0];
+ ctx->h[1] = b + ctx->h[1];
+ ctx->h[2] = c + ctx->h[2];
+ ctx->h[3] = d + ctx->h[3];
+ ctx->h[4] = e + ctx->h[4];
+}
+
+void SHA1_Final(SHA1_CTX *ctx, unsigned char *message_digest) {
+ uint8_t block[BLOCK_BYTES * 2] = {0};
+ pad_sha1_message(ctx->block, ctx->len * 8, ctx->active_len * 8, block);
+
+ add_block(ctx, block);
+
+ if (((ctx->active_len * 8 + 1) % 512) >= 448)
+ add_block(ctx, block + BLOCK_BYTES);
+
+ for (size_t i = 0; i < 5; i++)
+ ctx->h[i] = reverse_32(ctx->h[i]);
+
+ for (size_t i = 0; i < 5; i++)
+ memcpy(message_digest + sizeof(uint32_t) * i, &ctx->h[i], sizeof(uint32_t));
+}
+
+void SHA1_Update(SHA1_CTX *ctx, const void *data, uint64_t len) {
+ for (; len > 0;) {
+ size_t write_len = ((ctx->active_len + len) > BLOCK_BYTES)
+ ? (BLOCK_BYTES - ctx->active_len)
+ : len;
+
+ memcpy(ctx->block + ctx->active_len, data, write_len);
+ ctx->len += write_len;
+ ctx->active_len += write_len;
+ if (BLOCK_BYTES != ctx->active_len)
+ return;
+
+ add_block(ctx, ctx->block);
+ memset(ctx->block, 0, BLOCK_BYTES);
+ ctx->active_len = 0;
+
+ len -= write_len;
+ data = (const void *)((uintptr_t)data + write_len);
+ }
+}
+
+// https://www.rfc-editor.org/rfc/rfc2104
+#define BLOCK_SIZE 64
+void SHA1_HMAC(unsigned char *message, uint64_t message_len, unsigned char *key,
+ uint64_t key_len, uint8_t output[SHA1_LEN]) {
+ // This function can be presented as follows:
+ // H(K XOR opad, H(K XOR ipad, message))
+ //
+ // H : SHA1 hashing algorithm.
+ // K : Key that got shrunk/padded to block size.
+ // ipad : the byte 0x36 repeated B times
+ // opad : the byte 0x5C repeated B times.
+ // message : The input.
+
+ char o_key_pad[BLOCK_SIZE];
+ char i_key_pad[BLOCK_SIZE];
+
+ // (1) append zeros to the end of K to create a B byte string
+ // (e.g., if K is of length 20 bytes and B=64, then K will be
+ // appended with 44 zero bytes 0x00)
+ unsigned char hashed_key[SHA1_LEN];
+ if (key_len > BLOCK_SIZE) {
+ SHA1_CTX ctx;
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, key, key_len);
+ SHA1_Final(&ctx, hashed_key);
+ key = hashed_key;
+ key_len = SHA1_LEN;
+ }
+
+ // (2) XOR (bitwise exclusive-OR) the B byte string computed in step
+ // (1) with ipad
+ // (5) XOR (bitwise exclusive-OR) the B byte string computed in
+ // step (1) with opad
+ memset(i_key_pad, 0x36, BLOCK_SIZE);
+ memset(o_key_pad, 0x5C, BLOCK_SIZE);
+ for (size_t i = 0; i < key_len; i++) {
+ i_key_pad[i] ^= key[i];
+ o_key_pad[i] ^= key[i];
+ }
+
+ // (3) append the stream of data 'message' to the B byte string resulting
+ // from step (2)
+ // (4) apply H to the stream generated in step (3)
+ SHA1_CTX first_ctx;
+ SHA1_Init(&first_ctx);
+ SHA1_Update(&first_ctx, i_key_pad, BLOCK_SIZE);
+ SHA1_Update(&first_ctx, message, message_len);
+ SHA1_Final(&first_ctx, output);
+
+ // (6) append the H result from step (4) to the B byte string
+ // resulting from step (5)
+ // (7) apply H to the stream generated in step (6) and output
+ // the result
+ SHA1_CTX second_ctx;
+ SHA1_Init(&second_ctx);
+ SHA1_Update(&second_ctx, o_key_pad, BLOCK_SIZE);
+ SHA1_Update(&second_ctx, output, SHA1_LEN);
+ SHA1_Final(&second_ctx, output);
+}