Skip to content

Commit

Permalink
0.0.6: Merge pull request #6 from CODIANZ/development
Browse files Browse the repository at this point in the history
Global refactoring and test code implementation
  • Loading branch information
terukazu-inoue committed Jun 23, 2023
2 parents 533d386 + 3c2e708 commit 69c04ac
Show file tree
Hide file tree
Showing 9 changed files with 481 additions and 100 deletions.
105 changes: 105 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Simple APDU handling library

## Usage

1. Add this repository's `include` directory to the header search path.
1. Add `#include <apdu_cpp/apdu_cpp.h>` in your file.
1. The library namespace is `apdu_cpp`. Add `using namespace adpu_cpp;` if necessary.


## Sample

```cpp
#include <apdu_cpp/apdu_cpp.h>
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
```
1 change: 1 addition & 0 deletions include/apdu-cpp/apdu-cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
112 changes: 105 additions & 7 deletions include/apdu-cpp/data_chunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <vector>
#include <string>
#include <sstream>
#include "endian.h"

namespace apdu_cpp {

Expand Down Expand Up @@ -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();
Expand All @@ -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";
Expand All @@ -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<const uint8_t*>(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<const uint16_t*>(p);
}
else {
return (
(static_cast<uint16_t>(p[0]) << 8) |
(static_cast<uint16_t>(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<const uint32_t*>(p);
}
else{
return (
(static_cast<uint64_t>(p[0]) << 24) |
(static_cast<uint64_t>(p[1]) << 16) |
(static_cast<uint64_t>(p[2]) << 8) |
(static_cast<uint64_t>(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<const uint64_t*>(p);
} else {
return (
(static_cast<uint64_t>(p[0]) << 56) |
(static_cast<uint64_t>(p[1]) << 48) |
(static_cast<uint64_t>(p[2]) << 40) |
(static_cast<uint64_t>(p[3]) << 32) |
(static_cast<uint64_t>(p[4]) << 24) |
(static_cast<uint64_t>(p[5]) << 16) |
(static_cast<uint64_t>(p[6]) << 8) |
(static_cast<uint64_t>(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 */
Expand Down
52 changes: 50 additions & 2 deletions include/apdu-cpp/data_chunk_holder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
24 changes: 24 additions & 0 deletions include/apdu-cpp/endian.h
Original file line number Diff line number Diff line change
@@ -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__) */
18 changes: 5 additions & 13 deletions include/apdu-cpp/rapdu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
Expand All @@ -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);
}
Expand Down
Loading

0 comments on commit 69c04ac

Please sign in to comment.