Skip to content

Commit 475bfde

Browse files
committed
Generalize TagType; Start impl 4 TLVDecoder; Simplify some utility consumers
1 parent e9cc019 commit 475bfde

13 files changed

Lines changed: 325 additions & 57 deletions

File tree

source/lib/interpreter/detail/common/include/Context.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ namespace interpreter::detail::common
6565
*/
6666
std::span<std::uint8_t const> peekBytes(std::size_t offset, std::size_t size);
6767

68+
/* Returns and consumes just one byte from current position
69+
to current position + 1.
70+
*/
71+
std::uint8_t consumeByte();
72+
6873
/* Returns and consumes size bytes from current position
6974
to current position + size.
7075
Throws runtime_error if size exceeds remaining bytes.
@@ -118,6 +123,8 @@ namespace interpreter::detail::common
118123

119124
bool isEmpty() const;
120125

126+
void ensureEmpty() const;
127+
121128
std::size_t getOverallSize() const;
122129

123130
std::size_t getRemainingSize() const;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// SPDX-FileCopyrightText: (C) 2025 user4223 and (other) contributors to ticket-decoder <https://github.com/user4223/ticket-decoder>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
#pragma once
5+
6+
#include <array>
7+
#include <cstdint>
8+
#include <string>
9+
#include <initializer_list>
10+
11+
namespace interpreter::detail::common
12+
{
13+
class Context;
14+
15+
class TLVTag
16+
{
17+
std::uint32_t value; // this is used in big endian byte order, so do not access it directly or be confused on little endian machines
18+
std::size_t currentSize;
19+
20+
constexpr std::uint8_t *getByte(std::size_t index) const { return ((std::uint8_t *)&value) + index; }
21+
22+
public:
23+
constexpr TLVTag() : value(0), currentSize(0) {}
24+
constexpr TLVTag(std::initializer_list<std::uint8_t> const initial) : TLVTag::TLVTag()
25+
{
26+
auto const *source = initial.begin();
27+
auto *destination = getByte(0);
28+
std::size_t index = 0;
29+
for (; index < initial.size() && index < maximumSize(); ++index)
30+
{
31+
destination[index] = source[index];
32+
}
33+
currentSize = index;
34+
}
35+
36+
TLVTag(TLVTag const &) = default;
37+
TLVTag(TLVTag &&) = default;
38+
39+
TLVTag &operator=(TLVTag const &) = default;
40+
TLVTag &operator=(TLVTag &&) = default;
41+
42+
constexpr void assign(std::size_t index, std::uint8_t value)
43+
{
44+
currentSize = index + 1;
45+
*getByte(index) = value;
46+
}
47+
48+
constexpr std::uint8_t const &operator[](std::size_t index) const { return *getByte(index); }
49+
50+
constexpr std::size_t size() const { return currentSize; }
51+
52+
constexpr std::size_t maximumSize() const { return sizeof(decltype(value)); }
53+
54+
constexpr bool operator==(TLVTag const &rhs) const { return currentSize == rhs.currentSize && value == rhs.value; }
55+
56+
constexpr bool operator!=(TLVTag const &rhs) const { return currentSize != rhs.currentSize || value != rhs.value; }
57+
58+
void ensureEqual(TLVTag const &rhs) const;
59+
60+
std::string toHexString() const;
61+
};
62+
63+
class TLVDecoder
64+
{
65+
public:
66+
constexpr static bool hasSuccessor(std::uint8_t const &value);
67+
68+
static TLVTag consumeTag(common::Context &context);
69+
};
70+
}

source/lib/interpreter/detail/common/source/Context.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ namespace interpreter::detail::common
8181
return std::span<std::uint8_t const>(position + offset, size);
8282
}
8383

84+
std::uint8_t Context::consumeByte()
85+
{
86+
if (getRemainingSize() < 1)
87+
{
88+
throw std::runtime_error("Not enough bytes available to consume");
89+
}
90+
91+
auto value = *position;
92+
position += 1;
93+
return value;
94+
}
95+
8496
std::span<std::uint8_t const> Context::consumeBytes(std::size_t size)
8597
{
8698
if (getRemainingSize() < size)
@@ -163,6 +175,14 @@ namespace interpreter::detail::common
163175
return position == end;
164176
}
165177

178+
void Context::ensureEmpty() const
179+
{
180+
if (!isEmpty())
181+
{
182+
throw std::runtime_error(std::string("Expecting fully consumed context, but found remaining bytes: ") + std::to_string(getRemainingSize()));
183+
}
184+
}
185+
166186
std::size_t Context::getOverallSize() const
167187
{
168188
return std::distance(begin, end);

source/lib/interpreter/detail/common/source/InterpreterUtility.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ namespace interpreter::detail::common
6060

6161
std::uint8_t consumeInteger1(Context &context)
6262
{
63-
return getInteger<std::uint8_t>(context);
63+
return context.consumeByte();
6464
}
6565

6666
std::uint16_t consumeDecimalInteger2(Context &context)
@@ -72,7 +72,7 @@ namespace interpreter::detail::common
7272

7373
std::uint8_t consumeDecimalInteger1(Context &context)
7474
{
75-
auto byte = context.consumeBytes(1)[0];
75+
auto byte = context.consumeByte();
7676
std::uint8_t const high = byte >> 4 & 0x0F;
7777
std::uint8_t const low = byte & 0x0F;
7878
return high * 10 + low;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// SPDX-FileCopyrightText: (C) 2025 user4223 and (other) contributors to ticket-decoder <https://github.com/user4223/ticket-decoder>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
#include "../include/TLVDecoder.h"
5+
6+
#include "lib/interpreter/detail/common/include/Context.h"
7+
8+
#include <sstream>
9+
10+
namespace interpreter::detail::common
11+
{
12+
13+
void TLVTag::ensureEqual(TLVTag const &rhs) const
14+
{
15+
if (*this != rhs)
16+
{
17+
throw std::runtime_error(std::string("Unexpected tag found: ") + toHexString());
18+
}
19+
}
20+
21+
std::string TLVTag::toHexString() const
22+
{
23+
std::stringstream os;
24+
auto const bytes = std::span<std::uint8_t const>(getByte(0), size());
25+
std::for_each(std::begin(bytes), std::end(bytes), [&](auto const &byte)
26+
{ os << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << (int)byte; });
27+
return os.str();
28+
}
29+
30+
constexpr bool TLVDecoder::hasSuccessor(std::uint8_t const &value)
31+
{
32+
return (value & 0x1F) == 0x1F;
33+
}
34+
35+
TLVTag TLVDecoder::consumeTag(common::Context &context)
36+
{
37+
auto tag = TLVTag{0, 0, 0, 0};
38+
tag.assign(0, context.consumeByte());
39+
// auto const usage = (first & 0xC0) >> 6; // 0 universal, 1 application, 2 context-specific, 3 private
40+
// auto const type = (first & 0x20) >> 5; // 0 primitive, 1 constructed
41+
// auto const tag = (first & 0x1F); // 0b11111 (31) see further bytes or else single byte tag value
42+
43+
if (hasSuccessor(tag[0]))
44+
{
45+
for (int index = 1; index < tag.maximumSize(); index++)
46+
{
47+
auto const byte = context.consumeByte();
48+
tag.assign(index, byte);
49+
if ((byte & 0x80) == 0)
50+
{
51+
break;
52+
}
53+
}
54+
}
55+
56+
return tag;
57+
}
58+
}

source/lib/interpreter/detail/vdv/include/VDVUtility.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,32 @@
55

66
#include "Certificate.h"
77

8-
#include "lib/interpreter/detail/common/include/Context.h"
8+
#include "lib/interpreter/detail/common/include/TLVDecoder.h"
99

1010
#include <array>
1111
#include <cstdint>
1212

13-
namespace interpreter::detail::vdv
13+
namespace interpreter::detail::common
1414
{
15+
class Context;
16+
}
1517

16-
using TagType = std::array<std::uint8_t, 2>;
18+
namespace interpreter::detail::vdv
19+
{
1720

1821
std::uint32_t consumeLength(common::Context &context);
1922

20-
TagType consumeTag(common::Context &context);
21-
22-
common::Context &consumeExpectedTag(common::Context &context, TagType const &expectedTag);
23+
common::Context &consumeExpectedTag(common::Context &context, common::TLVTag const &expectedTag);
2324

24-
common::Context &consumeExpectedEndTag(common::Context &context, TagType const &expectedTag);
25+
common::Context &consumeExpectedEndTag(common::Context &context, common::TLVTag const &expectedTag);
2526

26-
common::Context &consumeExpectedFrameTags(common::Context &context, TagType const &expectedBeginTag, TagType const &expectedEndTag);
27+
common::Context &consumeExpectedFrameTags(common::Context &context, common::TLVTag const &expectedBeginTag, common::TLVTag const &expectedEndTag);
2728

28-
std::span<std::uint8_t const> consumeExpectedTagValue(common::Context &context, TagType const &expectedTag);
29+
std::span<std::uint8_t const> consumeExpectedTagValue(common::Context &context, common::TLVTag const &expectedTag);
2930

3031
std::span<std::uint8_t const> consumeExpected(common::Context &context, std::vector<std::uint8_t> expectedValue);
3132

3233
bool peekExpected(common::Context &context, std::vector<std::uint8_t> expectedValue);
3334

34-
void ensureTag(TagType const &tag, TagType const &expectedTag);
35-
36-
void ensureEmpty(common::Context const &context);
35+
void ensureTag(common::TLVTag const &tag, common::TLVTag const &expectedTag);
3736
}

source/lib/interpreter/detail/vdv/source/BotanMessageDecoder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ namespace interpreter::detail::vdv
6262
consumeExpectedFrameTags(context, {0x6A}, {0xBC});
6363
auto const expectedHash = context.consumeBytesEnd(20);
6464
auto content = context.consumeRemainingBytesAppend(signature.remainder);
65-
ensureEmpty(context);
65+
context.ensureEmpty();
6666

6767
auto const actualHash = sha1HashFunction->process(content);
6868
if (!std::equal(actualHash.begin(), actualHash.end(), expectedHash.begin(), expectedHash.end()))

source/lib/interpreter/detail/vdv/source/Certificate.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ namespace interpreter::detail::vdv
2323

2424
Signature Signature::consumeFromEnvelope(common::Context &context)
2525
{
26-
auto value = consumeExpectedTagValue(context, {0x9e, 0x00});
27-
auto remainder = consumeExpectedTagValue(context, {0x9a, 0x00});
26+
auto value = consumeExpectedTagValue(context, {0x9e});
27+
auto remainder = consumeExpectedTagValue(context, {0x9a});
2828
return Signature{std::move(value), std::move(remainder)};
2929
}
3030

@@ -38,11 +38,11 @@ namespace interpreter::detail::vdv
3838
Certificate Certificate::consumeFromEnvelope(common::Context &context)
3939
{
4040
auto const signatureData = consumeExpectedTagValue(context, {0x7f, 0x21});
41-
auto authority = common::bytesToHexString(consumeExpectedTagValue(context, {0x42, 0x00}));
41+
auto authority = common::bytesToHexString(consumeExpectedTagValue(context, {0x42}));
4242

4343
auto signatureContext = common::Context(signatureData);
4444
auto signature = Signature::consumeFrom(signatureContext);
45-
ensureEmpty(signatureContext);
45+
context.ensureEmpty();
4646

4747
return Certificate{std::move(authority), "envelope", std::move(signature), {}};
4848
}
@@ -233,7 +233,7 @@ namespace interpreter::detail::vdv
233233
auto context = common::Context(content);
234234
auto identity = CertificateIdentity::consumeFrom(context, 9);
235235
auto publicKey = PublicKey::consumeFrom(context);
236-
ensureEmpty(context);
236+
context.ensureEmpty();
237237
return DecodedCertificate{std::nullopt, std::move(identity), std::move(publicKey)};
238238
}
239239

@@ -242,7 +242,7 @@ namespace interpreter::detail::vdv
242242
auto context = common::Context(content);
243243
auto identity = CertificateIdentity::consumeFrom(context, 7); // TODO OID length is probably not always 7 for all sub-certificates
244244
auto publicKey = PublicKey::consumeFrom(context);
245-
ensureEmpty(context);
245+
context.ensureEmpty();
246246
return DecodedCertificate{std::make_optional(std::move(content)), std::move(identity), std::move(publicKey)};
247247
}
248248
}

source/lib/interpreter/detail/vdv/source/LDIFFileCertificateProvider.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ namespace interpreter::detail::vdv
3636

3737
auto context = common::Context(data);
3838
auto payload = consumeExpectedTagValue(context, {0x7f, 0x21});
39-
ensureEmpty(context);
39+
context.ensureEmpty();
4040

4141
auto content = std::span<std::uint8_t const>{};
4242
auto signature = std::span<std::uint8_t const>{};
@@ -45,17 +45,17 @@ namespace interpreter::detail::vdv
4545
auto payloadContext = common::Context(payload);
4646
while (!payloadContext.isEmpty())
4747
{
48-
auto const tag = consumeTag(payloadContext);
48+
auto const tag = common::TLVDecoder::consumeTag(payloadContext);
4949
auto const value = payloadContext.consumeBytes(consumeLength(payloadContext));
50-
if (tag == TagType{0x5f, 0x4e})
50+
if (tag == common::TLVTag{0x5f, 0x4e})
5151
{
5252
content = value;
5353
}
54-
else if (tag == TagType{0x5f, 0x37})
54+
else if (tag == common::TLVTag{0x5f, 0x37})
5555
{
5656
signature = value;
5757
}
58-
else if (tag == TagType{0x5f, 0x38})
58+
else if (tag == common::TLVTag{0x5f, 0x38})
5959
{
6060
remainder = value;
6161
}

source/lib/interpreter/detail/vdv/source/VDVInterpreter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ namespace interpreter::detail::vdv
4646

4747
auto const signature = Signature::consumeFromEnvelope(context);
4848
auto const certificate = Certificate::consumeFromEnvelope(context);
49-
ensureEmpty(context);
49+
context.ensureEmpty();
5050

5151
auto const remainderTail = common::Context(signature.remainder).consumeBytesEnd(5);
5252
auto const signatureIdent = common::bytesToString(remainderTail.subspan(0, 3));

0 commit comments

Comments
 (0)