Botan 3.13.0
Crypto and TLS for C&
x509_serial.cpp
Go to the documentation of this file.
1/*
2* (C) 2026 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#include <botan/pkix_types.h>
8
9#include <botan/assert.h>
10#include <botan/ber_dec.h>
11#include <botan/bigint.h>
12#include <botan/der_enc.h>
13#include <botan/hex.h>
14#include <botan/rng.h>
15#include <botan/internal/asn1_utils.h>
16#include <cstring>
17
18namespace Botan {
19
20X509_Serial_Number::X509_Serial_Number(const BigInt& value) : m_contents(ASN1::integer_contents(value)) {}
21
22X509_Serial_Number X509_Serial_Number::from_bytes(std::span<const uint8_t> bytes) {
23 while(!bytes.empty() && bytes.front() == 0x00) {
24 bytes = bytes.subspan(1);
25 }
26
27 if(bytes.empty()) {
28 return X509_Serial_Number(); // zero
29 }
30
32 sn.m_contents.clear();
33 if((bytes.front() & 0x80) == 0x80) {
34 sn.m_contents.push_back(0x00);
35 }
36 sn.m_contents.insert(sn.m_contents.end(), bytes.begin(), bytes.end());
37 return sn;
38}
39
41 if(contents.empty()) {
42 throw Decoding_Error("Serial number INTEGER encoding has no contents octets");
43 }
44
45 // Normalize away redundant leading octets a BER encoding may carry
46 size_t offset = 0;
47 while(offset + 1 < contents.size() && ((contents[offset] == 0x00 && (contents[offset + 1] & 0x80) == 0x00) ||
48 (contents[offset] == 0xFF && (contents[offset + 1] & 0x80) == 0x80))) {
49 offset += 1;
50 }
51
53 sn.m_contents.assign(contents.begin() + offset, contents.end());
54 return sn;
55}
56
58 std::array<uint8_t, 16> bytes{};
59 rng.randomize(bytes);
60 bytes[0] &= 0x7F; // clear bit 128
61 bytes[0] |= 0x40; // set bit 127
63}
64
66 BOTAN_STATE_CHECK(!m_contents.empty());
67 return (m_contents[0] & 0x80) == 0x80;
68}
69
71 return m_contents.size() == 1 && m_contents[0] == 0x00;
72}
73
74std::vector<uint8_t> X509_Serial_Number::magnitude() const {
75 BOTAN_STATE_CHECK(!m_contents.empty());
76
77 if(is_zero()) {
78 return {};
79 } else if(is_negative()) {
80 return to_bigint().serialize();
81 } else if(m_contents[0] == 0x00) {
82 // Positive value whose leading magnitude bit is set; skip the sign octet
83 return {m_contents.begin() + 1, m_contents.end()};
84 } else {
85 return m_contents;
86 }
87}
88
90 BOTAN_STATE_CHECK(!m_contents.empty());
91 return ASN1::integer_from_contents(m_contents);
92}
93
94std::string X509_Serial_Number::to_string() const {
95 BOTAN_STATE_CHECK(!m_contents.empty());
96 if(is_zero()) {
97 return "00";
98 }
99 const std::string hex = hex_encode(magnitude());
100 return is_negative() ? "-" + hex : hex;
101}
102
104 BOTAN_STATE_CHECK(!m_contents.empty());
106}
107
109 // Decode via BigInt so the decoder's limits apply, in particular the
110 // rejection of non-minimal INTEGER encodings in DER mode
111 BigInt value;
112 from.decode(value);
113 *this = X509_Serial_Number(value);
114}
115
116std::strong_ordering X509_Serial_Number::operator<=>(const X509_Serial_Number& other) const {
117 BOTAN_STATE_CHECK(!m_contents.empty());
118
119 const bool neg = is_negative();
120
121 if(neg != other.is_negative()) {
122 return neg ? std::strong_ordering::less : std::strong_ordering::greater;
123 }
124
125 // Same sign: for positive values the longer encoding is the larger value,
126 // for negative values the longer encoding is the smaller (more negative)
127 if(m_contents.size() != other.m_contents.size()) {
128 const bool shorter = m_contents.size() < other.m_contents.size();
129 return (shorter != neg) ? std::strong_ordering::less : std::strong_ordering::greater;
130 }
131
132 /*
133 * When comparing two values of the same sign in two's complement
134 * encoding, the lexicographic ordering is correct for both signs,
135 * for instance -2 (0xFE) is less than -1 (0xFF)
136 */
137 const int cmp = std::memcmp(m_contents.data(), other.m_contents.data(), m_contents.size());
138 if(cmp < 0) {
139 return std::strong_ordering::less;
140 } else if(cmp > 0) {
141 return std::strong_ordering::greater;
142 } else {
143 return std::strong_ordering::equal;
144 }
145}
146
147} // namespace Botan
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
BER_Decoder & decode(bool &out)
Definition ber_dec.h:358
T serialize(size_t len) const
Definition bigint.h:790
DER_Encoder & add_object(ASN1_Type type_tag, ASN1_Class class_tag, const uint8_t rep[], size_t length)
Definition der_enc.cpp:285
void randomize(std::span< uint8_t > output)
Definition rng.h:86
std::string to_string() const
std::strong_ordering operator<=>(const X509_Serial_Number &other) const
static X509_Serial_Number random(RandomNumberGenerator &rng)
static X509_Serial_Number from_bytes(std::span< const uint8_t > bytes)
void decode_from(BER_Decoder &from) override
std::vector< uint8_t > magnitude() const
static X509_Serial_Number from_der_contents(std::span< const uint8_t > contents)
void encode_into(DER_Encoder &to) const override
BigInt integer_from_contents(std::span< const uint8_t > contents)
Definition ber_dec.cpp:801
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase)
Definition hex.cpp:34