aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorEgor Tensin <egor@tensin.name>2026-07-22 23:48:16 +0200
committerEgor Tensin <egor@tensin.name>2026-07-23 10:54:24 +0200
commitd6ba2397803c5f2d6a39a8684239c53f84e73b80 (patch)
treec33b3fe76890e05ddea32071f461f892f9d17e6a
parentaesxx/utils: add tests for invalid inputs (diff)
downloadaes-tools-d6ba2397803c5f2d6a39a8684239c53f84e73b80.tar.gz
aes-tools-d6ba2397803c5f2d6a39a8684239c53f84e73b80.zip
aes: always require full inputs
I.e. no 31 digit for 128-bit blocks, reuse code, etc. Also, generalize hex string parsing.
-rw-r--r--aes/include/aes/all.h1
-rw-r--r--aes/include/aes/hex.h25
-rw-r--r--aes/src/block.c15
-rw-r--r--aes/src/hex.c42
-rw-r--r--aes/src/key.c81
-rw-r--r--aesxx/utils/test/basic.cmake18
6 files changed, 105 insertions, 77 deletions
diff --git a/aes/include/aes/all.h b/aes/include/aes/all.h
index fb0a191..8c4b04d 100644
--- a/aes/include/aes/all.h
+++ b/aes/include/aes/all.h
@@ -10,6 +10,7 @@
#include "block.h"
#include "box.h"
#include "error.h"
+#include "hex.h"
#include "key.h"
#include "mode.h"
#include "padding.h"
diff --git a/aes/include/aes/hex.h b/aes/include/aes/hex.h
new file mode 100644
index 0000000..d0e4586
--- /dev/null
+++ b/aes/include/aes/hex.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2026 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "AES tools" project.
+// For details, see https://github.com/egor-tensin/aes-tools.
+// Distributed under the MIT License.
+
+#pragma once
+
+#include "error.h"
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+AES_StatusCode aes_parse_hex_string(
+ unsigned char* dest,
+ const char* src,
+ size_t numof_bytes,
+ AES_ErrorDetails* err_details
+);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/aes/src/block.c b/aes/src/block.c
index 3fbd24f..1b72b62 100644
--- a/aes/src/block.c
+++ b/aes/src/block.c
@@ -77,19 +77,12 @@ AES_StatusCode aes_parse_block(AES_Block* dest, const char* src, AES_ErrorDetail
if (src == NULL)
return aes_error_null_argument(err_details, "src");
- const char* cursor = src;
-
AES_ALIGN(unsigned char, 16) bytes[16];
- for (int i = 0; i < 16; ++i) {
- int n;
- unsigned int byte;
- if (sscanf(cursor, "%2x%n", &byte, &n) != 1)
- return aes_error_parse(err_details, src, "a 128-bit block");
- bytes[i] = (unsigned char)byte;
- cursor += n;
- }
+ AES_StatusCode status = aes_parse_hex_string(bytes, src, sizeof(bytes), err_details);
+ if (aes_is_error(status))
+ return status;
*dest = aes_load_block_aligned(bytes);
- return AES_SUCCESS;
+ return status;
}
diff --git a/aes/src/hex.c b/aes/src/hex.c
new file mode 100644
index 0000000..62b6e5a
--- /dev/null
+++ b/aes/src/hex.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2026 Egor Tensin <Egor.Tensin@gmail.com>
+ * This file is part of the "AES tools" project.
+ * For details, see https://github.com/egor-tensin/aes-tools.
+ * Distributed under the MIT License.
+ */
+
+#include <aes/all.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+AES_StatusCode aes_parse_hex_string(
+ unsigned char* dest,
+ const char* src,
+ size_t numof_bytes,
+ AES_ErrorDetails* err_details
+) {
+ if (dest == NULL)
+ return aes_error_null_argument(err_details, "dest");
+ if (src == NULL)
+ return aes_error_null_argument(err_details, "src");
+
+ char what[30];
+ snprintf(what, sizeof(what), "a %zu-byte hex string", numof_bytes);
+
+ const char* cursor = src;
+
+ for (size_t i = 0; i < numof_bytes; ++i) {
+ int n;
+ unsigned int byte;
+ if (sscanf(cursor, "%2x%n", &byte, &n) != 1 || n != 2)
+ return aes_error_parse(err_details, src, what);
+ dest[i] = (unsigned char)byte;
+ cursor += n;
+ }
+
+ if (*cursor != '\0')
+ return aes_error_parse(err_details, src, what);
+
+ return AES_SUCCESS;
+}
diff --git a/aes/src/key.c b/aes/src/key.c
index 64cd460..1251a8c 100644
--- a/aes/src/key.c
+++ b/aes/src/key.c
@@ -98,6 +98,11 @@ AES_StatusCode aes256_format_key(
}
AES_StatusCode aes128_parse_key(AES128_Key* dest, const char* src, AES_ErrorDetails* err_details) {
+ if (dest == NULL)
+ return aes_error_null_argument(err_details, "dest");
+ if (src == NULL)
+ return aes_error_null_argument(err_details, "src");
+
return aes_parse_block(&dest->key, src, err_details);
}
@@ -107,40 +112,17 @@ AES_StatusCode aes192_parse_key(AES192_Key* dest, const char* src, AES_ErrorDeta
if (src == NULL)
return aes_error_null_argument(err_details, "src");
- const char* cursor = src;
-
- {
- AES_ALIGN(unsigned char, 16) bytes[16];
-
- for (int i = 0; i < 16; ++i) {
- int n;
- unsigned int byte;
- if (sscanf(cursor, "%2x%n", &byte, &n) != 1)
- return aes_error_parse(err_details, src, "a 192-bit block");
- bytes[i] = (unsigned char)byte;
- cursor += n;
- }
+ AES_ALIGN(unsigned char, 16) bytes[32];
+ memset(bytes, 0x00, sizeof(bytes));
- dest->lo = aes_load_block_aligned(bytes);
- }
+ AES_StatusCode status = aes_parse_hex_string(bytes, src, 24, err_details);
+ if (aes_is_error(status))
+ return status;
- {
- AES_ALIGN(unsigned char, 16) bytes[16];
-
- for (int i = 0; i < 8; ++i) {
- int n;
- unsigned int byte;
- if (sscanf(cursor, "%2x%n", &byte, &n) != 1)
- return aes_error_parse(err_details, src, "a 192-bit block");
- bytes[i] = (unsigned char)byte;
- cursor += n;
- }
+ dest->lo = aes_load_block_aligned(bytes);
+ dest->hi = aes_load_block_aligned(bytes + 16);
- memset(bytes + 8, 0x00, 8);
- dest->hi = aes_load_block_aligned(bytes);
- }
-
- return AES_SUCCESS;
+ return status;
}
AES_StatusCode aes256_parse_key(AES256_Key* dest, const char* src, AES_ErrorDetails* err_details) {
@@ -149,37 +131,14 @@ AES_StatusCode aes256_parse_key(AES256_Key* dest, const char* src, AES_ErrorDeta
if (src == NULL)
return aes_error_null_argument(err_details, "src");
- const char* cursor = src;
+ AES_ALIGN(unsigned char, 16) bytes[32];
- {
- AES_ALIGN(unsigned char, 16) bytes[16];
+ AES_StatusCode status = aes_parse_hex_string(bytes, src, sizeof(bytes), err_details);
+ if (aes_is_error(status))
+ return status;
- for (int i = 0; i < 16; ++i) {
- int n;
- unsigned int byte;
- if (sscanf(cursor, "%2x%n", &byte, &n) != 1)
- return aes_error_parse(err_details, src, "a 256-bit block");
- bytes[i] = (unsigned char)byte;
- cursor += n;
- }
+ dest->lo = aes_load_block_aligned(bytes);
+ dest->hi = aes_load_block_aligned(bytes + 16);
- dest->lo = aes_load_block_aligned(bytes);
- }
-
- {
- AES_ALIGN(unsigned char, 16) bytes[16];
-
- for (int i = 0; i < 16; ++i) {
- int n;
- unsigned int byte;
- if (sscanf(cursor, "%2x%n", &byte, &n) != 1)
- return aes_error_parse(err_details, src, "a 256-bit block");
- bytes[i] = (unsigned char)byte;
- cursor += n;
- }
-
- dest->hi = aes_load_block_aligned(bytes);
- }
-
- return AES_SUCCESS;
+ return status;
}
diff --git a/aesxx/utils/test/basic.cmake b/aesxx/utils/test/basic.cmake
index df4150b..2383f61 100644
--- a/aesxx/utils/test/basic.cmake
+++ b/aesxx/utils/test/basic.cmake
@@ -67,7 +67,7 @@ foreach(action encrypt decrypt)
"${CMAKE_SOURCE_DIR}/cmake/tools/ctest-driver.py"
run
--exit-code 1
- --pass-regex [=[AES error: Couldn't parse '0001020304050607' \(possibly not complete input\) as a 256-bit block]=]
+ --pass-regex [=[AES error: Couldn't parse '0001020304050607' \(possibly not complete input\) as a 32-byte hex string]=]
--
"$<TARGET_FILE:${tgt}>" -a aes256 -m ecb 0001020304050607
)
@@ -75,7 +75,7 @@ foreach(action encrypt decrypt)
"${CMAKE_SOURCE_DIR}/cmake/tools/ctest-driver.py"
run
--exit-code 1
- --pass-regex [=[AES error: Couldn't parse 'foobar' \(possibly not complete input\) as a 256-bit block]=]
+ --pass-regex [=[AES error: Couldn't parse 'foobar' \(possibly not complete input\) as a 32-byte hex string]=]
--
"$<TARGET_FILE:${tgt}>" -a aes256 -m ecb foobar
)
@@ -83,16 +83,24 @@ foreach(action encrypt decrypt)
"${CMAKE_SOURCE_DIR}/cmake/tools/ctest-driver.py"
run
--exit-code 1
- --pass-regex [=[AES error: Couldn't parse '2222222222222222222222222222222' \(possibly not complete input\) as a 128-bit block]=]
+ --pass-regex [=[AES error: Couldn't parse '2222222222222222222222222222222' \(possibly not complete input\) as a 16-byte hex string]=]
--
"$<TARGET_FILE:${tgt}>" -a aes128 -m cbc 11111111111111111111111111111111 2222222222222222222222222222222
)
+ add_test(NAME "${tgt}_invalid_iv2" COMMAND Python3::Interpreter
+ "${CMAKE_SOURCE_DIR}/cmake/tools/ctest-driver.py"
+ run
+ --exit-code 1
+ --pass-regex [=[AES error: Couldn't parse '222222222222222222222222222222222' \(possibly not complete input\) as a 16-byte hex string]=]
+ --
+ "$<TARGET_FILE:${tgt}>" -a aes128 -m cbc 11111111111111111111111111111111 222222222222222222222222222222222
+ )
add_test(NAME "${tgt}_invalid_block" COMMAND Python3::Interpreter
"${CMAKE_SOURCE_DIR}/cmake/tools/ctest-driver.py"
run
--exit-code 1
- --pass-regex [=[AES error: Couldn't parse '0' \(possibly not complete input\) as a 128-bit block]=]
+ --pass-regex [=[AES error: Couldn't parse '0' \(possibly not complete input\) as a 16-byte hex string]=]
--
- "$<TARGET_FILE:${tgt}>" -a aes128 -m cbc 11111111111111111111111111111111 2222222222222222222222222222222 0
+ "$<TARGET_FILE:${tgt}>" -a aes128 -m cbc 11111111111111111111111111111111 22222222222222222222222222222222 0
)
endforeach()