From 3c2e7081c22ef8df209f63ce152082615874f985 Mon Sep 17 00:00:00 2001 From: terukazu inoue Date: Fri, 23 Jun 2023 18:05:52 +0900 Subject: [PATCH] Global refactoring and test code implementation --- README.md | 105 +++++++++++++++ include/apdu-cpp/apdu-cpp.h | 1 + include/apdu-cpp/data_chunk.h | 112 +++++++++++++++- include/apdu-cpp/data_chunk_holder.h | 52 +++++++- include/apdu-cpp/endian.h | 24 ++++ include/apdu-cpp/rapdu.h | 18 +-- include/apdu-cpp/tlv.h | 81 ++---------- include/apdu-cpp/tlv_descriptor.h | 2 +- test/main.cpp | 186 +++++++++++++++++++++++++-- 9 files changed, 481 insertions(+), 100 deletions(-) create mode 100644 README.md create mode 100644 include/apdu-cpp/endian.h diff --git a/README.md b/README.md new file mode 100644 index 0000000..2633ec0 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# Simple APDU handling library + +## Usage + +1. Add this repository's `include` directory to the header search path. +1. Add `#include ` in your file. +1. The library namespace is `apdu_cpp`. Add `using namespace adpu_cpp;` if necessary. + + +## Sample + +```cpp +#include +using namespace apdu_cpp; + +/* + 6F2B800205A0810205A0820139830200008410616263642D313233343536372E637274850100860100870200009000 + 6F 2B + 80 02 05A0 + 81 02 05A0 + 82 01 39 + 83 02 0000 + 84 10 616263642D313233343536372E637274 abcd-1234567.crt + 85 01 00 + 86 01 00 + 87 02 0000 + 9000 +*/ +int main() { + auto chunk = data_chunk::from_hex_string( + "6F2B800205A0810205A0820139830200008410616263642D313233343536372E637274850100860100870200009000" + ); + auto rapdu = rapdu::create(chunk); + std::cout << "status word: " << rapdu.status_word().data().to_hex_string() << std::endl; + auto _tlv = rapdu.get_tlv(); + std::cout << tlv_descriptor::to_string(_tlv) << std::endl; +} +``` + +```log +status word: 9000 +tag: 6F + - class_type: application + - is_constructed: true + - is_primitive: false +length:2B(43) +value: 800205A0810205A0820139830200008410616263642D313233343536372E63727485010086010087020000 +children: + [0] + tag: 80 + - class_type: context_specific + - is_constructed: false + - is_primitive: true + length:02(2) + value: 05A0 + [1] + tag: 81 + - class_type: context_specific + - is_constructed: false + - is_primitive: true + length:02(2) + value: 05A0 + [2] + tag: 82 + - class_type: context_specific + - is_constructed: false + - is_primitive: true + length:01(1) + value: 39 + [3] + tag: 83 + - class_type: context_specific + - is_constructed: false + - is_primitive: true + length:02(2) + value: 0000 + [4] + tag: 84 + - class_type: context_specific + - is_constructed: false + - is_primitive: true + length:10(16) + value: 616263642D313233343536372E637274 + [5] + tag: 85 + - class_type: context_specific + - is_constructed: false + - is_primitive: true + length:01(1) + value: 00 + [6] + tag: 86 + - class_type: context_specific + - is_constructed: false + - is_primitive: true + length:01(1) + value: 00 + [7] + tag: 87 + - class_type: context_specific + - is_constructed: false + - is_primitive: true + length:02(2) + value: 0000 +``` \ No newline at end of file diff --git a/include/apdu-cpp/apdu-cpp.h b/include/apdu-cpp/apdu-cpp.h index a5809e9..90df1b6 100644 --- a/include/apdu-cpp/apdu-cpp.h +++ b/include/apdu-cpp/apdu-cpp.h @@ -3,6 +3,7 @@ #include "data_chunk_holder.h" #include "data_chunk.h" +#include "endian.h" #include "rapdu.h" #include "tlv_descriptor.h" #include "tlv.h" diff --git a/include/apdu-cpp/data_chunk.h b/include/apdu-cpp/data_chunk.h index fb5c702..252e5e5 100644 --- a/include/apdu-cpp/data_chunk.h +++ b/include/apdu-cpp/data_chunk.h @@ -5,6 +5,7 @@ #include #include #include +#include "endian.h" namespace apdu_cpp { @@ -79,15 +80,23 @@ class data_chunk { ~data_chunk() {} bool is_valid() const { return m_data.get() != nullptr; } - operator bool() const { return is_valid(); } + bool is_empty() const { return m_length == 0; } std::size_t size() const { return m_length; } - const value_type* raw_data_ptr() const { return m_data->data() + m_offset; } - - const_iterator cbegin() const { return m_data->data() + m_offset; } - const_iterator cend() const { return m_data->data() + m_offset + m_length; } - const_iterator begin() const { return cbegin(); } - const_iterator end() const { return cend(); } + const value_type* raw_data_ptr() const throw(std::runtime_error) { + if(!is_valid()) throw std::runtime_error("invalid data chunk"); + return m_data->data() + m_offset; + } + const_iterator cbegin() const throw(std::runtime_error) { + if(!is_valid()) throw std::runtime_error("invalid data chunk"); + return m_data->data() + m_offset; + } + const_iterator cend() const throw(std::runtime_error) { + if(!is_valid()) throw std::runtime_error("invalid data chunk"); + return m_data->data() + m_offset + m_length; + } + const_iterator begin() const throw(std::runtime_error) { return cbegin(); } + const_iterator end() const throw(std::runtime_error) { return cend(); } data_chunk get_range(std::size_t offset, std::size_t length) const { if(offset + length > m_length) return data_chunk::invalid(); @@ -99,6 +108,26 @@ class data_chunk { return data_chunk(m_data, m_offset + offset, m_length - offset); } + const value_type& operator [] (std::size_t index) const throw(std::runtime_error) { + if(!is_valid()) throw std::runtime_error("invalid data chunk"); + if(index >= m_length) throw std::runtime_error("invalid index"); + return m_data->at(m_offset + index); + } + + bool operator == (const data_chunk& rhs) const { + if(!is_valid() && !rhs.is_valid()) return true; + if(!is_valid() || !rhs.is_valid()) return false; + if(size() != rhs.size()) return false; + if(rhs.raw_data_ptr() == raw_data_ptr() && + rhs.m_offset == m_offset && + rhs.m_length == m_length) return true; + return std::equal(cbegin(), cend(), rhs.cbegin(), rhs.cend()); + } + + bool operator != (const data_chunk& rhs) const { + return !(*this == rhs); + } + std::string to_hex_string() const { if(!is_valid()) return std::string(); constexpr const char tbl[] = "0123456789ABCDEF"; @@ -117,6 +146,75 @@ class data_chunk { } return str; } + + uint8_t to_uint8() const throw(std::runtime_error) { + if(!is_valid()) throw std::runtime_error("invalid value"); + if(size() != 1) throw std::runtime_error("invalid length"); + return *reinterpret_cast(raw_data_ptr()); + } + + uint16_t to_uint16() const throw(std::runtime_error) { + if(!is_valid()) throw std::runtime_error("invalid value"); + if(size() != 2) throw std::runtime_error("invalid length"); + auto p = raw_data_ptr(); + if APDU_CPP_CONSTEXPR (system_endian == endian::big){ + return *reinterpret_cast(p); + } + else { + return ( + (static_cast(p[0]) << 8) | + (static_cast(p[1]) ) + ); + } + } + + uint32_t to_uint32() const throw(std::runtime_error) { + if(!is_valid()) throw std::runtime_error("invalid value"); + if(size() != 4) throw std::runtime_error("invalid length"); + auto p = raw_data_ptr(); + if APDU_CPP_CONSTEXPR (system_endian == endian::big){ + return *reinterpret_cast(p); + } + else{ + return ( + (static_cast(p[0]) << 24) | + (static_cast(p[1]) << 16) | + (static_cast(p[2]) << 8) | + (static_cast(p[3]) ) + ); + } + } + + uint64_t to_uint64() const throw(std::runtime_error) { + if(!is_valid()) throw std::runtime_error("invalid value"); + if(size() != 8) throw std::runtime_error("invalid length"); + auto p = raw_data_ptr(); + if APDU_CPP_CONSTEXPR (system_endian == endian::big){ + return *reinterpret_cast(p); + } else { + return ( + (static_cast(p[0]) << 56) | + (static_cast(p[1]) << 48) | + (static_cast(p[2]) << 40) | + (static_cast(p[3]) << 32) | + (static_cast(p[4]) << 24) | + (static_cast(p[5]) << 16) | + (static_cast(p[6]) << 8) | + (static_cast(p[7]) ) + ); + } + } + + uint64_t to_uint() const throw(std::runtime_error) { + if(!is_valid()) throw std::runtime_error("invalid value"); + switch(size()) { + case 1: return to_uint8(); + case 2: return to_uint16(); + case 4: return to_uint32(); + case 8: return to_uint64(); + default: throw std::runtime_error("invalid length"); + } + } }; } /** namespace apdu_cpp */ diff --git a/include/apdu-cpp/data_chunk_holder.h b/include/apdu-cpp/data_chunk_holder.h index ba3d565..cb20fcf 100644 --- a/include/apdu-cpp/data_chunk_holder.h +++ b/include/apdu-cpp/data_chunk_holder.h @@ -17,10 +17,58 @@ class data_chunk_holder { virtual ~data_chunk_holder() {} bool is_valid() const { return m_data.is_valid(); } - operator bool() const { return is_valid(); } + bool is_empty() const { return m_data.is_empty(); } std::size_t size() const { return m_data.size(); } - data_chunk data() const { return m_data; } + const data_chunk& data() const { return m_data; } + + const data_chunk::value_type& operator [] (std::size_t index) const throw(std::runtime_error) { + return m_data[index]; + } + + bool operator == (const data_chunk& rhs) const { + return m_data == rhs; + } + + bool operator != (const data_chunk& rhs) const { + return !(*this == rhs); + } + + bool operator == (const data_chunk_holder& rhs) const { + return m_data == rhs.m_data; + } + + bool operator != (const data_chunk_holder& rhs) const { + return !(*this == rhs); + } + + std::string to_hex_string() const { + return m_data.to_hex_string(); + } + + std::string to_string() const { + return m_data.to_string(); + } + + uint8_t to_uint8() const throw(std::runtime_error) { + return m_data.to_uint8(); + } + + uint16_t to_uint16() const throw(std::runtime_error) { + return m_data.to_uint16(); + } + + uint32_t to_uint32() const throw(std::runtime_error) { + return m_data.to_uint32(); + } + + uint64_t to_uint64() const throw(std::runtime_error) { + return m_data.to_uint64(); + } + + uint64_t to_uint() const throw(std::runtime_error) { + return m_data.to_uint(); + } }; } /** namespace apdu_cpp */ diff --git a/include/apdu-cpp/endian.h b/include/apdu-cpp/endian.h new file mode 100644 index 0000000..a533bac --- /dev/null +++ b/include/apdu-cpp/endian.h @@ -0,0 +1,24 @@ +#if !defined(__apdu_cpp_h_endian__) +#define __apdu_cpp_h_endian__ +namespace apdu_cpp { + +enum class endian { + big, little, unknown +}; + +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +constexpr endian system_endian = endian::big; +#elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +constexpr endian system_endian = endian::little; +#else +constexpr endian system_endian = endian::unknown; +#endif + +#if __cplusplus >= 201703L +#define APDU_CPP_CONSTEXPR constexpr +#else +#define APDU_CPP_CONSTEXPR +#endif + +} /** namespace apdu_cpp */ +#endif /** !defined(__apdu_cpp_h_endian__) */ \ No newline at end of file diff --git a/include/apdu-cpp/rapdu.h b/include/apdu-cpp/rapdu.h index ca7bbd3..103b96f 100644 --- a/include/apdu-cpp/rapdu.h +++ b/include/apdu-cpp/rapdu.h @@ -17,7 +17,7 @@ class rapdu { status_word(data_chunk data) : data_chunk_holder(data) {} static status_word create(const data_chunk& src) { do{ - if(!src) break; + if(!src.is_valid()) break; if(src.size() < 2) break; return status_word(src); } while(false); @@ -49,10 +49,6 @@ class rapdu { return data().size() == 2; } - operator bool() const { - return is_valid(); - } - bool is_ok() const { return sw1() == 0x90 && sw2() == 0x00; } @@ -72,7 +68,7 @@ class rapdu { public: static rapdu create(const data_chunk& src) { - if(!src) return rapdu(); + if(!src.is_valid()) return rapdu(); if(src.size() < 2) return rapdu(); const auto data = src.get_range(0, src.size() - 2); @@ -81,21 +77,17 @@ class rapdu { } bool is_valid() const { - return m_status_word; + return m_data.is_valid() && m_status_word.is_valid(); } - operator bool() const { - return is_valid(); + const data_chunk& data() const { + return m_data; } const status_word& status_word() const { return m_status_word; } - const data_chunk& data() const { - return m_data; - } - tlv get_tlv() const { return tlv::create(m_data); } diff --git a/include/apdu-cpp/tlv.h b/include/apdu-cpp/tlv.h index d53ba01..a8912b9 100644 --- a/include/apdu-cpp/tlv.h +++ b/include/apdu-cpp/tlv.h @@ -20,7 +20,7 @@ friend class rapdu; static tag create(const data_chunk& src) { do{ - if(!src) break; + if(!src.is_valid()) break; auto it = src.cbegin(); if(it == src.cend()) break; if((*it & 0x1F) != 0x1F) { @@ -68,12 +68,6 @@ friend class rapdu; if(!is_valid()) return false; return !is_constructed(); } - - bool operator == (const tag& rhs) const { - if(!is_valid()) return false; - if(!rhs) return false; - return std::equal(data().cbegin(), data().cend(), rhs.data().cbegin(), rhs.data().cend()); - } }; struct tag_hash { @@ -93,8 +87,8 @@ friend class rapdu; static length create(const tag& tag, const data_chunk& src){ do{ - if(!tag) break; - if(!src) break; + if(!tag.is_valid()) break; + if(!src.is_valid()) break; return create(src.get_range(tag.size(), src.size() - tag.size())); } while(false); return length(); @@ -102,7 +96,7 @@ friend class rapdu; static length create(const data_chunk& src) { do{ - if(!src) break; + if(!src.is_valid()) break; auto it = src.cbegin(); if(it == src.cend()) break; if(*it & 0x80) { @@ -128,7 +122,7 @@ friend class rapdu; } protected: public: - uint64_t get_length() const { return m_value_length; } + uint64_t get_value_length() const { return m_value_length; } }; class value : public data_chunk_holder { @@ -138,61 +132,16 @@ friend class rapdu; value(data_chunk data) : data_chunk_holder(data) {} static value create(const tag& tag, const length& length, const data_chunk& src) { do{ - if(!tag) break; - if(!length) break; - if(!src) break; - if(src.size() < (tag.size() + length.size() + length.get_length())) break; - return value(src.get_range(tag.size() + length.size(), length.get_length())); + if(!tag.is_valid()) break; + if(!length.is_valid()) break; + if(!src.is_valid()) break; + if(src.size() < (tag.size() + length.size() + length.get_value_length())) break; + return value(src.get_range(tag.size() + length.size(), length.get_value_length())); } while(false); return value(); } protected: public: - uint8_t as_uint8() const throw(std::runtime_error) { - if(!is_valid()) throw std::runtime_error("invalid value"); - if(size() != 1) throw std::runtime_error("invalid length"); - return data().raw_data_ptr()[0]; - } - - uint16_t as_uint16() const throw(std::runtime_error) { - if(!is_valid()) throw std::runtime_error("invalid value"); - if(size() != 2) throw std::runtime_error("invalid length"); - return (data().raw_data_ptr()[0] << 8) | data().raw_data_ptr()[1]; - } - - uint32_t as_uint32() const throw(std::runtime_error) { - if(!is_valid()) throw std::runtime_error("invalid value"); - if(size() != 4) throw std::runtime_error("invalid length"); - return (data().raw_data_ptr()[0] << 24) | (data().raw_data_ptr()[1] << 16) | (data().raw_data_ptr()[2] << 8) | data().raw_data_ptr()[3]; - } - - uint64_t as_uint64() const throw(std::runtime_error) { - if(!is_valid()) throw std::runtime_error("invalid value"); - if(size() != 8) throw std::runtime_error("invalid length"); - return (static_cast(data().raw_data_ptr()[0]) << 56) | (static_cast(data().raw_data_ptr()[1]) << 48) | (static_cast(data().raw_data_ptr()[2]) << 40) | (static_cast(data().raw_data_ptr()[3]) << 32) | (static_cast(data().raw_data_ptr()[4]) << 24) | (static_cast(data().raw_data_ptr()[5]) << 16) | (static_cast(data().raw_data_ptr()[6]) << 8) | static_cast(data().raw_data_ptr()[7]); - } - - template T as_integer() const throw(std::runtime_error) { - if(!is_valid()) throw std::runtime_error("invalid value"); - if(size() > sizeof(T)) throw std::runtime_error("invalid length"); - T value = 0; - for(int i = 0; i < size(); i++) { - value <<= 8; - value |= data().raw_data_ptr()[i]; - } - return value; - } - - uint64_t as_uint() const throw(std::runtime_error) { - if(!is_valid()) throw std::runtime_error("invalid value"); - if(size() > 8) throw std::runtime_error("invalid length"); - uint64_t value = 0; - for(int i = 0; i < size(); i++) { - value <<= 8; - value |= data().raw_data_ptr()[i]; - } - return value; - } }; private: @@ -212,7 +161,7 @@ friend class rapdu; auto it = m_value.data().cbegin(); while(it != m_value.data().cend()){ const auto child = tlv::create(m_value.data().get_range(it - m_value.data().cbegin(), m_value.data().cend() - it)); - if(!child) break; + if(!child.is_valid()) break; m_children.push_back(child); it += child.size(); } @@ -223,7 +172,7 @@ friend class rapdu; public: static tlv create(const data_chunk& src) { - if(!src) return tlv(); + if(!src.is_valid()) return tlv(); const auto tag = tag::create(src); const auto length = length::create(tag, src); const auto value = value::create(tag, length, src); @@ -231,11 +180,7 @@ friend class rapdu; } bool is_valid() const { - return m_tag && m_length && m_value; - } - - operator bool() const { - return is_valid(); + return m_tag.is_valid() && m_length.is_valid() && m_value.is_valid(); } std::size_t size() const { diff --git a/include/apdu-cpp/tlv_descriptor.h b/include/apdu-cpp/tlv_descriptor.h index 44102a6..79b7de2 100644 --- a/include/apdu-cpp/tlv_descriptor.h +++ b/include/apdu-cpp/tlv_descriptor.h @@ -48,7 +48,7 @@ class tlv_descriptor { << _i << " - is_constructed: " << (tlv.get_tag().is_constructed() ? "true" : "false") << std::endl << _i << " - is_primitive: " << (tlv.get_tag().is_primitive() ? "true" : "false") << std::endl << _i << "length:" << tlv.get_length().data().to_hex_string() - << "(" << tlv.get_length().get_length() << ")" << std::endl + << "(" << tlv.get_length().get_value_length() << ")" << std::endl << _i << "value: " << tlv.get_value().data().to_hex_string() << std::endl; if(tlv.get_children().empty()) return; diff --git a/test/main.cpp b/test/main.cpp index 39c3c62..4b77bbc 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -3,24 +3,190 @@ using namespace apdu_cpp; +template bool is_exception_thrown(FUNC func) +{ + try { + func(); + } catch(...) { + return true; + } + return false; +} + +void test_endian() +{ + if(system_endian == endian::big){ + std::cout << "system_endian = endian::big" << std::endl; + } else if(system_endian == endian::little) { + std::cout << "system_endian = endian::little" << std::endl; + } else { + std::cout << "system_endian = endian::unknown" << std::endl; + } + + const uint16_t d = 0x1234; + const uint8_t* p = reinterpret_cast(&d); + if(p[0] == 0x12){ + std::cout << "actual endian: big" << std::endl; + } + else { + std::cout << "actual endian: big" << std::endl; + } +} + void test_data_chunk() { - const std::string str = "Hello, World!"; - auto chunk = data_chunk::create(str.c_str(), str.size()); - std::cout << "to_hex_string: " << chunk.to_hex_string() << std::endl; - std::cout << "to_string: " << chunk.to_string() << std::endl; - - auto chunk2 = data_chunk::from_hex_string(chunk.to_hex_string()); - std::cout << "to_hex_string: " << chunk2.to_hex_string() << std::endl; - std::cout << "to_string: " << chunk2.to_string() << std::endl; + const std::string hello_world = "Hello, World!"; + const std::string hello_akiba = "Hello, Akiba!"; + + auto chunk0 = data_chunk::invalid(); + auto chunk1 = data_chunk::create(hello_world.c_str(), hello_world.size()); + auto chunk2 = data_chunk::from_hex_string(chunk1.to_hex_string()); + auto chunk3 = data_chunk::create(hello_akiba.c_str(), hello_akiba.size()); + + assert(chunk0.is_valid() == false); + assert(chunk0.is_empty() == true); + assert(chunk0.size() == 0); + assert(chunk0.to_string() == ""); + assert(chunk0.to_hex_string() == ""); + assert(chunk0 == chunk0); + assert(chunk0 != chunk1); + assert(is_exception_thrown([&chunk0](){ chunk0.raw_data_ptr(); })); + assert(is_exception_thrown([&chunk0](){ chunk0.cbegin(); })); + assert(is_exception_thrown([&chunk0](){ chunk0.cend(); })); + assert(is_exception_thrown([&chunk0](){ chunk0.begin(); })); + assert(is_exception_thrown([&chunk0](){ chunk0.end(); })); + + assert(chunk0.get_range(0, 5).to_string() == ""); + assert(chunk0.get_range(7, 5).to_string() == ""); + assert(chunk0.get_range(7).to_string() == ""); + assert(chunk0.get_range(0, 0).to_string() == ""); + assert(chunk0.get_range(0, 0) == chunk0); /** ranged invalid is invalid */ + + assert(chunk1.is_valid() == true); + assert(chunk1.is_empty() == false); + assert(chunk1.size() == hello_world.size()); + assert(chunk1.to_string() == hello_world); + assert(chunk1.to_hex_string() == "48656C6C6F2C20576F726C6421"); + assert(chunk1 != chunk0); + assert(chunk1 == chunk1); + assert(chunk1 != chunk3); + assert(!is_exception_thrown([&chunk1](){ chunk1.raw_data_ptr(); })); + assert(!is_exception_thrown([&chunk1](){ chunk1.cbegin(); })); + assert(!is_exception_thrown([&chunk1](){ chunk1.cend(); })); + assert(!is_exception_thrown([&chunk1](){ chunk1.begin(); })); + assert(!is_exception_thrown([&chunk1](){ chunk1.end(); })); + + assert(chunk1.get_range(0, 5).to_string() == "Hello"); + assert(chunk1.get_range(7, 5).to_string() == "World"); + assert(chunk1.get_range(7).to_string() == "World!"); + assert(chunk1.get_range(0, 0).to_string() == ""); + assert(chunk1.get_range(0, 0).size() == 0); + assert(chunk1.get_range(0, 0).to_hex_string() == ""); + assert(chunk1.get_range(0, 0) != chunk0); /** empty is not equal invalid */ + assert(chunk1.get_range(5000, 1000) == chunk0); /** out of range is invalid */ + assert(chunk1.get_range(123) == chunk0); /** out of range is invalid */ + + assert(chunk1 == chunk2); + + assert(chunk3 != chunk1); +} + +/* + 6F2B800205A0810205A0820139830200008410616263642D313233343536372E637274850100860100870200009000 + 6F 2B + 80 02 05A0 + 81 02 05A0 + 82 01 39 + 83 02 0000 + 84 10 616263642D313233343536372E637274 abcd-1234567.crt + 85 01 00 + 86 01 00 + 87 02 0000 + 9000 +*/ + +void test_tlv() +{ + auto chunk = data_chunk::from_hex_string( + "6F2B800205A0810205A0820139830200008410616263642D313233343536372E637274850100860100870200009000" + ); + auto tlv = tlv::create(chunk); + assert(tlv.is_valid() == true); + assert(tlv.size() == chunk.size() - 2); + assert(tlv.get_tag() == tlv::tag::create({0x6F})); + assert(tlv.get_tag() != tlv::tag::create({0x80})); + assert(tlv.get_value() == chunk.get_range(2, chunk.size() - 4)); + + auto children = tlv.get_children(); + assert(children.size() == 8); + assert(children[0].get_tag() == tlv::tag::create({0x80})); + assert(children[1].get_tag() == tlv::tag::create({0x81})); + assert(children[2].get_tag() == tlv::tag::create({0x82})); + assert(children[3].get_tag() == tlv::tag::create({0x83})); + assert(children[4].get_tag() == tlv::tag::create({0x84})); + assert(children[5].get_tag() == tlv::tag::create({0x85})); + assert(children[6].get_tag() == tlv::tag::create({0x86})); + assert(children[7].get_tag() == tlv::tag::create({0x87})); + + assert(children[0].get_length().get_value_length() == 2); + assert(children[1].get_length().get_value_length() == 2); + assert(children[2].get_length().get_value_length() == 1); + assert(children[3].get_length().get_value_length() == 2); + assert(children[4].get_length().get_value_length() == 16); + assert(children[5].get_length().get_value_length() == 1); + assert(children[6].get_length().get_value_length() == 1); + assert(children[7].get_length().get_value_length() == 2); + + assert(children[0].get_length().get_value_length() == children[0].get_value().size()); + assert(children[1].get_length().get_value_length() == children[1].get_value().size()); + assert(children[2].get_length().get_value_length() == children[2].get_value().size()); + assert(children[3].get_length().get_value_length() == children[3].get_value().size()); + assert(children[4].get_length().get_value_length() == children[4].get_value().size()); + assert(children[5].get_length().get_value_length() == children[5].get_value().size()); + assert(children[6].get_length().get_value_length() == children[6].get_value().size()); + assert(children[7].get_length().get_value_length() == children[7].get_value().size()); + + assert(children[0].get_value().to_hex_string() == "05A0"); + assert(children[1].get_value().to_hex_string() == "05A0"); + assert(children[2].get_value().to_hex_string() == "39"); + assert(children[3].get_value().to_hex_string() == "0000"); + assert(children[4].get_value().to_hex_string() == "616263642D313233343536372E637274"); + assert(children[5].get_value().to_hex_string() == "00"); + assert(children[6].get_value().to_hex_string() == "00"); + assert(children[7].get_value().to_hex_string() == "0000"); + + assert(children[4].get_value().to_string() == "abcd-1234567.crt"); + + assert(is_exception_thrown([&children]{ children[0].get_value().to_uint8(); })); + assert(children[0].get_value().to_uint16() == 0x05A0); + assert(is_exception_thrown([&children]{ children[0].get_value().to_uint32(); })); + assert(is_exception_thrown([&children]{ children[0].get_value().to_uint64(); })); + assert(children[0].get_value().to_uint() == 0x05A0); + + auto child_84 = tlv.find_first_child(tlv::tag::create({0x84})); + assert(child_84.get_tag() == tlv::tag::create({0x84})); + assert(child_84.get_value().to_hex_string() == "616263642D313233343536372E637274"); + assert(child_84.get_value().to_string() == "abcd-1234567.crt"); + + auto child_82 = tlv.find_first_child(tlv::tag::create({0x82})); + assert(child_82.get_tag() == tlv::tag::create({0x82})); + assert(child_82.get_value().to_hex_string() == "39"); + assert(child_82.get_value().to_uint8() == 0x39); } + void test_rapdu() { auto chunk = data_chunk::from_hex_string( - "6F2B800205A0810205A082013983020000841074656D702D6B65796C6F61642E637274850100860100870200009000" + "6F2B800205A0810205A0820139830200008410616263642D313233343536372E637274850100860100870200009000" ); auto rapdu = rapdu::create(chunk); + assert(rapdu.is_valid() == true); + assert(rapdu.data().size() == chunk.size() - 2); + assert(rapdu.status_word().data().to_hex_string() == "9000"); + assert(rapdu.status_word().is_ok() == true); + assert(rapdu.get_tlv().get_tag() == tlv::tag::create({0x6F})); + std::cout << "status word: " << rapdu.status_word().data().to_hex_string() << std::endl; auto _tlv = rapdu.get_tlv(); { @@ -30,7 +196,9 @@ void test_rapdu() int main() { + test_endian(); test_data_chunk(); + test_tlv(); test_rapdu(); return 0; } \ No newline at end of file