Skip to content

Clear PIN from buffer on destruction#138

Open
metsma wants to merge 1 commit intomainfrom
verify
Open

Clear PIN from buffer on destruction#138
metsma wants to merge 1 commit intomainfrom
verify

Conversation

@metsma
Copy link
Contributor

@metsma metsma commented Oct 17, 2025

WE2-1055

Signed-off-by: Raul Metsma raul@metsma.ee

@mrts
Copy link
Member

mrts commented Oct 31, 2025

Isn't it better to use a separate dedicated class for VERIFY PIN?

See #84

@metsma
Copy link
Contributor Author

metsma commented Nov 1, 2025

Isn't it better to use a separate dedicated class for VERIFY PIN?

See #84

It has separate struct?

@mrts mrts mentioned this pull request Feb 23, 2026
WE2-1055

Signed-off-by: Raul Metsma <raul@metsma.ee>
using CommandApdu::CommandApdu;
PCSC_CPP_DISABLE_COPY(VerifyApdu);
PCSC_CPP_DEFAULT_MOVE(VerifyApdu);
constexpr ~VerifyApdu() noexcept final { std::fill(d.begin(), d.end(), byte_type(0)); }
Copy link
Member

@mrts mrts Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use memset_explicit() to ensure that the compiler will not optimize the memory overwrite away? See this and this. However, it is officially available since C23, so I'm unsure if our toolchain supports this on all platforms. In Windows we could use SecureZeroMemory(). Note that neither memset_explicit() or SecureZeroMemory() is constexpr.

We should use memset_explicit() here as well:

  1. https://github.com/web-eid/web-eid-app/blob/main/src/controller/utils/erasedata.hpp#L32-L34
  2. https://github.com/web-eid/web-eid-app/blob/main/src/ui/webeiddialog.cpp#L252-L254

(and elsewhere where we erase the pin).

0x00, 0x20, 0x00, p2, pcsc_cpp::byte_vector(pinMinMax.second, paddingChar)};
response = session.transmitCTL(verifyPin, 0, pinMinMax.first);
response = session.transmitCTL(
pcsc_cpp::CommandApdu::verify(p2, std::move(pin), pinMinMax.second, paddingChar), 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the caller accidentally provides a non-empty PIN for the pin-pad reader case, this change causes it to be embedded into the APDU. Previous code with the dummy padding-only PIN was safer. Or am I mistaken here?

pcsc_cpp::byte_type paddingChar)
{
if (!pin.empty() && pin.capacity() < paddingLength + 5) {
throw std::invalid_argument(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the THROW macro to include source location metadata:

Suggested change
throw std::invalid_argument(
THROW(ProgrammingError,

* Lc and Data field = Empty or verification data
* Le = Empty
*/
static PCSC_CPP_CONSTEXPR_VECTOR VerifyApdu verify(byte_type p2, byte_vector&& pin,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding PCSC_CPP_CONSTEXPR_VECTOR, can and should polymorphic types (with a virtual destructor) be constexpr? CommandApdu and VerifyApdu are polymorphic now and I'm not sure constexpr really applies to them any more.

On the other hand, we could avoid making CommandApdu destructor virtual as the temporary VerifyApdu object is deleted inside verifyPin() directly, not through a CommandApdu pointer or reference.

constexpr ~VerifyApdu() noexcept final { std::fill(d.begin(), d.end(), byte_type(0)); }
};

PCSC_CPP_CONSTEXPR_VECTOR VerifyApdu CommandApdu::verify(byte_type p2, byte_vector&& pin,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be inline?

size_t paddingLength,
pcsc_cpp::byte_type paddingChar)
{
if (!pin.empty() && pin.capacity() < paddingLength + 5) {
Copy link
Member

@mrts mrts Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will fail if pin.size() > paddingLength. You can argue that this will not happen, but we should consider edge cases.

For example, assume pin.size() == 12 and paddingLength == 8, so no padding, payload is 12 and required capacity is 12 + 5 = 17. But if you only check for paddingLength + 5 = 13, there will be a reallocation.

And by ES.45: Avoid “magic constants”; use symbolic constants I would recommend replacing 5 with APDU_HEADER_AND_LC_SIZE or something like that. If you run clang-tidy, you will see a warning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants