From d0babb29b67d021896939ec0d000e3936ab9cd42 Mon Sep 17 00:00:00 2001 From: mar77i Date: Tue, 9 Jun 2026 07:37:34 +0200 Subject: [PATCH] initial commit --- .gitignore | 6 + build.sh | 25 ++++ encrypt_secrets_to_header.py | 72 +++++++++++ main.c | 133 ++++++++++++++++++++ otp_nano.ino | 0 repl.py | 126 +++++++++++++++++++ sha1.c | 162 ++++++++++++++++++++++++ sha1.h | 42 +++++++ totp.c | 231 +++++++++++++++++++++++++++++++++++ totp.h | 14 +++ uart.c | 50 ++++++++ uart.h | 37 ++++++ 12 files changed, 898 insertions(+) create mode 100644 .gitignore create mode 100755 build.sh create mode 100755 encrypt_secrets_to_header.py create mode 100644 main.c create mode 100644 otp_nano.ino create mode 100755 repl.py create mode 100644 sha1.c create mode 100644 sha1.h create mode 100644 totp.c create mode 100644 totp.h create mode 100644 uart.c create mode 100644 uart.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe4f21d --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.bin +*.eep +*.elf +*.hex +.idea +secrets.h diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..560399c --- /dev/null +++ b/build.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +tmpdir="$(mktemp -d -p/dev/shm)" +cleanup() { + rm -rf "${tmpdir}" +} +trap cleanup EXIT + +extra_args=( + --fqbn arduino:avr:nano + --build-path "${tmpdir}" + --build-property "compiler.c.extra_flags=-Wall -Wextra -std=c99" + --build-property "compiler.cpp.extra_flags=-Wall -Wextra" + --output-dir . +) +while (( $# )); do + case "${1}" in + -u|--upload) + extra_args+=(-u -p /dev/ttyUSB0) + ;; + esac + shift +done +arduino-cli compile "${extra_args[@]}" . +#rm -r "${tmpdir}" diff --git a/encrypt_secrets_to_header.py b/encrypt_secrets_to_header.py new file mode 100755 index 0000000..14e8575 --- /dev/null +++ b/encrypt_secrets_to_header.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +import hmac +import sys +from getpass import getpass +from hashlib import sha1 +from io import BytesIO +from secrets import randbelow, randbits + + +def advance_round_key(orig_iv, iv): + for i in range(len(iv)): + j = orig_iv[i] & 31 + iv[j] = (iv[j] + 1) & 255 + if iv[j] != 0: + break + + +def scramble(iv_bytes, password, data): + assert not any(len(line) > 79 for line in data.split(b"\n")) + iv_list = list(iv_bytes) + round_key = iv_list[:] + bio = BytesIO() + derived = b"" + for i in range(len(data)): + if i % 10 == 0: + if i > 0: + advance_round_key(iv_list, round_key) + derived = hmac.new(password, bytes(round_key), sha1).digest() + print("derived:", derived.hex(), file=sys.stderr) + bio.write(bytes((data[i] ^ derived[i % 10],))) + return bio.getvalue() + + +def print_as_const(varname, data): + print(f"static const uint8_t {varname}[] PROGMEM = {{") + print(" ", end="") + for i, b in enumerate(data): + print(f"0x{b:02x}", end=",") + if i == len(data) - 1: + print() + elif i % 12 == 11: + print("\n ", end="") + else: + print(end=" ") + print("};") + + +def main(): + indices = list(range(20)) + key = bytes( + [ + indices.pop(randbelow(len(indices))) | (randbits(3) << 5) + for _ in range(len(indices)) + ] + ) + password = getpass().encode() + with open(sys.argv[1], "rb") as fh: + scrambled_data = scramble(key, password, fh.read()) + print("\n// secrets.h\n") + print("#ifndef SECRETS_H") + print("#define SECRETS_H\n") + print("#include ") + print("#include \n") + print_as_const("key", key) + print_as_const("pw_mac", hmac.new(key, password, sha1).digest()) + print_as_const("secret", scrambled_data) + print("\n#endif // SECRETS_H") + + +if __name__ == "__main__": + main() diff --git a/main.c b/main.c new file mode 100644 index 0000000..c977868 --- /dev/null +++ b/main.c @@ -0,0 +1,133 @@ + +// main.c + +#include +#include + +#include "totp.h" +#include "sha1.h" +#include "uart.h" + +static uint8_t password[64]; + +static inline void wait_for_input(void) { + register uint8_t corr = 0; // prevent drift (8 iterations of TCNT1 => 62500) + DDRB |= (1 << PB5); // Set Digital Pin 13 (Port B, Pin 5) to OUTPUT + TCCR1A = 0; // Normal operation mode + TCCR1B = (1 << CS12); // Set Prescaler to 256 (62,500 ticks per second) + while (!UART_bytes_available()) { + if (TCNT1 >= 7812 + corr) { + PORTB ^= (1 << PB5); // Toggle LED + TCNT1 = 0; // Reset hardware timer register back to 0 + corr ^= 1; + } + } + PORTB &= ~(1 << PB5); // Turn off LED + TCCR1B = 0; // Turn off Timer 1 +} + +/* +static inline void process_sha1(uint8_t *payload) { + struct sha1_ctx_type ctx; + uint8_t digest[20], i; + sha1_init(&ctx); + sha1_update(&ctx, payload, strlen(payload)); + sha1_final(&ctx, digest); + for (i = 0; i < sizeof digest * 2; i++) + UART_write_byte( + "0123456789abcdef"[digest[i >> 1] >> (!(i & 1) << 2) & 15] + ); +} +*/ + +static inline uint8_t process_payload(uint8_t *payload) { + size_t password_len; + const char *reply = "NOK\n"; + uint8_t ret = 0; + if (strcmp(payload, "bye") == 0) + return 1; + else if (strncmp(payload, "password ", 9) == 0) { + payload += 9; + password_len = strlen(payload); + if (password_len + 1 > sizeof password) + UART_write_string("ERROR:PASSWORDTOOLONG\n"); + else if (check_password(payload, password_len) == 0) { + memcpy(password, payload, strlen(payload) + 1); + reply = "OK\n"; + } + } else if (strncmp(payload, "gettoken ", 9) == 0) { + if (decrypt_secrets(password, payload + 9) == 0) + reply = "OK\n"; + } else if (strncmp(payload, "dumpsecret", 9) == 0) { + if (dump_secrets(password) == 0) + reply = "OK\n"; + } else + UART_write_string("ERROR:UNKNOWNCOMMAND\n"); + UART_write_string(reply); + return ret; +} + +void parse_serial_frame(void) { + enum { STATE_IDLE, STATE_PAYLOAD, STATE_CHECKSUM } state = STATE_IDLE; + uint8_t payload[64], payload_idx, calculated_xor, received_xor, c, esc = 0; + payload_idx = calculated_xor = received_xor = 0; + while (c = UART_read_byte_blocking()) { + if (state != STATE_CHECKSUM && esc == 0 && c == '\\') { + esc = 1; + continue; + } + switch (state) { + case STATE_IDLE: + if (esc == 0 && c == '$') { + state = STATE_PAYLOAD; + payload_idx = calculated_xor = 0; + } + break; + + case STATE_PAYLOAD: + if (esc == 0 && c == '*') { + payload[payload_idx] = '\0'; + state = STATE_CHECKSUM; + received_xor = 0; + } else if (payload_idx < (sizeof payload - 1)) { + payload[payload_idx++] = c; + calculated_xor = calculated_xor * 33 ^ c; + } else { + state = STATE_IDLE; + UART_write_string("ERROR:PAYLOAD_OVERFLOW\n"); + } + break; + + case STATE_CHECKSUM: + if (c >= '0' && c <= '9') { + c = received_xor * 10 + (c - '0'); + if (c / 10 != received_xor) + goto checksum_error; + received_xor = c; + break; + } else if (c == '\n' && calculated_xor == received_xor) { + if (process_payload(payload)) + return; + } else { +checksum_error: + UART_write_string("ERROR:CHECKSUM_ERROR\n"); + } + state = STATE_IDLE; // Reset parser state machine + break; + } + if (esc == 1) + esc = 0; + } +} + +int main(void) { + UART_init(); + sei(); + for (;;) { + wait_for_input(); + parse_serial_frame(); + memset(password, 0, sizeof password); + UART_flush(); + UART_write_string("EYB\n"); + } +} diff --git a/otp_nano.ino b/otp_nano.ino new file mode 100644 index 0000000..e69de29 diff --git a/repl.py b/repl.py new file mode 100755 index 0000000..6b309f8 --- /dev/null +++ b/repl.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 + +import os +import sys +from getpass import getpass +from termios import ( + B115200, + CS8, + CLOCAL, + CREAD, + CSIZE, + CSTOPB, + ECHO, + ECHOE, + ICANON, + ICRNL, + IXANY, + IXOFF, + IXON, + ISIG, + ONLCR, + OPOST, + PARENB, + TCSANOW, + tcgetattr, + tcsetattr, +) +from time import sleep, time +from traceback import print_exc + + +def configure_port(fd): + """Configures the raw file descriptor to 115200 baud, 8N1, non-blocking.""" + # Get the current OS configuration attributes for the port file descriptor + attrs = tcgetattr(fd) + # Disable software flow control and mapping formatting + attrs[0] &= ~(IXON | IXOFF | IXANY | ICRNL) + # Pure raw output formatting + attrs[1] &= ~(OPOST | ONLCR) + # 8 data bits, enable receiver, ignore control lines + attrs[2] &= ~(PARENB | CSTOPB | CSIZE) # No Parity, 1 Stop Bit + attrs[2] |= (CS8 | CLOCAL | CREAD) # 8 Data Bits + # Raw input mode (Disable echo, erase, kill, interrupts) + attrs[3] &= ~(ICANON | ECHO | ECHOE | ISIG) + attrs[4] = B115200 # ospeed + attrs[5] = B115200 # ispeed + tcsetattr(fd, TCSANOW, attrs) + + +def send(fd, payload): + checksum = 0 + for char in payload: + checksum = checksum * 33 ^ ord(char) + for char, *rest in (("\\",), ("*",), ("$",), ("\n", "n")): + payload = payload.replace(char, (*rest, f"\\{char}")[0]) + os.write(fd, f"${payload}*{checksum & 0xff}\n".encode('ascii')) + + +def await_some_reply(fd, block=False): + received_nothing = True + while received_nothing: + sleep(0.1) + try: + response_bytes = os.read(fd, 256) + except BlockingIOError: + if block: + continue + break + if not response_bytes: + if block: + continue + break + received_nothing = False + try: + response = response_bytes.decode('ascii').strip() + except UnicodeDecodeError: + print("decoding failed.") + response = str(response_bytes) + print(response) + + +sent_bye = False + + +def one_read_eval_print(fd): + global sent_bye + try: + cmd = input("> ") + except EOFError: + print() + return False + if cmd == "quit": + return False + elif cmd: + if cmd == "password": + cmd = f"password {getpass()}" + elif cmd.startswith("gettoken "): + cmd = f"gettoken {int(time()) + 1} {cmd[9:]}" + send(fd, cmd) + sent_bye = cmd == "bye" + await_some_reply(fd, True) + else: + await_some_reply(fd, False) + return True + + +def main(): + configure_port( + # Open fd with r/w, preventing controlling tty and non-blocking + fd := os.open(sys.argv[1], os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK) + ) + try: + while one_read_eval_print(fd): + pass + finally: + if not sent_bye: + try: + send(fd, "bye") + except OSError: + print_exc() + await_some_reply(fd) + os.close(fd) + + +if __name__ == "__main__": + main() diff --git a/sha1.c b/sha1.c new file mode 100644 index 0000000..2125c3a --- /dev/null +++ b/sha1.c @@ -0,0 +1,162 @@ + +// sha1.c + +#include +#include "sha1.h" + +#define ARRAY_LENGTH(arr) (sizeof (arr) / sizeof (arr)[0]) +#define ROTLEFT(a, b) ((a << b) | (a >> (32 - b))) + +void sha1_init(struct sha1_ctx_type *ctx) { + *ctx = (struct sha1_ctx_type){ + .state = { + .i = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }, + }, + .bytelen = 0, + }; +} + +static const uint32_t k[] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }; + +static void sha1_transform(struct sha1_ctx_type *ctx) { + struct sha1_ctx_state st; + uint32_t f, t, m[80]; + uint8_t i; + for (i = 0; i < sizeof ctx->data.b; i += 4) + m[i >> 2] = ( + ((uint32_t)ctx->data.b[i] << 24) + | ((uint32_t)ctx->data.b[i + 1] << 16) + | ((uint32_t)ctx->data.b[i + 2] << 8) + | (uint32_t)ctx->data.b[i + 3] + ); + for (i = 16; i < 80; i++) { + m[i] = (m[i - 3] ^ m[i - 8] ^ m[i - 14] ^ m[i - 16]); + m[i] = ROTLEFT(m[i], 1); + } + st = ctx->state; + for (i = 0; i < 80; i++) { + if (i < 20) + f = (st.i[1] & st.i[2]) | (~st.i[1] & st.i[3]); + else if (i < 40) + f = st.i[1] ^ st.i[2] ^ st.i[3]; + else if (i < 60) + f = (st.i[1] & st.i[2]) | (st.i[1] & st.i[3]) | (st.i[2] & st.i[3]); + else + f = st.i[1] ^ st.i[2] ^ st.i[3]; + t = ROTLEFT(st.i[0], 5) + f + st.i[4] + k[i / 20] + m[i]; + st.i[4] = st.i[3]; + st.i[3] = st.i[2]; + st.i[2] = ROTLEFT(st.i[1], 30); + st.i[1] = st.i[0]; + st.i[0] = t; + } + + for (i = 0; i < ARRAY_LENGTH(st.i); i++) + ctx->state.i[i] += st.i[i]; +} + +void sha1_update(struct sha1_ctx_type *ctx, const uint8_t *data, size_t len) { + size_t i; + uint8_t di; + for (i = 0, di = ctx->bytelen & 63; i < len; i++) { + ctx->data.b[di++] = data[i]; + if (di == 64) { + sha1_transform(ctx); + di = 0; + } + } + ctx->bytelen += len; +} + +void sha1_final(struct sha1_ctx_type *ctx, uint8_t *hash) { + uint8_t di = ctx->bytelen & 63, i; + ctx->data.b[di++] = 128; + if (di > 56) { + memset(ctx->data.b + di, 0, 64 - di); + sha1_transform(ctx); + di = 0; + } else + memset(ctx->data.b + di, 0, 56 - di); + ctx->data.b[56] = ctx->bytelen >> 53; + ctx->data.b[57] = ctx->bytelen >> 45; + ctx->data.b[58] = ctx->bytelen >> 37; + ctx->data.b[59] = ctx->bytelen >> 29; + ctx->data.b[60] = ctx->bytelen >> 21; + ctx->data.b[61] = ctx->bytelen >> 13; + ctx->data.b[62] = ctx->bytelen >> 5; + ctx->data.b[63] = ctx->bytelen << 3; + sha1_transform(ctx); + for (i = 0; i < 20; i++) + hash[i] = ctx->state.i[i >> 2] >> (24 - ((i & 3) << 3)); +} + +void sha1_hmac( + const uint8_t *key, + size_t key_len, + const uint8_t *data, + size_t data_len, + uint8_t *mac +) { + struct sha1_ctx_type ctx; + uint8_t k_ipad[64], k_opad[64], tk[20], i; + if (key_len > 64) { + sha1_init(&ctx); + sha1_update(&ctx, key, key_len); + sha1_final(&ctx, tk); + key = tk; + key_len = sizeof tk; + } + for (i = 0; i < key_len; i++) { + k_ipad[i] = key[i] ^ 0x36; + k_opad[i] = key[i] ^ 0x5c; + } + memset(k_ipad + key_len, 0x36, sizeof k_ipad - key_len); + memset(k_opad + key_len, 0x5c, sizeof k_opad - key_len); + sha1_init(&ctx); + sha1_update(&ctx, k_ipad, 64); + sha1_update(&ctx, data, data_len); + sha1_final(&ctx, mac); + sha1_init(&ctx); + sha1_update(&ctx, k_opad, 64); + sha1_update(&ctx, mac, 20); + sha1_final(&ctx, mac); +} + +void sha1_hmac_ipad_opad_init( + const uint8_t *key, + size_t key_len, + struct sha1_ctx_type ipad_opad_ctx[2] +) { + struct sha1_ctx_type ctx; + uint8_t k_ipad[64], k_opad[64], tk[20], i; + if (key_len > 64) { + sha1_init(&ctx); + sha1_update(&ctx, key, key_len); + sha1_final(&ctx, tk); + key = tk; + key_len = sizeof tk; + } + for (i = 0; i < key_len; i++) { + k_ipad[i] = key[i] ^ 0x36; + k_opad[i] = key[i] ^ 0x5c; + } + memset(k_ipad + key_len, 0x36, sizeof k_ipad - key_len); + memset(k_opad + key_len, 0x5c, sizeof k_opad - key_len); + sha1_init(ipad_opad_ctx); + sha1_update(ipad_opad_ctx, k_ipad, 64); + sha1_init(&ipad_opad_ctx[1]); + sha1_update(&ipad_opad_ctx[1], k_opad, 64); +} + +void sha1_hmac_ipad_opad_digest( + struct sha1_ctx_type ipad_opad_ctx[2], + const uint8_t *data, + size_t data_len, + uint8_t *mac +) { + struct sha1_ctx_type ipad_ctx = ipad_opad_ctx[0], opad_ctx = ipad_opad_ctx[1]; + sha1_update(&ipad_ctx, data, data_len); + sha1_final(&ipad_ctx, mac); + sha1_update(&opad_ctx, mac, 20); + sha1_final(&opad_ctx, mac); +} diff --git a/sha1.h b/sha1.h new file mode 100644 index 0000000..abc8219 --- /dev/null +++ b/sha1.h @@ -0,0 +1,42 @@ + +// sha1.h + +#ifndef SHA1_H +#define SHA1_H + +#include +#include + +struct sha1_ctx_type { + struct sha1_ctx_data { + uint8_t b[64]; + } data; + struct sha1_ctx_state { + uint32_t i[5]; + } state; + uint64_t bytelen; +}; + +void sha1_init(struct sha1_ctx_type *ctx); +void sha1_update(struct sha1_ctx_type *ctx, const uint8_t *data, size_t len); +void sha1_final(struct sha1_ctx_type *ctx, uint8_t *hash); +void sha1_hmac( + const uint8_t *key, + size_t key_len, + const uint8_t *data, + size_t data_len, + uint8_t *mac +); +void sha1_hmac_ipad_opad_init( + const uint8_t *key, + size_t key_len, + struct sha1_ctx_type ipad_opad_ctx[2] +); +void sha1_hmac_ipad_opad_digest( + struct sha1_ctx_type ipad_opad_ctx[2], + const uint8_t *data, + size_t data_len, + uint8_t *mac +); + +#endif // SHA1_H diff --git a/totp.c b/totp.c new file mode 100644 index 0000000..eb3ecd1 --- /dev/null +++ b/totp.c @@ -0,0 +1,231 @@ + +// totp.c + +#include +#include + +#include "secrets.h" +#include "sha1.h" +#include "uart.h" + +struct decrypt_buffers { + struct sha1_ctx_type ipad_opad_ctx[2]; + uint8_t orig_key[20], round_key[20], mac[20], chunk[32]; +}; + +int check_password(uint8_t *password, size_t password_len) { + const uint8_t mac[20], key_copy[sizeof key], pw_mac_copy[sizeof pw_mac]; + memcpy_P(key_copy, key, sizeof key); + memcpy_P(pw_mac_copy, pw_mac, sizeof pw_mac); + sha1_hmac(key_copy, sizeof key, password, password_len, mac); + return memcmp(mac, pw_mac_copy, sizeof mac); +} + +static inline uint8_t b32_decode(uint8_t *buf) { + static const uint8_t alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + uint8_t bits, *outptr, *inptr, *found_in_alphabet; + int8_t shift = 3; + /* Cycle through {-4..3} in intervals of 3 (== -5): + * { 3, -2, 1, -4, -1, 2, -3, 0 } + * This corresponds to how the shift offsets for decoded bits unfold. + */ + for ( + outptr = inptr = buf; + *inptr != '\n' && *inptr != '\0' && *inptr != '='; + inptr++ + ) { + if (*inptr == ' ' || *inptr == '\t') + continue; + found_in_alphabet = strchr(alphabet, toupper(*inptr)); + if (found_in_alphabet == NULL) { + UART_write_string("ERROR:INVALIDB32\n"); + return 0; + } + bits = found_in_alphabet - alphabet; + switch (shift) { + case 0: + *outptr++ |= bits; + break; + case 1: + case 2: + *outptr |= bits << shift; + break; + case 3: + *outptr = bits << 3; + break; + default: + *outptr |= bits >> -shift; + *++outptr = bits << (shift + 8); + break; + } + shift += 3 - (shift > 0) * 8; + } + return outptr - buf; +} + +void process_line( + const uint8_t *password, + uint8_t password_len, + const uint8_t *line, + const uint8_t *search, + int64_t time +) { + uint32_t result; + uint8_t *colon = strrchr(line, ':'), *find, time_msg[sizeof time], md[20], i, len; + if ( + colon == NULL + || search == NULL + || (*search && (find = strstr(line, search)) == NULL) + ) + return; + if (!*search) { + UART_write_string_partial(line, colon - line); + UART_write_byte('\n'); + return; + } + time /= 30; + for (i = 0; i < sizeof time; i++) + time_msg[i] = time >> ((sizeof time - 1 - i) * 8); + colon++; + if ((len = b32_decode(colon)) == 0) + return 1; + sha1_hmac(colon, len, time_msg, sizeof time_msg, md); + i = md[sizeof md - 1] & 0xf; + result = ( + ((uint32_t)(md[i] & 0x7f) << 24) + | ((uint32_t)md[i + 1] << 16) + | ((uint32_t)md[i + 2] << 8) + | md[i + 3] + ) % 1000000; + UART_write_string_partial(line, colon - line); +/* + UART_write_byte(' '); + for (i = 0; i < sizeof md * 2; i++) + UART_write_byte( + "0123456789abcdef"[md[i >> 1] >> (!(i & 1) << 2) & 15] + ); + UART_write_byte(' '); + for (i = 0; i < len * 2; i++) + UART_write_byte( + "0123456789abcdef"[colon[i >> 1] >> (!(i & 1) << 2) & 15] + ); + UART_write_byte(' '); + for (i = 0; i < sizeof time_msg * 2; i++) + UART_write_byte( + "0123456789abcdef"[time_msg[i >> 1] >> (!(i & 1) << 2) & 15] + ); +*/ + UART_write_byte(' '); + UART_write_byte('0' + result / 100000 % 10); + UART_write_byte('0' + result / 10000 % 10); + UART_write_byte('0' + result / 1000 % 10); + UART_write_byte('0' + result / 100 % 10); + UART_write_byte('0' + result / 10 % 10); + UART_write_byte('0' + result % 10); + UART_write_byte('\n'); + return 1; +} + +void advance_round_key(const char *orig_key, char *round_key) { + uint8_t i, j; + for (i = 0; i < 20; i++) { + j = orig_key[i] & 31; + round_key[j]++; + if (round_key[j] != 0) + break; + } +} + +static inline uint8_t *parse_time(const uint8_t *args, int64_t *time) { + int64_t a = 0, b; + while (*args >= '0' && *args <= '9') { + b = 10 * a + *args - '0'; + if (b / 10 != a) + return NULL; + a = b; + args++; + } + *time = a; + while (*args == ' ') + args++; + return args; +} + +static inline void init_decrypt_buffers( + struct decrypt_buffers *bufs, uint8_t *password, uint8_t password_len +) { + memcpy_P(bufs->orig_key, key, 20); + memcpy(bufs->round_key, bufs->orig_key, 20); + sha1_hmac_ipad_opad_init(password, password_len, bufs->ipad_opad_ctx); +} + +static inline uint8_t get_byte_from_decrypt_buffers( + struct decrypt_buffers *bufs, size_t i +) { + if (i % sizeof bufs->chunk == 0) { + if (i + sizeof bufs->chunk <= sizeof secret) + memcpy_P(bufs->chunk, secret + i, sizeof bufs->chunk); + else { + memcpy_P(bufs->chunk, secret + i, sizeof secret - i); + memset( + bufs->chunk + sizeof secret - i, + 0, + sizeof bufs->chunk - (sizeof secret - i) + ); + } + } + if (i % (sizeof bufs->mac / 2) == 0) { + if (i > 0) + advance_round_key(bufs->orig_key, bufs->round_key); + sha1_hmac_ipad_opad_digest( + bufs->ipad_opad_ctx, bufs->round_key, sizeof bufs->round_key, bufs->mac + ); + } + return bufs->chunk[i % sizeof bufs->chunk] ^ bufs->mac[i % (sizeof bufs->mac / 2)]; +} + +uint8_t decrypt_secrets(const uint8_t *password, const uint8_t *args) { + struct decrypt_buffers bufs; + int64_t time; + size_t i; + uint8_t round_bytes = 0, chunk_offset = 0, bytes_remaining, chunk_bytes = 0; + uint8_t line[80], c, line_index = 0, replied = 0; + uint8_t password_len = strlen(password); + if ((password_len = strlen(password)) == 0) { + UART_write_string("ERROR:NOPASSWORD\n"); + return 1; + } + if ((args = parse_time(args, &time)) == NULL) { + UART_write_string("ERROR:PARSETIMEFAILED\n"); + return 1; + } + init_decrypt_buffers(&bufs, password, password_len); + for (i = 0; i < sizeof secret; i++) { + c = get_byte_from_decrypt_buffers(&bufs, i); + if (c == '\n' || line_index == sizeof line - 1) { + line[line_index] = '\0'; + process_line(password, password_len, line, args, time); + line_index = 0; + } else + line[line_index++] = c; + } + if (line_index > 0) { + line[line_index] = '\0'; + process_line(password, password_len, line, args, time); + } + return 0; +} + +uint8_t dump_secrets(const uint8_t *password) { + struct decrypt_buffers bufs; + size_t i; + uint8_t password_len = strlen(password); + if (password_len == 0) { + UART_write_string("ERROR:NOPASSWORD\n"); + return 1; + } + init_decrypt_buffers(&bufs, password, password_len); + for (i = 0; i < sizeof secret; i++) + UART_write_byte(get_byte_from_decrypt_buffers(&bufs, i)); + return 0; +} diff --git a/totp.h b/totp.h new file mode 100644 index 0000000..45e9162 --- /dev/null +++ b/totp.h @@ -0,0 +1,14 @@ + +// decrypt.h + +#ifndef DECRYPT_H +#define DECRYPT_H + +#include +#include + +int check_password(uint8_t *password, size_t password_len); +uint8_t decrypt_secrets(const uint8_t *password, const uint8_t *args); +uint8_t dump_secrets(const uint8_t *password); + +#endif // DECRYPT_H \ No newline at end of file diff --git a/uart.c b/uart.c new file mode 100644 index 0000000..5e4f3e1 --- /dev/null +++ b/uart.c @@ -0,0 +1,50 @@ + +// uart.c + +#include + +#include "uart.h" + +#define MYUBRR 16 // Register value for 115200 baud when U2X0 is enabled + +static struct { + volatile uint8_t data[62], head, tail; +} buffer = { { 0 }, 0, 0 }; + +void UART_init(void) { + UBRR0H = (uint8_t)(MYUBRR >> 8); // Set baud rate High byte + UBRR0L = (uint8_t)MYUBRR; // Set baud rate Low byte + UCSR0A |= (1 << U2X0); // Enable Double Speed Mode + // Enable Transmitter, Receiver and Receive Interrupt hardware trigger + UCSR0B = (1 << TXEN0) | (1 << RXEN0) | (1 << RXCIE0); + UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // 8 data bits, 1 stop bit +} + +void UART_flush(void) { + buffer.head = buffer.tail = 0; +} + +ISR(USART_RX_vect) { + uint8_t next_head = buffer.head + 1, c = UDR0; + if (next_head == sizeof buffer.data) + next_head = 0; + // Store in ring buffer if it isn't full + if (next_head != buffer.tail) { + buffer.data[buffer.head] = c; + buffer.head = next_head; + } +} + +uint8_t UART_bytes_available(void) { + return buffer.head != buffer.tail; +} + +uint8_t UART_read_byte(void) { + unsigned char c = buffer.data[buffer.tail]; + if (buffer.tail != buffer.head) { + buffer.tail = buffer.tail + 1; + if (buffer.tail == sizeof buffer.data) + buffer.tail = 0; + } + return c; +} diff --git a/uart.h b/uart.h new file mode 100644 index 0000000..c0e7c2b --- /dev/null +++ b/uart.h @@ -0,0 +1,37 @@ + +// uart.h + +#ifndef UART_H +#define UART_H + +#include +#include +#include + +void UART_init(void); +void UART_flush(void); +uint8_t UART_bytes_available(void); +uint8_t UART_read_byte(void); + +static inline uint8_t UART_read_byte_blocking(void) { + while (!UART_bytes_available()); + return UART_read_byte(); +} + +static inline void UART_write_byte(uint8_t c) { + while (!(UCSR0A & (1 << UDRE0))); + UDR0 = c; +} + +static inline void UART_write_string(const uint8_t* str) { + while (*str) + UART_write_byte(*str++); +} + +static inline void UART_write_string_partial(const uint8_t* str, size_t len) { + size_t i; + for (i = 0; i < len; i++) + UART_write_byte(str[i]); +} + +#endif // UART_H -- 2.54.0