xref: /trafficserver/iocore/net/quic/QUICTypes.h (revision a80d7794)
1 /** @file
2  *
3  *  A brief file description
4  *
5  *  @section license License
6  *
7  *  Licensed to the Apache Software Foundation (ASF) under one
8  *  or more contributor license agreements.  See the NOTICE file
9  *  distributed with this work for additional information
10  *  regarding copyright ownership.  The ASF licenses this file
11  *  to you under the Apache License, Version 2.0 (the
12  *  "License"); you may not use this file except in compliance
13  *  with the License.  You may obtain a copy of the License at
14  *
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  *
17  *  Unless required by applicable law or agreed to in writing, software
18  *  distributed under the License is distributed on an "AS IS" BASIS,
19  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  *  See the License for the specific language governing permissions and
21  *  limitations under the License.
22  */
23 
24 #pragma once
25 
26 #include <cstring>
27 #include "tscore/ink_endian.h"
28 #include "tscore/ink_hrtime.h"
29 #include "tscore/Ptr.h"
30 #include "I_EventSystem.h"
31 
32 #include "I_NetVConnection.h"
33 
34 #include <memory>
35 #include <random>
36 #include <cstdint>
37 #include "tscore/ink_memory.h"
38 #include "tscore/ink_inet.h"
39 #include "openssl/evp.h"
40 
41 using QUICPacketNumber = uint64_t;
42 using QUICVersion      = uint32_t;
43 using QUICStreamId     = uint64_t;
44 using QUICOffset       = uint64_t;
45 
46 static constexpr uint8_t kPacketNumberSpace = 3;
47 
48 // TODO: Update version number
49 // Note: Prefix for drafts (0xff000000) + draft number
50 // Note: Fix "Supported Version" field in test case of QUICPacketFactory_Create_VersionNegotiationPacket
51 // Note: Fix QUIC_ALPN_PROTO_LIST in QUICConfig.cc
52 // Note: Change ExtensionType (QUICTransportParametersHandler::TRANSPORT_PARAMETER_ID) if it's changed
53 constexpr QUICVersion QUIC_SUPPORTED_VERSIONS[] = {
54   0xff000017,
55 };
56 constexpr QUICVersion QUIC_EXERCISE_VERSION = 0x1a2a3a4a;
57 
58 enum class QUICEncryptionLevel {
59   NONE      = -1,
60   INITIAL   = 0,
61   ZERO_RTT  = 1,
62   HANDSHAKE = 2,
63   ONE_RTT   = 3,
64 };
65 
66 // For range-based for loop. This starts from INITIAL to ONE_RTT. It doesn't include NONE.
67 // Defining begin, end, operator*, operator++ doen't work for duplicate symbol issue with libmgmt_p.a :(
68 // TODO: support ZERO_RTT
69 constexpr QUICEncryptionLevel QUIC_ENCRYPTION_LEVELS[] = {
70   QUICEncryptionLevel::INITIAL,
71   QUICEncryptionLevel::ZERO_RTT,
72   QUICEncryptionLevel::HANDSHAKE,
73   QUICEncryptionLevel::ONE_RTT,
74 };
75 
76 // introduce by draft-19 kPacketNumberSpace
77 enum class QUICPacketNumberSpace {
78   None            = -1,
79   Initial         = 0,
80   Handshake       = 1,
81   ApplicationData = 2,
82 };
83 
84 // Devide to QUICPacketType and QUICPacketLongHeaderType ?
85 enum class QUICPacketType : uint8_t {
86   INITIAL             = 0x00, // draft-17 version-specific type
87   ZERO_RTT_PROTECTED  = 0x01, // draft-17 version-specific type
88   HANDSHAKE           = 0x02, // draft-17 version-specific type
89   RETRY               = 0x03, // draft-17 version-specific type
90   VERSION_NEGOTIATION = 0xF0, // Not on the spec. but just for convenience
91   PROTECTED,                  // Not on the spec. but just for convenience
92   STATELESS_RESET,            // Not on the spec. but just for convenience
93   UNINITIALIZED = 0xFF,       // Not on the spec. but just for convenience
94 };
95 
96 // XXX If you add or remove QUICFrameType, you might also need to change QUICFrame::type(const uint8_t *)
97 enum class QUICFrameType : uint8_t {
98   PADDING = 0x00,
99   PING,
100   ACK,
101   ACK_WITH_ECN,
102   RESET_STREAM = 0x04,
103   STOP_SENDING,
104   CRYPTO,
105   NEW_TOKEN,
106   STREAM, // 0x08 - 0x0f
107   MAX_DATA = 0x10,
108   MAX_STREAM_DATA,
109   MAX_STREAMS, // 0x12 - 0x13
110   DATA_BLOCKED = 0x14,
111   STREAM_DATA_BLOCKED,
112   STREAMS_BLOCKED, // 0x16 - 0x17
113   NEW_CONNECTION_ID = 0x18,
114   RETIRE_CONNECTION_ID,
115   PATH_CHALLENGE,
116   PATH_RESPONSE,
117   CONNECTION_CLOSE, // 0x1c - 0x1d
118   UNKNOWN = 0x1e,
119 };
120 
121 enum class QUICVersionNegotiationStatus {
122   NOT_NEGOTIATED, // Haven't negotiated yet
123   NEGOTIATED,     // Negotiated
124   VALIDATED,      // Validated with a one in transport parameters
125   FAILED,         // Negotiation failed
126 };
127 
128 enum class QUICKeyPhase : int {
129   PHASE_0 = 0,
130   PHASE_1,
131   INITIAL,
132   ZERO_RTT,
133   HANDSHAKE,
134 };
135 
136 enum class QUICPacketCreationResult {
137   SUCCESS,
138   FAILED,
139   NO_PACKET,
140   NOT_READY,
141   IGNORED,
142   UNSUPPORTED,
143 };
144 
145 enum class QUICErrorClass {
146   UNDEFINED,
147   TRANSPORT,
148   APPLICATION,
149 };
150 
151 enum class QUICTransErrorCode : uint64_t {
152   NO_ERROR = 0x00,
153   INTERNAL_ERROR,
154   SERVER_BUSY,
155   FLOW_CONTROL_ERROR,
156   STREAM_LIMIT_ERROR,
157   STREAM_STATE_ERROR,
158   FINAL_SIZE_ERROR,
159   FRAME_ENCODING_ERROR,
160   TRANSPORT_PARAMETER_ERROR,
161   PROTOCOL_VIOLATION     = 0x0A,
162   CRYPTO_BUFFER_EXCEEDED = 0x0D,
163   CRYPTO_ERROR           = 0x0100, // 0x100 - 0x1FF
164 };
165 
166 // Application Protocol Error Codes defined in application
167 using QUICAppErrorCode                          = uint64_t;
168 constexpr uint16_t QUIC_APP_ERROR_CODE_STOPPING = 0;
169 
170 class QUICError
171 {
172 public:
~QUICError()173   virtual ~QUICError() {}
174 
175   QUICErrorClass cls = QUICErrorClass::UNDEFINED;
176   uint16_t code      = 0;
177   const char *msg    = nullptr;
178 
179 protected:
QUICError()180   QUICError(){};
QUICError(QUICErrorClass error_class,uint16_t error_code,const char * error_msg=nullptr)181   QUICError(QUICErrorClass error_class, uint16_t error_code, const char *error_msg = nullptr)
182     : cls(error_class), code(error_code), msg(error_msg)
183   {
184   }
185 };
186 
187 class QUICConnectionError : public QUICError
188 {
189 public:
QUICConnectionError()190   QUICConnectionError() : QUICError() {}
QUICConnectionError(QUICTransErrorCode error_code,const char * error_msg=nullptr,QUICFrameType frame_type=QUICFrameType::UNKNOWN)191   QUICConnectionError(QUICTransErrorCode error_code, const char *error_msg = nullptr,
192                       QUICFrameType frame_type = QUICFrameType::UNKNOWN)
193     : QUICError(QUICErrorClass::TRANSPORT, static_cast<uint16_t>(error_code), error_msg), _frame_type(frame_type){};
QUICConnectionError(QUICErrorClass error_class,uint16_t error_code,const char * error_msg=nullptr,QUICFrameType frame_type=QUICFrameType::UNKNOWN)194   QUICConnectionError(QUICErrorClass error_class, uint16_t error_code, const char *error_msg = nullptr,
195                       QUICFrameType frame_type = QUICFrameType::UNKNOWN)
196     : QUICError(error_class, error_code, error_msg), _frame_type(frame_type){};
197 
198   QUICFrameType frame_type() const;
199 
200 private:
201   QUICFrameType _frame_type = QUICFrameType::UNKNOWN;
202 };
203 
204 class QUICStream;
205 
206 class QUICStreamError : public QUICError
207 {
208 public:
QUICStreamError()209   QUICStreamError() : QUICError() {}
QUICStreamError(const QUICStream * s,const QUICTransErrorCode error_code,const char * error_msg=nullptr)210   QUICStreamError(const QUICStream *s, const QUICTransErrorCode error_code, const char *error_msg = nullptr)
211     : QUICError(QUICErrorClass::TRANSPORT, static_cast<uint16_t>(error_code), error_msg), stream(s){};
QUICStreamError(const QUICStream * s,const QUICAppErrorCode error_code,const char * error_msg=nullptr)212   QUICStreamError(const QUICStream *s, const QUICAppErrorCode error_code, const char *error_msg = nullptr)
213     : QUICError(QUICErrorClass::APPLICATION, static_cast<uint16_t>(error_code), error_msg), stream(s){};
214 
215   const QUICStream *stream;
216 };
217 
218 using QUICErrorUPtr           = std::unique_ptr<QUICError>;
219 using QUICConnectionErrorUPtr = std::unique_ptr<QUICConnectionError>;
220 using QUICStreamErrorUPtr     = std::unique_ptr<QUICStreamError>;
221 
222 class QUICConnectionId
223 {
224 public:
225   static uint8_t SCID_LEN;
226 
227   static const int MIN_LENGTH_FOR_INITIAL = 8;
228   static const int MAX_LENGTH             = 20;
229   static const size_t MAX_HEX_STR_LENGTH  = MAX_LENGTH * 2 + 1;
230   static QUICConnectionId ZERO();
231   QUICConnectionId();
232   QUICConnectionId(const uint8_t *buf, uint8_t len);
233 
234   explicit operator bool() const { return true; }
235   /**
236    * Note that this returns a kind of hash code so we can use a ConnectionId as a key for a hashtable.
237    */
238   operator uint64_t() const { return this->_hashcode(); }
239   operator const uint8_t *() const { return this->_id; }
240   bool
operator ==(const QUICConnectionId & x) const241   operator==(const QUICConnectionId &x) const
242   {
243     if (this->_len != x._len) {
244       return false;
245     }
246     return memcmp(this->_id, x._id, this->_len) == 0;
247   }
248 
249   bool
operator !=(const QUICConnectionId & x) const250   operator!=(const QUICConnectionId &x) const
251   {
252     if (this->_len != x._len) {
253       return true;
254     }
255     return memcmp(this->_id, x._id, this->_len) != 0;
256   }
257 
258   /*
259    * This is just for debugging.
260    */
261   uint32_t h32() const;
262   int hex(char *buf, size_t len) const;
263 
264   uint8_t length() const;
265   bool is_zero() const;
266   void randomize();
267 
268 private:
269   uint64_t _hashcode() const;
270   uint8_t _id[MAX_LENGTH];
271   uint8_t _len = 0;
272 };
273 
274 class QUICStatelessResetToken
275 {
276 public:
277   constexpr static int8_t LEN = 16;
278 
QUICStatelessResetToken()279   QUICStatelessResetToken() {}
280   QUICStatelessResetToken(const QUICConnectionId &conn_id, uint32_t instance_id);
QUICStatelessResetToken(const uint8_t * buf)281   QUICStatelessResetToken(const uint8_t *buf) { memcpy(this->_token, buf, QUICStatelessResetToken::LEN); }
282 
283   bool
operator ==(const QUICStatelessResetToken & x) const284   operator==(const QUICStatelessResetToken &x) const
285   {
286     return memcmp(this->_token, x._token, QUICStatelessResetToken::LEN) == 0;
287   }
288 
289   const uint8_t *
buf() const290   buf() const
291   {
292     return _token;
293   }
294 
295 private:
296   uint8_t _token[LEN] = {0};
297 
298   void _generate(uint64_t data);
299 };
300 
301 class QUICAddressValidationToken
302 {
303 public:
304   enum class Type : uint8_t {
305     RESUMPTION,
306     RETRY,
307   };
308 
~QUICAddressValidationToken()309   virtual ~QUICAddressValidationToken(){};
310 
311   static Type
type(const uint8_t * buf)312   type(const uint8_t *buf)
313   {
314     ink_assert(static_cast<Type>(buf[0]) == Type::RESUMPTION || static_cast<Type>(buf[0]) == Type::RETRY);
315     return static_cast<Type>(buf[0]) == Type::RESUMPTION ? Type::RESUMPTION : Type::RETRY;
316   }
317 
318   virtual const uint8_t *buf() const = 0;
319   virtual uint8_t length() const     = 0;
320 };
321 
322 class QUICResumptionToken : public QUICAddressValidationToken
323 {
324 public:
QUICResumptionToken()325   QUICResumptionToken() {}
QUICResumptionToken(const uint8_t * buf,uint8_t len)326   QUICResumptionToken(const uint8_t *buf, uint8_t len) : _token_len(len) { memcpy(this->_token, buf, len); }
327   QUICResumptionToken(const IpEndpoint &src, QUICConnectionId cid, ink_hrtime expire_time);
328 
329   bool
operator ==(const QUICResumptionToken & x) const330   operator==(const QUICResumptionToken &x) const
331   {
332     if (this->_token_len != x._token_len) {
333       return false;
334     }
335     return memcmp(this->_token, x._token, this->_token_len) == 0;
336   }
337 
338   bool is_valid(const IpEndpoint &src) const;
339 
340   const QUICConnectionId cid() const;
341   const ink_hrtime expire_time() const;
342 
343   const uint8_t *
buf() const344   buf() const override
345   {
346     return this->_token;
347   }
348 
349   uint8_t
length() const350   length() const override
351   {
352     return this->_token_len;
353   }
354 
355 private:
356   uint8_t _token[1 + EVP_MAX_MD_SIZE + QUICConnectionId::MAX_LENGTH + 4];
357   unsigned int _token_len;
358 };
359 
360 class QUICRetryToken : public QUICAddressValidationToken
361 {
362 public:
QUICRetryToken()363   QUICRetryToken() {}
QUICRetryToken(const uint8_t * buf,uint8_t len)364   QUICRetryToken(const uint8_t *buf, uint8_t len) : _token_len(len) { memcpy(this->_token, buf, len); }
365   QUICRetryToken(const IpEndpoint &src, QUICConnectionId original_dcid);
366 
367   bool
operator ==(const QUICRetryToken & x) const368   operator==(const QUICRetryToken &x) const
369   {
370     if (this->_token_len != x._token_len) {
371       return false;
372     }
373     return memcmp(this->_token, x._token, this->_token_len) == 0;
374   }
375 
376   bool is_valid(const IpEndpoint &src) const;
377 
378   const QUICConnectionId original_dcid() const;
379 
380   const uint8_t *
buf() const381   buf() const override
382   {
383     return this->_token;
384   }
385 
386   uint8_t
length() const387   length() const override
388   {
389     return this->_token_len;
390   }
391 
392 private:
393   uint8_t _token[1 + EVP_MAX_MD_SIZE + QUICConnectionId::MAX_LENGTH] = {};
394   unsigned int _token_len                                            = 0;
395   QUICConnectionId _original_dcid;
396 };
397 
398 class QUICPreferredAddress
399 {
400 public:
401   constexpr static int16_t MIN_LEN = 41;
402   constexpr static int16_t MAX_LEN = 61;
403 
QUICPreferredAddress(IpEndpoint endpoint_ipv4,IpEndpoint endpoint_ipv6,const QUICConnectionId & cid,QUICStatelessResetToken token)404   QUICPreferredAddress(IpEndpoint endpoint_ipv4, IpEndpoint endpoint_ipv6, const QUICConnectionId &cid,
405                        QUICStatelessResetToken token)
406     : _endpoint_ipv4(endpoint_ipv4), _endpoint_ipv6(endpoint_ipv6), _cid(cid), _token(token), _valid(true)
407   {
408   }
409   QUICPreferredAddress(const uint8_t *buf, uint16_t len);
410 
411   bool is_available() const;
412   bool has_ipv4() const;
413   bool has_ipv6() const;
414   const IpEndpoint endpoint_ipv4() const;
415   const IpEndpoint endpoint_ipv6() const;
416   const QUICConnectionId cid() const;
417   const QUICStatelessResetToken token() const;
418 
419   void store(uint8_t *buf, uint16_t &len) const;
420 
421 private:
422   IpEndpoint _endpoint_ipv4 = {};
423   IpEndpoint _endpoint_ipv6 = {};
424   QUICConnectionId _cid;
425   QUICStatelessResetToken _token;
426   bool _valid = false;
427 };
428 
429 enum class QUICStreamType : uint8_t {
430   CLIENT_BIDI = 0x00,
431   SERVER_BIDI,
432   CLIENT_UNI,
433   SERVER_UNI,
434 };
435 
436 enum class QUICStreamDirection : uint8_t {
437   UNKNOWN = 0,
438   SEND,
439   RECEIVE,
440   BIDIRECTIONAL,
441 };
442 
443 class QUICFiveTuple
444 {
445 public:
QUICFiveTuple()446   QUICFiveTuple(){};
447   QUICFiveTuple(IpEndpoint src, IpEndpoint dst, int protocol);
448   void update(IpEndpoint src, IpEndpoint dst, int protocol);
449   IpEndpoint source() const;
450   IpEndpoint destination() const;
451   int protocol() const;
452 
453 private:
454   IpEndpoint _source;
455   IpEndpoint _destination;
456   int _protocol;
457   uint64_t _hash_code = 0;
458 };
459 
460 class QUICPath
461 {
462 public:
463   QUICPath(IpEndpoint local_ep, IpEndpoint remote_ep);
464   const IpEndpoint &local_ep() const;
465   const IpEndpoint &remote_ep() const;
466 
467   inline bool
operator ==(const QUICPath & x) const468   operator==(const QUICPath &x) const
469   {
470     if ((this->_local_ep.port() != 0 && x._local_ep.port() != 0) && this->_local_ep.port() != x._local_ep.port()) {
471       return false;
472     }
473 
474     if ((this->_remote_ep.port() != 0 && x._remote_ep.port() != 0) && this->_remote_ep.port() != x._remote_ep.port()) {
475       return false;
476     }
477 
478     if ((!IpAddr(this->_local_ep).isAnyAddr() && !IpAddr(x._local_ep).isAnyAddr()) && this->_local_ep != x._local_ep) {
479       return false;
480     }
481 
482     if ((!IpAddr(this->_remote_ep).isAnyAddr() || !IpAddr(x._remote_ep).isAnyAddr()) && this->_remote_ep != x._remote_ep) {
483       return false;
484     }
485 
486     return true;
487   }
488 
489 private:
490   IpEndpoint _local_ep;
491   IpEndpoint _remote_ep;
492 };
493 
494 class QUICPathHasher
495 {
496 public:
497   std::size_t
operator ()(const QUICPath & k) const498   operator()(const QUICPath &k) const
499   {
500     return k.remote_ep().port();
501   }
502 };
503 
504 class QUICPathValidationData
505 {
506 public:
QUICPathValidationData(const uint8_t * data)507   QUICPathValidationData(const uint8_t *data) { memcpy(this->_data, data, sizeof(this->_data)); }
508 
509   inline operator const uint8_t *() const { return this->_data; }
510 
511 private:
512   uint8_t _data[8];
513 };
514 
515 class QUICTPConfig
516 {
517 public:
518   virtual uint32_t no_activity_timeout() const                 = 0;
519   virtual const IpEndpoint *preferred_address_ipv4() const     = 0;
520   virtual const IpEndpoint *preferred_address_ipv6() const     = 0;
521   virtual uint32_t initial_max_data() const                    = 0;
522   virtual uint32_t initial_max_stream_data_bidi_local() const  = 0;
523   virtual uint32_t initial_max_stream_data_bidi_remote() const = 0;
524   virtual uint32_t initial_max_stream_data_uni() const         = 0;
525   virtual uint64_t initial_max_streams_bidi() const            = 0;
526   virtual uint64_t initial_max_streams_uni() const             = 0;
527   virtual uint8_t ack_delay_exponent() const                   = 0;
528   virtual uint8_t max_ack_delay() const                        = 0;
529   virtual uint8_t active_cid_limit() const                     = 0;
530 };
531 
532 class QUICLDConfig
533 {
534 public:
~QUICLDConfig()535   virtual ~QUICLDConfig() {}
536   virtual uint32_t packet_threshold() const = 0;
537   virtual float time_threshold() const      = 0;
538   virtual ink_hrtime granularity() const    = 0;
539   virtual ink_hrtime initial_rtt() const    = 0;
540 };
541 
542 class QUICCCConfig
543 {
544 public:
~QUICCCConfig()545   virtual ~QUICCCConfig() {}
546   virtual uint32_t max_datagram_size() const               = 0;
547   virtual uint32_t initial_window() const                  = 0;
548   virtual uint32_t minimum_window() const                  = 0;
549   virtual float loss_reduction_factor() const              = 0;
550   virtual uint32_t persistent_congestion_threshold() const = 0;
551 };
552 
553 // TODO: move version independent functions to QUICInvariants
554 class QUICTypeUtil
555 {
556 public:
557   static bool is_supported_version(QUICVersion version);
558   static QUICStreamType detect_stream_type(QUICStreamId id);
559   static QUICStreamDirection detect_stream_direction(QUICStreamId id, NetVConnectionContext_t context);
560   static QUICEncryptionLevel encryption_level(QUICPacketType type);
561   static QUICPacketType packet_type(QUICEncryptionLevel level);
562   static QUICKeyPhase key_phase(QUICPacketType type);
563   static QUICPacketNumberSpace pn_space(QUICEncryptionLevel level);
564 
565   static QUICConnectionId read_QUICConnectionId(const uint8_t *buf, uint8_t n);
566   static int read_QUICPacketNumberLen(const uint8_t *buf);
567   static QUICPacketNumber read_QUICPacketNumber(const uint8_t *buf, int len);
568   static QUICVersion read_QUICVersion(const uint8_t *buf);
569   static QUICStreamId read_QUICStreamId(const uint8_t *buf);
570   static QUICOffset read_QUICOffset(const uint8_t *buf);
571   static uint16_t read_QUICTransErrorCode(const uint8_t *buf);
572   static QUICAppErrorCode read_QUICAppErrorCode(const uint8_t *buf);
573   static uint64_t read_QUICMaxData(const uint8_t *buf);
574 
575   static void write_QUICConnectionId(QUICConnectionId connection_id, uint8_t *buf, size_t *len);
576   static void write_QUICPacketNumberLen(int len, uint8_t *buf);
577   static void write_QUICPacketNumber(QUICPacketNumber packet_number, uint8_t n, uint8_t *buf, size_t *len);
578   static void write_QUICVersion(QUICVersion version, uint8_t *buf, size_t *len);
579   static void write_QUICStreamId(QUICStreamId stream_id, uint8_t *buf, size_t *len);
580   static void write_QUICOffset(QUICOffset offset, uint8_t *buf, size_t *len);
581   static void write_QUICTransErrorCode(uint64_t error_code, uint8_t *buf, size_t *len);
582   static void write_QUICAppErrorCode(QUICAppErrorCode error_code, uint8_t *buf, size_t *len);
583   static void write_QUICMaxData(uint64_t max_data, uint8_t *buf, size_t *len);
584 
585 private:
586 };
587 
588 class QUICInvariants
589 {
590 public:
591   static bool is_long_header(const uint8_t *buf);
592   static bool is_version_negotiation(QUICVersion v);
593   static bool version(QUICVersion &dst, const uint8_t *buf, uint64_t buf_len);
594   static bool dcil(uint8_t &dst, const uint8_t *buf, uint64_t buf_len);
595   static bool scil(uint8_t &dst, const uint8_t *buf, uint64_t buf_len);
596   static bool dcid(QUICConnectionId &dst, const uint8_t *buf, uint64_t buf_len);
597   static bool scid(QUICConnectionId &dst, const uint8_t *buf, uint64_t buf_len);
598 
599   static const size_t LH_VERSION_OFFSET = 1;
600   static const size_t LH_CIL_OFFSET     = 5;
601   static const size_t LH_DCID_OFFSET    = 6;
602   static const size_t SH_DCID_OFFSET    = 1;
603   static const size_t LH_MIN_LEN        = 6;
604   static const size_t SH_MIN_LEN        = 1;
605 };
606 
607 int to_hex_str(char *dst, size_t dst_len, const uint8_t *src, size_t src_len);
608