Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
asn1-cer.cc
1 #include "asn1-cer.h"
2 #include <algorithm>
3 
4 static std::string cer_encode_length(size_t len) {
5  std::string res;
6  // 1-byte length
7  if (len <= 127) {
8  res.push_back(static_cast<char>(len));
9  return res;
10  }
11 
12  // multibyte length
13  do {
14  res.push_back(static_cast<char>(len & 0xFF));
15  len >>= 8;
16  } while (len != 0);
17 
18  res.push_back(static_cast<char>(res.length() | 0x80));
19  std::reverse(res.begin(), res.end());
20 
21  return res;
22 }
23 
24 // shifting signed integers right is UB, make sure it's filled with 1s
25 constexpr int32_t shr8(int32_t arg) {
26  return static_cast<int32_t>((static_cast<uint32_t>(arg) >> 8) | ((arg < 0) ? 0xff000000 : 0x00000000));
27 }
28 
29 std::string cer_encode_integer(int32_t number) {
30  std::string res;
31 
32  do {
33  res.push_back(static_cast<char>(number & 0xFF));
34  number = shr8(number);
35  } while ((number != 0) && (static_cast<uint32_t>(number) != 0xffffffff));
36 
37  if (((res[0] & 0x80) != 0) && (number == 0)) {
38  res.push_back(0x00);
39  } else if (((res[0] & 0x80) == 0) && (static_cast<uint32_t>(number) == 0xffffffff)) {
40  res.push_back(static_cast<char>(0xFF));
41  }
42 
43  res.push_back(static_cast<char>(res.length()));
44  std::reverse(res.begin(), res.end());
45  return res;
46 }
47 
48 std::string cer_encode_string(const std::string& contents, ASN1_UniversalTag tag) {
49  size_t len = contents.length();
50 
51  std::string res;
52 
53  if (len <= CER_MAX_PRIMITIVESTRING) {
54  res += cer_encode_length(len);
55  res += contents;
56  return res;
57  }
58 
59  res.push_back(static_cast<char>(0x80));
60  std::string contents_copy = contents;
61  while (!contents_copy.empty()) {
62  res.push_back(static_cast<char>(tag));
63  size_t chunk_size =
64  (contents_copy.length() > CER_MAX_PRIMITIVESTRING) ? CER_MAX_PRIMITIVESTRING : contents_copy.length();
65  res += cer_encode_string(contents_copy.substr(0, chunk_size), tag);
66  contents_copy = contents_copy.substr(chunk_size);
67  }
68  res.push_back(0x00);
69  res.push_back(0x00);
70  return res;
71 }
72 
73 static int32_t cer_decode_length(const std::string& content, int32_t* endpos) {
74  if ((static_cast<uint8_t>(content[0])) == 0x80) {
75  *endpos = 1;
76  return -1;
77  }
78 
79  if (((static_cast<uint8_t>(content[0])) & 0x80) == 0) {
80  *endpos = 1;
81  return content[0];
82  }
83 
84  int32_t len_len = (static_cast<uint8_t>(content[0])) & 0x7F;
85  *endpos = len_len + 1;
86  if (len_len > 4) {
87  return -2;
88  }
89 
90  int32_t res = 0;
91  for (uint32_t i = 0; i < static_cast<uint32_t>(len_len); i++) {
92  res <<= 8;
93  res |= static_cast<int32_t>(content[i + 1UL] & 0xFF);
94  }
95 
96  // In case of overflow number can accidentially take a 'special' value (only -1 now). Make sure it is interpreted as
97  // error.
98  if (res < 0) {
99  res = -2;
100  }
101 
102  return res;
103 }
104 
105 uint8_t cer_decode_token(const std::string& ber, int32_t* endpos, int32_t* int_param, std::string* string_param) {
106  *endpos = 0;
107  if (ber.length() < 2) {
108  return kUnknown;
109  }
110 
111  auto type_class = static_cast<ASN1_Class>(ber[0] & 0xC0);
112  auto tag = static_cast<ASN1_UniversalTag>(ber[0] & 0x1F);
113  bool constructed = !((ber[0] & 0x20) == 0);
114  int32_t len_endpos;
115  int32_t token_len = cer_decode_length(ber.substr(1), &len_endpos);
116 
117  // token_len of -1 is used as indefinite length marker
118  if (token_len < -1) {
119  return kUnknown;
120  }
121 
122  std::string content;
123  if (token_len == -1) { // indefinite form, take the whole tail
124  content = ber.substr(2);
125  } else { // definite form
126  content = ber.substr(static_cast<uint32_t>(1L + len_endpos), static_cast<uint32_t>(token_len));
127  }
128 
129  if (type_class == kAsn1Universal) {
130  switch (tag) {
131  case kAsn1Sequence:
132  if (!constructed) {
133  return kUnknown;
134  }
135 
136  if (int_param != nullptr) {
137  *int_param = token_len;
138  }
139  *endpos = len_endpos + 1;
140  return kAsn1Sequence;
141 
142  case kAsn1EndSequence:
143  if (token_len != 0) {
144  return kUnknown;
145  } else {
146  return kAsn1EndSequence;
147  }
148 
149  case kAsn1Integer:
150  case kAsn1Boolean:
151  case kAsn1Enum: {
152  if (constructed || token_len == -1) {
153  return kUnknown;
154  }
155 
156  // support max. 32 bit-wide integers
157  if (content.length() > 4 || content.length() < 1) {
158  return kUnknown;
159  }
160 
161  bool sign = !((content[0] & 0x80) == 0);
162 
163  int32_t res = 0;
164  for (size_t i = 0; i < content.length(); i++) {
165  res <<= 8;
166  res |= content[i];
167  }
168 
169  if (sign) {
170  for (int i = token_len; i < 4; i++) {
171  res |= (0xff << (i << 3));
172  }
173  }
174 
175  if (int_param != nullptr) {
176  *int_param = res;
177  }
178  *endpos = 1 + len_endpos + token_len;
179  return tag;
180  }
181  case kAsn1OctetString:
182  case kAsn1Utf8String:
183  case kAsn1NumericString:
184  case kAsn1PrintableString:
185  case kAsn1TeletexString:
186  case kAsn1VideotexString:
187  case kAsn1UTCTime:
188  case kAsn1GeneralizedTime:
189  case kAsn1GraphicString:
190  case kAsn1VisibleString:
191  case kAsn1GeneralString:
192  case kAsn1UniversalString:
193  case kAsn1CharacterString:
194  case kAsn1BMPString: {
195  if (token_len >= 0) { // Fixed length encoding
196  if (string_param != nullptr) {
197  *string_param = content;
198  }
199  *endpos = 1 + len_endpos + token_len;
200  } else {
201  int32_t position = 1 + len_endpos;
202  if (string_param != nullptr) {
203  *string_param = std::string();
204  }
205  for (;;) {
206  int32_t internal_endpos;
207  std::string internal_string_param;
208  uint8_t token = cer_decode_token(ber.substr(static_cast<size_t>(position)), &internal_endpos, nullptr,
209  &internal_string_param);
210  if (token == kAsn1EndSequence) {
211  return tag;
212  }
213  if (token != tag) {
214  return kUnknown;
215  }
216 
217  // common case: a string segment
218  if (string_param != nullptr) {
219  *string_param += internal_string_param;
220  }
221  position += internal_endpos;
222  }
223  *endpos = position;
224  }
225  return tag;
226  }
227  default:
228  return kUnknown;
229  }
230  } else { // assume explicit tag, return full tag value except constructed bit
231  if (!constructed) {
232  return kUnknown;
233  }
234 
235  if (int_param != nullptr) {
236  *int_param = token_len;
237  }
238  *endpos = len_endpos + 1;
239 
240  return static_cast<uint8_t>(static_cast<uint8_t>(tag) | static_cast<uint8_t>(type_class));
241  }
242 }