Skip to content

Commit

Permalink
Merge libpcsc-cpp and libpcsc-mock repositories
Browse files Browse the repository at this point in the history
WE2-828

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Oct 26, 2023
1 parent 0b5e58c commit dd6c27b
Show file tree
Hide file tree
Showing 29 changed files with 4,125 additions and 4 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
[submodule "lib/libpcsc-cpp"]
path = lib/libpcsc-cpp
url = ../libpcsc-cpp
1 change: 0 additions & 1 deletion lib/libpcsc-cpp
Submodule libpcsc-cpp deleted from fd79af
96 changes: 96 additions & 0 deletions lib/libpcsc-cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
cmake_minimum_required(VERSION 3.16)

project(pcsc-cpp)

# The libpcsc-cpp library itself.

add_library(${PROJECT_NAME}
STATIC
include/${PROJECT_NAME}/${PROJECT_NAME}.hpp
include/${PROJECT_NAME}/${PROJECT_NAME}-utils.hpp
include/${PROJECT_NAME}/comp_winscard.hpp
include/flag-set-cpp/flag_set.hpp
include/magic_enum/magic_enum.hpp
src/Context.hpp
src/Reader.cpp
src/SCardCall.hpp
src/SmartCard.cpp
src/listReaders.cpp
src/utils.cpp
)

target_include_directories(${PROJECT_NAME}
PUBLIC
include
)

target_compile_features(${PROJECT_NAME}
PUBLIC
cxx_std_17
)

target_compile_options(${PROJECT_NAME} PUBLIC
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -pedantic -Werror>
)

target_compile_definitions(${PROJECT_NAME} PUBLIC
$<$<CXX_COMPILER_ID:MSVC>:WIN32_LEAN_AND_MEAN;UNICODE;_CRT_SECURE_NO_WARNINGS>
)

target_link_libraries(${PROJECT_NAME} PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:Ws2_32>
)

# PC/SC API dependencies.

add_library(pcsc INTERFACE)
if(MSVC)
target_link_libraries(pcsc INTERFACE winscard)
elseif(APPLE)
target_link_libraries(pcsc INTERFACE "-framework PCSC")
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(PCSC libpcsclite REQUIRED IMPORTED_TARGET)
target_include_directories(${PROJECT_NAME} PRIVATE ${PCSC_INCLUDE_DIRS})
target_link_libraries(pcsc INTERFACE PkgConfig::PCSC)
endif()

# Common testing options.

enable_testing()

find_package(GTest REQUIRED)

# Mock tests that use libpcsc-mock to mock PC/SC API calls.

set(MOCK_TEST_EXE lib${PROJECT_NAME}-test-mock)

add_subdirectory(tests/lib/libpcsc-mock)

add_executable(${MOCK_TEST_EXE}
tests/mock/test-select-card-reader-and-card.cpp
tests/mock/test-connect-to-card-transmit-apdus.cpp
)

target_link_libraries(${MOCK_TEST_EXE}
${PROJECT_NAME}
pcsc-mock
GTest::Main
)

add_test(${MOCK_TEST_EXE} ${MOCK_TEST_EXE})

# Integration tests that use the real operating system PC/SC service.

set(INTEGRATION_TEST_EXE lib${PROJECT_NAME}-test-integration)

add_executable(${INTEGRATION_TEST_EXE}
tests/integration/test-${PROJECT_NAME}.cpp
)

target_link_libraries(${INTEGRATION_TEST_EXE}
${PROJECT_NAME}
pcsc
GTest::Main
)
21 changes: 21 additions & 0 deletions lib/libpcsc-cpp/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2020-2023 Estonian Information System Authority

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
48 changes: 48 additions & 0 deletions lib/libpcsc-cpp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# libpcsc-cpp

![European Regional Development Fund](https://github.com/open-eid/DigiDoc4-Client/blob/master/client/images/EL_Regionaalarengu_Fond.png)

C++ library for accessing smart cards using the PC/SC API.

## Usage

Example how to list available readers, connect to the smart card in first
reader and transmit an APDU:

auto readers = listReaders();
auto card = readers[0].connectToCard();
auto command = CommandApdu::fromBytes({0x2, 0x1, 0x3, 0x4});

auto transactionGuard = card->beginTransaction();
auto response = card->transmit(command);

See more examples in [tests](tests).

## Building

In Ubuntu:

apt install build-essential pkg-config cmake libgtest-dev valgrind libpcsclite-dev
sudo bash -c 'cd /usr/src/googletest && cmake . && cmake --build . --target install'

cd build
cmake .. # optionally with -DCMAKE_BUILD_TYPE=Debug
cmake --build . # optionally with VERBOSE=1

## Testing

Build as described above, then, inside the `build` directory, run:

ctest # or 'valgrind --leak-check=full ctest'

`ctest` runs tests that use the _libscard-mock_ library to mock PC/SC API calls.

There are also integration tests that use the real operating system PC/SC
service, run them inside `build` directory with:

./libpcsc-cpp-test-integration

## Development guidelines

- Format code with `scripts/clang-format.sh` before committing
- See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)
8 changes: 8 additions & 0 deletions lib/libpcsc-cpp/docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
TODO:

- coding conventions - Qt coding style and conventions
- gtest and winscard-mock usage
- API
- architecture
- technical design
- describe PIMPL (we don't want to leak `winscard.h` to clients)
175 changes: 175 additions & 0 deletions lib/libpcsc-cpp/include/flag-set-cpp/flag_set.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* Copyright (c) 2019 Arnaud Kapp (Xaqq), Barry Revzin, Mart Somermaa
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

// flag_set is a type-safe class for using enums as flags in C++14 with an underlying std::bitset.
// See https://github.com/mrts/flag-set-cpp

#pragma once

#include <bitset>
#include <iostream>
#include <cassert>
#include <string>

template <typename T>
class flag_set
{
public:
flag_set() = default;

explicit flag_set(const T& val) { flags.set(static_cast<u_type>(val)); }

// Binary operations.

flag_set& operator&=(const T& val) noexcept
{
bool tmp = flags.test(static_cast<u_type>(val));
flags.reset();
flags.set(static_cast<u_type>(val), tmp);
return *this;
}

flag_set& operator&=(const flag_set& o) noexcept
{
flags &= o.flags;
return *this;
}

flag_set& operator|=(const T& val) noexcept
{
flags.set(static_cast<u_type>(val));
return *this;
}

flag_set& operator|=(const flag_set& o) noexcept
{
flags |= o.flags;
return *this;
}

// The resulting bitset can contain at most 1 bit.
flag_set operator&(const T& val) const
{
flag_set ret(*this);
ret &= val;

assert(ret.flags.count() <= 1);
return ret;
}

flag_set operator&(const flag_set& val) const
{
flag_set ret(*this);
ret.flags &= val.flags;

return ret;
}

// The resulting bitset contains at least 1 bit.
flag_set operator|(const T& val) const
{
flag_set ret(*this);
ret |= val;

assert(ret.flags.count() >= 1);
return ret;
}

flag_set operator|(const flag_set& val) const
{
flag_set ret(*this);
ret.flags |= val.flags;

return ret;
}

flag_set operator~() const
{
flag_set cp(*this);
cp.flags.flip();

return cp;
}

// The bitset evaluates to true if any bit is set.
explicit operator bool() const { return flags.any(); }

// Methods from std::bitset.

bool operator==(const flag_set& o) const { return flags == o.flags; }

std::size_t size() const { return flags.size(); }

std::size_t count() const { return flags.count(); }

flag_set& set()
{
flags.set();
return *this;
}

flag_set& reset()
{
flags.reset();
return *this;
}

flag_set& flip()
{
flags.flip();
return *this;
}

flag_set& set(const T& val, bool value = true)
{
flags.set(static_cast<u_type>(val), value);
return *this;
}

flag_set& reset(const T& val)
{
flags.reset(static_cast<u_type>(val));
return *this;
}

flag_set& flip(const T& val)
{
flags.flip(static_cast<u_type>(val));
return *this;
}

constexpr bool operator[](const T& val) const { return flags[static_cast<u_type>(val)]; }

std::string to_string() const { return flags.to_string(); }

// Operator for outputting to stream.
friend std::ostream& operator<<(std::ostream& stream, const flag_set& self)
{
return stream << self.flags;
}

private:
using u_type = std::underlying_type_t<T>;

// _ is last value sentinel and must be present in enum T.
std::bitset<static_cast<u_type>(T::_)> flags;
};
Loading

0 comments on commit dd6c27b

Please sign in to comment.