KItinerary

berelement.cpp
1 /*
2  SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "berelement.h"
8 
9 #include <QDebug>
10 #include <QIODevice>
11 #include <QtEndian>
12 
13 using namespace KItinerary;
14 
15 enum {
16  BerLongTypeMask = 0x1F,
17  BerExtendedTypeMask = 0x80,
18  BerExtendedLenghtMask = 0x80,
19  BerVariableLengthMarker = 0x80,
20 };
21 
22 BER::Element::Element() = default;
23 
24 BER::Element::Element(const QByteArray &data, int offset, int size)
25  : m_data(data)
26  , m_offset(offset)
27  , m_dataSize(size < 0 ? data.size() : std::min<int>(offset + size, data.size()))
28 {
29  assert(m_dataSize <= m_data.size());
30  if (!isValid()) {
31  m_data.clear();
32  m_offset = -1;
33  m_dataSize = -1;
34  }
35 }
36 
37 BER::Element::~Element() = default;
38 
40 {
41  if (m_offset < 0 || m_dataSize <= 0 || m_offset + 2 > m_dataSize) {
42  return false;
43  }
44 
45  // check type size
46  const auto ts = typeSize();
47  if (ts < 0 || ts >= 4 || m_offset + ts + 1 > m_dataSize) {
48  return false;
49  }
50 
51  // check size of length field
52  const auto ls = lengthSize();
53  if (ls <= 0 || ls >= 4 || m_offset + ts + ls > m_dataSize) {
54  return false;
55  }
56 
57  // check size of the content
58  const auto cs = contentSize();
59  return cs >= 0 && m_offset + ts + ls + cs <= m_dataSize;
60 }
61 
62 int BER::Element::typeSize() const
63 {
64  assert(m_offset >= 0);
65  assert(m_offset + 1 < m_dataSize);
66  auto it = m_data.begin() + m_offset;
67  if (((*it) & BerLongTypeMask) != BerLongTypeMask) {
68  return 1;
69  }
70 
71  while (it != m_data.end() && std::distance(m_data.begin(), it) < m_dataSize) {
72  ++it;
73  if (((*it) & BerExtendedTypeMask) == 0) {
74  return std::distance(m_data.begin(), it) - m_offset + 1;
75  }
76  }
77 
78  return 0;
79 }
80 
81 uint32_t BER::Element::type() const
82 {
83  const auto ts = typeSize();
84 
85  // this is actually complete wrong compared to BER encoding rules, but since pretty much all docs
86  // we works with use the encoded type ids rather than the decoded ones this is actually much more practical
87  // might be worth to eventually have two methods for this, one with proper decoding and one with the raw data
88  uint32_t result = 0;
89  for (int i = 0; i < ts; ++i) {
90  result <<= 8;
91  result += (uint8_t)*(m_data.constData() + m_offset + i);
92  }
93  return result;
94 }
95 
96 int BER::Element::lengthSize() const
97 {
98  const auto ts = typeSize();
99  const uint8_t firstLengthByte = *(m_data.constData() + m_offset + ts);
100  if (firstLengthByte == BerVariableLengthMarker) {
101  return 1;
102  }
103  if (firstLengthByte & BerExtendedLenghtMask) {
104  return (firstLengthByte & ~BerExtendedLenghtMask) + 1;
105  }
106  return 1;
107 }
108 
110 {
111  const auto ts = typeSize();
112  const auto s = ts + lengthSize() + contentSize();
113  const uint8_t firstLengthByte = *(m_data.constData() + m_offset + ts);
114  if (firstLengthByte == BerVariableLengthMarker) {
115  return s + 2;
116  }
117  return s;
118 }
119 
120 const char *BER::Element::rawData() const
121 {
122  return m_data.constData() + m_offset;
123 }
124 
126 {
127  const auto ts = typeSize();
128  const uint8_t firstLengthByte = *(m_data.constData() + m_offset + ts);
129  if (firstLengthByte == BerVariableLengthMarker) {
130  const auto idx = m_data.indexOf(QByteArray("\0\0", 2), m_offset + ts + 1);
131  if (idx + 1 > m_dataSize) {
132  return - 1;
133  }
134  return idx - m_offset - ts - 1;
135  }
136  if (firstLengthByte & BerExtendedLenghtMask) {
137  const auto ls = firstLengthByte & ~BerExtendedLenghtMask;
138  int result = 0;
139  for (int i = 0; i < ls; ++i) {
140  result <<= 8;
141  result += (uint8_t)*(m_data.constData() + m_offset + ts + 1 + i);
142  }
143  return result;
144  }
145  return firstLengthByte;
146 }
147 
148 int BER::Element::contentOffset() const
149 {
150  return m_offset + typeSize() + lengthSize();
151 }
152 
153 const uint8_t* BER::Element::contentData() const
154 {
155  return reinterpret_cast<const uint8_t*>(m_data.constData() + contentOffset());
156 }
157 
159 {
160  return BER::Element(m_data, contentOffset(), contentSize());
161 }
162 
164 {
165  const auto s = size();
166  if (m_dataSize <= m_offset + s) {
167  return {};
168  }
169  return BER::Element(m_data, m_offset + s, m_dataSize - m_offset - s);
170 }
171 
172 BER::Element BER::Element::find(uint32_t type) const
173 {
174  auto e = first();
175  while (e.isValid()) {
176  if (e.type() == type) {
177  return e;
178  }
179  e = e.next();
180  }
181  return {};
182 }
183 
184 void BER::Element::writeSize(QIODevice *out, int size)
185 {
186  const uint32_t beSize = qToBigEndian((uint32_t)(size));
187  constexpr auto maxBytes = sizeof(size);
188  static_assert(maxBytes == sizeof(beSize), "wrong size datatypes");
189 
190  if (size <= 127) {
191  out->write(((const char*)&beSize) + maxBytes - 1, 1);
192  return;
193  }
194 
195  uint32_t mask = 0xff000000;
196  static_assert(maxBytes == sizeof(mask), "wrong size datatypes");
197  for (int i = maxBytes; i > 0; --i) {
198  if (size & mask) {
199  uint8_t sizeTag = BerExtendedLenghtMask | i;
200  out->write((const char*)&sizeTag, 1);
201  out->write(((const char*)&beSize) + maxBytes - i, i);
202  return;
203  }
204  mask >>= 8;
205  }
206 }
const char * rawData() const
Raw data of this element.
Definition: berelement.cpp:120
Element next() const
Next child element, for nested types.
Definition: berelement.cpp:163
bool isValid() const
Returns true if this element has a valid structure and can be read from.
Definition: berelement.cpp:39
Element find(uint32_t type) const
Returns the first child element of the given type.
Definition: berelement.cpp:172
uint32_t type() const
Type, "right-aligned" in the returned 32bit value.
Definition: berelement.cpp:81
int contentSize() const
Size of the value part of this element.
Definition: berelement.cpp:125
Element first() const
First child element, for nested types.
Definition: berelement.cpp:158
bool isValid(QStringView ifopt)
An element in BER/DER/X.690 encoding.
Definition: berelement.h:27
const uint8_t * contentData() const
Raw content data.
Definition: berelement.cpp:153
int size() const
Size of the entire element (type, size and content).
Definition: berelement.cpp:109
static void writeSize(QIODevice *out, int size)
Writes the given size in BER encoding to out.
Definition: berelement.cpp:184
qint64 write(const char *data, qint64 maxSize)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Thu Feb 15 2024 03:56:47 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.