Skip to content

Commit

Permalink
Suggestion: Split hex-to-bytes and bytes-to-words
Browse files Browse the repository at this point in the history
  • Loading branch information
reneme committed Apr 2, 2024
1 parent 2a71a7a commit ce80c8e
Showing 1 changed file with 48 additions and 25 deletions.
73 changes: 48 additions & 25 deletions src/lib/math/mp/mp_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
#include <botan/mem_ops.h>
#include <botan/types.h>
#include <botan/internal/ct_utils.h>
#include <botan/internal/loadstor.h>
#include <botan/internal/mp_asmi.h>
#include <algorithm>
#include <array>
#include <cassert>

namespace Botan {

Expand Down Expand Up @@ -745,41 +747,62 @@ inline constexpr auto bigint_modop_vartime(W n1, W n0, W d) -> W {
return (n0 - z);
}

template <WordType W, size_t N>
consteval auto hex_to_words(const char (&s)[N]) {
// Char count includes null terminator which we ignore
const constexpr size_t C = N - 1;

const size_t S = (C + sizeof(W) * 2 - 1) / (sizeof(W) * 2);

auto hex2int = [](char c) -> int8_t {
consteval void decode_from_hex(std::span<uint8_t> out_bytes, std::span<const char> in_hex) {
auto decode = [](char c) -> uint8_t {
if(c >= '0' && c <= '9') {
return static_cast<int8_t>(c - '0');
return static_cast<uint8_t>(c - '0');
} else if(c >= 'a' && c <= 'f') {
return static_cast<int8_t>(c - 'a' + 10);
return static_cast<uint8_t>(c - 'a' + 10);
} else if(c >= 'A' && c <= 'F') {
return static_cast<int8_t>(c - 'A' + 10);
return static_cast<uint8_t>(c - 'A' + 10);
} else {
return -1;
throw std::runtime_error("Invalid hex character: " + std::to_string(c));
}
};

std::array<W, S> r = {0};
// If the hex string has an odd length, decode the first character first,
// implicitly adding a zero hex byte as a prefix.
assert((in_hex.size() + 1) / 2 == out_bytes.size());
if(in_hex.size() % 2 != 0) {
out_bytes[0] = decode(in_hex[0]);
out_bytes = out_bytes.subspan(1);
in_hex = in_hex.subspan(1);
}

for(size_t i = 0; i != C; ++i) {
const int8_t c = hex2int(s[i]);
if(c >= 0) {
W carry = 0;
for(size_t j = 0; j != S; ++j) {
const W w = r[j];
r[j] = (w << 4) | carry;
carry = w >> (sizeof(W) * 8 - 4);
}
r[0] += c;
}
// Decode the hex string that is now guaranteed to have an even length.
assert(out_bytes.size() * 2 == in_hex.size());
for(auto& byte : out_bytes) {
const uint8_t hi = decode(in_hex[0]);
const uint8_t lo = decode(in_hex[1]);
byte = (hi << 4) | lo;
in_hex = in_hex.subspan(2);
}
}

return r;
template <WordType W, size_t N>
consteval auto hex_to_words(const char (&hex_string)[N]) {
constexpr size_t hex_len = N - 1; // Char count includes null terminator which we ignore
constexpr size_t byte_len = (hex_len + 1) / 2; // hex_string might have an odd length
constexpr size_t word_len = (byte_len + sizeof(W) - 1) / sizeof(W);
constexpr size_t zero_prefix_len = word_len * sizeof(W) - byte_len;
constexpr size_t padded_byte_len = byte_len + zero_prefix_len;

std::array<uint8_t, padded_byte_len> decoded_bytes = {0};
decode_from_hex(std::span<uint8_t>{decoded_bytes}.subspan<zero_prefix_len, byte_len>(),
std::span<const char>{hex_string}.first<hex_len>());

// Decode the (potentially zero-padded) big-endian byte string into "little-
// endian" limbs. Words are decoded from the right of the byte string.
static_assert(decoded_bytes.size() % sizeof(W) == 0, "Padded bytes is a multiple of word size");
static_assert(decoded_bytes.size() / sizeof(W) == word_len, "Padded bytes is the right size");
std::array<W, word_len> words = {0};
std::span<const uint8_t> unread_bytes(decoded_bytes);
for(auto& one_word : words) {
one_word = load_be(unread_bytes.last<sizeof(W)>());
unread_bytes = unread_bytes.first(unread_bytes.size() - sizeof(W));
}

return words;
}

/*
Expand Down

0 comments on commit ce80c8e

Please sign in to comment.