Keyple Card Calypso C++ Library - 3.2.2
Component of the Keyple C++ middleware
CommandVerifyPin.cpp
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (c) 2025 Calypso Networks Association https://calypsonet.org/ *
3 * *
4 * See the NOTICE file(s) distributed with this work for additional *
5 * information regarding copyright ownership. *
6 * *
7 * This program and the accompanying materials are made available under the *
8 * terms of the Eclipse Public License 2.0 which is available at *
9 * http://www.eclipse.org/legal/epl-2.0 *
10 * *
11 * SPDX-License-Identifier: EPL-2.0 *
12 ******************************************************************************/
13
14#include "keyple/card/calypso/CommandVerifyPin.hpp"
15
16#include <map>
17#include <memory>
18#include <string>
19#include <vector>
20
21#include "keyple/card/calypso/CardAccessForbiddenException.hpp"
22#include "keyple/card/calypso/CardIllegalParameterException.hpp"
23#include "keyple/card/calypso/CardPinException.hpp"
24#include "keyple/card/calypso/CardSecurityContextException.hpp"
25#include "keyple/card/calypso/CardTerminatedException.hpp"
26#include "keyple/core/util/ApduUtil.hpp"
27#include "keypop/calypso/card/transaction/CryptoException.hpp"
28#include "keypop/calypso/card/transaction/CryptoIOException.hpp"
29#include "keypop/calypso/card/transaction/InvalidPinException.hpp"
30#include "keypop/calypso/crypto/symmetric/SymmetricCryptoException.hpp"
31#include "keypop/calypso/crypto/symmetric/SymmetricCryptoIOException.hpp"
32
33namespace keyple {
34namespace card {
35namespace calypso {
36
37using keyple::core::util::ApduUtil;
38using keypop::calypso::card::transaction::CryptoException;
39using keypop::calypso::card::transaction::CryptoIOException;
40using keypop::calypso::card::transaction::InvalidPinException;
41using keypop::calypso::crypto::symmetric::SymmetricCryptoException;
42using keypop::calypso::crypto::symmetric::SymmetricCryptoIOException;
43
44const CardCommandRef CommandVerifyPin::mCommandRef = CardCommandRef::VERIFY_PIN;
45
46const std::map<int, const std::shared_ptr<Command::StatusProperties>>
47 CommandVerifyPin::STATUS_TABLE = [] {
48 std::map<int, const std::shared_ptr<Command::StatusProperties>> m(
49 Command::STATUS_TABLE);
50
51 m.insert(
52 {{0x6700,
53 std::make_shared<StatusProperties>(
54 "Lc value not supported (only 00h, 04h or 08h are supported)",
55 typeid(CardIllegalParameterException))},
56 {0x6900,
57 std::make_shared<StatusProperties>(
58 "Transaction Counter is 0", typeid(CardTerminatedException))},
59 {0x6982,
60 std::make_shared<StatusProperties>(
61 std::string("Security conditions not fulfilled (Get ")
62 + "Challenge not done: challenge unavailable)",
63 typeid(CardSecurityContextException))},
64 {0x6985,
65 std::make_shared<StatusProperties>(
66 "Access forbidden (a session is open or DF is invalidated)",
67 typeid(CardAccessForbiddenException))},
68 {0x63C1,
69 std::make_shared<StatusProperties>(
70 "Incorrect PIN (1 attempt remaining)",
71 typeid(CardPinException))},
72 {0x63C2,
73 std::make_shared<StatusProperties>(
74 "Incorrect PIN (2 attempt remaining)",
75 typeid(CardPinException))},
76 {0x6983,
77 std::make_shared<StatusProperties>(
78 "Presentation rejected (PIN is blocked)",
79 typeid(CardPinException))},
80 {0x6D00,
81 std::make_shared<StatusProperties>(
82 "PIN function not present",
83 typeid(CardIllegalParameterException))}});
84 return m;
85 }();
86
87CommandVerifyPin::CommandVerifyPin(
88 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
89 transactionContext,
90 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
91 const std::vector<std::uint8_t>& pin,
92 std::uint8_t cipheringKif,
93 std::uint8_t cipheringKvc)
94/* CL-CSS-RESPLE.1 */
95: Command(mCommandRef, 0, transactionContext, commandContext)
96, mPin(pin)
97, mIsReadCounterMode(false)
98, mIsPinEncryptedMode(true)
99, mCipheringKif(cipheringKif)
100, mCipheringKvc(cipheringKvc)
101{
102}
103
104CommandVerifyPin::CommandVerifyPin(
105 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
106 transactionContext,
107 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext,
108 const std::vector<std::uint8_t>& pin)
109: Command(mCommandRef, 0, transactionContext, commandContext)
110, mPin(pin)
111, mIsReadCounterMode(false)
112, mIsPinEncryptedMode(false)
113, mCipheringKif(0)
114, mCipheringKvc(0)
115{
116}
117
118CommandVerifyPin::CommandVerifyPin(
119 const std::shared_ptr<DtoAdapters::TransactionContextDto>&
120 transactionContext,
121 const std::shared_ptr<DtoAdapters::CommandContextDto>& commandContext)
122: Command(mCommandRef, 0, transactionContext, commandContext)
123, mPin({})
124, mIsReadCounterMode(true)
125, mIsPinEncryptedMode(false)
126, mCipheringKif(0)
127, mCipheringKvc(0)
128{
129}
130
131void
132CommandVerifyPin::finalizeRequest()
133{
134 if (mIsPinEncryptedMode) {
135 try {
136 mPin = getTransactionContext()
137 ->getSymmetricCryptoCardTransactionManagerSpi()
138 ->cipherPinForPresentation(
139 getTransactionContext()->getCard()->getChallenge(),
140 mPin,
141 std::make_shared<std::uint8_t>(mCipheringKif),
142 std::make_shared<std::uint8_t>(mCipheringKvc));
143
144 } catch (const SymmetricCryptoException& e) {
145 throw CryptoException(e.what(), e);
146
147 } catch (const SymmetricCryptoIOException& e) {
148 throw CryptoIOException(e.what(), e);
149 }
150 }
151
152 /* APDU Case 1 (check status) or 3 (verify) */
153 std::vector<std::uint8_t> apdu;
154
155 if (!mPin.empty()) {
156 apdu = ApduUtil::build(
157 getTransactionContext()->getCard()->getCardClass().getValue(),
158 mCommandRef.getInstructionByte(),
159 0x00, /* CL-PIN-PP1P2.1 */
160 0x00,
161 mPin); /* CL-C1-5BYTE.1 */
162 } else {
163 apdu = ApduUtil::build(
164 getTransactionContext()->getCard()->getCardClass().getValue(),
165 mCommandRef.getInstructionByte(),
166 0x00, /* CL-PIN-PP1P2.1 */
167 0x00,
168 0x00); /* CL-C1-5BYTE.1 */
169 }
170 setApduRequest(std::make_shared<DtoAdapters::ApduRequestAdapter>(apdu));
171
172 addSubName(
173 mIsReadCounterMode ? "Read presentation counter"
174 : mIsPinEncryptedMode ? "Encrypted"
175 : "Plain");
176
177 encryptRequestAndUpdateTerminalSessionMacIfNeeded();
178}
179
180bool
181CommandVerifyPin::isCryptoServiceRequiredToFinalizeRequest() const
182{
183 return mIsPinEncryptedMode || getCommandContext()->isEncryptionActive();
184}
185
186bool
187CommandVerifyPin::synchronizeCryptoServiceBeforeCardProcessing()
188{
189 if (getCommandContext()->isEncryptionActive()) {
190 return false;
191 }
192
193 updateTerminalSessionIfNeeded(APDU_RESPONSE_9000);
194
195 return true;
196}
197
198void
199CommandVerifyPin::parseResponse(std::shared_ptr<ApduResponseApi> apduResponse)
200{
201 decryptResponseAndUpdateTerminalSessionMacIfNeeded(apduResponse);
202
203 try {
204 Command::setApduResponseAndCheckStatus(apduResponse);
205 getTransactionContext()->getCard()->setPinAttemptRemaining(3);
206
207 } catch (const CardPinException&) {
208 switch (apduResponse->getStatusWord()) {
209 case 0x63C2:
210 getTransactionContext()->getCard()->setPinAttemptRemaining(2);
211 break;
212 case 0x63C1:
213 getTransactionContext()->getCard()->setPinAttemptRemaining(1);
214 break;
215 case 0x6983:
216 getTransactionContext()->getCard()->setPinAttemptRemaining(0);
217 break;
218 default: {
219 /* NOP */
220 }
221 }
222
223 /*
224 * Throw a functional exception if the operation do not target the
225 * reading of the attempt counter. Catch it silently otherwise.
226 */
227 if (!mIsReadCounterMode) {
228 throw InvalidPinException(
229 std::string("Invalid PIN. Remaining ")
230 + std::to_string(
231 getTransactionContext()
232 ->getCard()
233 ->getPinAttemptRemaining())
234 + " attempt(s)");
235 }
236 }
237
238 updateTerminalSessionIfNeeded();
239}
240
241const std::map<int, const std::shared_ptr<Command::StatusProperties>>&
242CommandVerifyPin::getStatusTable() const
243{
244 return STATUS_TABLE;
245}
246
247} /* namespace calypso */
248} /* namespace card */
249} /* namespace keyple */