summaryrefslogtreecommitdiff
path: root/smcc/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'smcc/src/main/java')
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/CypriotEID.java478
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java1
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java2
3 files changed, 302 insertions, 179 deletions
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CypriotEID.java b/smcc/src/main/java/at/gv/egiz/smcc/CypriotEID.java
index 35556049..c1695278 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/CypriotEID.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/CypriotEID.java
@@ -26,6 +26,7 @@ package at.gv.egiz.smcc;
import iaik.me.asn1.*;
import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -34,6 +35,7 @@ import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.Iterator;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
@@ -51,6 +53,8 @@ import at.gv.egiz.smcc.pin.gui.ModifyPINGUI;
import at.gv.egiz.smcc.pin.gui.PINGUI;
import at.gv.egiz.smcc.util.ISO7816Utils;
import at.gv.egiz.smcc.util.SMCCHelper;
+import at.gv.egiz.smcc.util.TLV;
+import at.gv.egiz.smcc.util.TLVSequence;
import at.gv.egiz.smcc.util.TransparentFileInputStream;
public class CypriotEID extends AbstractSignatureCard implements
@@ -63,7 +67,7 @@ public class CypriotEID extends AbstractSignatureCard implements
public static final byte KID_PIN_SIG = (byte) 0x01;
public static final byte[] CD_ID = new byte[] { (byte) 0x70, (byte) 0x05 };
-
+
public static final byte[] MF_ID = new byte[] { (byte) 0x3f, (byte) 0x00 };
public static final byte[] ADF_AWP_ID = new byte[] { (byte) 0xad,
@@ -79,32 +83,32 @@ public class CypriotEID extends AbstractSignatureCard implements
ObjectDirectory od;
protected byte[] cert_id;
-
+
@Override
public void init(Card card, CardTerminal cardTerminal) {
super.init(card, cardTerminal);
log.info("Cypriot EID found");
- pinPinInfo = new PinInfo(4, 8, "[0-9]", "at/gv/egiz/smcc/CypriotEID",
+ pinPinInfo = new PinInfo(4, 64, "[0-9]", "at/gv/egiz/smcc/CypriotEID",
"sig.pin", KID_PIN_SIG, AID_SIG, 3);
- //pinPinInfo.setActive(3);
-
- pukPinInfo = new PinInfo(4, 8, "[0-9]", "at/gv/egiz/smcc/CypriotEID",
+ // pinPinInfo.setActive(3);
+
+ pukPinInfo = new PinInfo(4, 64, "[0-9]", "at/gv/egiz/smcc/CypriotEID",
"sig.puk", KID_PUK_SIG, AID_SIG, 3);
-
+
try {
this.exec_readcd(getCardChannel());
} catch (CardException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ log.warn("Failed to read the certificate ID", e);
+ cert_id = null;
} catch (SignatureCardException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ log.warn("Failed to read the certificate ID", e);
+ cert_id = null;
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ log.warn("Failed to read the certificate ID", e);
+ cert_id = null;
}
}
@@ -116,14 +120,14 @@ public class CypriotEID extends AbstractSignatureCard implements
try {
return exec_readcert(channel);
} catch (CardException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ log.info("Failed to get the certificate.", e);
+ throw new SignatureCardException("Failed to get the certificate.",
+ e);
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ log.info("Failed to get the certificate.", e);
+ throw new SignatureCardException("Failed to get the certificate.",
+ e);
}
-
- return null;
}
@Override
@@ -137,58 +141,92 @@ public class CypriotEID extends AbstractSignatureCard implements
public byte[] createSignature(InputStream input, KeyboxName keyboxName,
PINGUI pinGUI, String alg) throws SignatureCardException,
InterruptedException, IOException {
-
+
byte AlgID = 0;
-
- MessageDigest md;
- try {
- if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)
- && (alg == null || "http://www.w3.org/2000/09/xmldsig#rsa-sha1".equals(alg))) {
- AlgID = (byte) 0x12; // SHA-1 with padding according to PKCS#1 block type 01
- md = MessageDigest.getInstance("SHA-1");
- } else if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)
- && "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256".equals(alg)) {
- AlgID = (byte) 0x41; // SHA-256 with padding according to PKCS#1
- md = MessageDigest.getInstance("SHA256");
- } else {
- throw new SignatureCardException("Card does not support signature algorithm " + alg + ".");
- }
- } catch (NoSuchAlgorithmException e) {
- log.error("Failed to get MessageDigest.", e);
- throw new SignatureCardException(e);
- }
-
- byte[] digest = new byte[md.getDigestLength()];
- for (int l; (l = input.read(digest)) != -1;) {
- md.update(digest, 0, l);
- }
- digest = md.digest();
-
- try
- {
- CardChannel channel = getCardChannel();
-
- // SELECT application
- exec_selectADF(channel);
-
- // MANAGE SECURITY ENVIRONMENT : SET DST
- exec_MSE(channel, AlgID);
- // VERIFY
- verifyPINLoop(channel, pinPinInfo, pinGUI);
-
- // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATRE
- return exec_sign(channel, digest);
- }
- catch (Exception e) {
- e.printStackTrace();
- return null;
+
+ MessageDigest md;
+ try {
+ if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)
+ && (alg == null || "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
+ .equals(alg))) {
+ AlgID = (byte) 0x12; // SHA-1 with padding according to PKCS#1
+ // block type 01
+ md = MessageDigest.getInstance("SHA-1");
+ } else if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)
+ && "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
+ .equals(alg)) {
+ AlgID = (byte) 0x41; // SHA-256 with padding according to PKCS#1
+ md = MessageDigest.getInstance("SHA256");
+ } else {
+ throw new SignatureCardException(
+ "Card does not support signature algorithm " + alg
+ + ".");
+ }
+ } catch (NoSuchAlgorithmException e) {
+ log.error("Failed to get MessageDigest.", e);
+ throw new SignatureCardException(e);
+ }
+
+ byte[] digest = new byte[md.getDigestLength()];
+ for (int l; (l = input.read(digest)) != -1;) {
+ md.update(digest, 0, l);
}
+ digest = md.digest();
+
+ CardChannel channel = getCardChannel();
+
+ try {
+ try {
+ // SELECT application
+ exec_selectADF(channel);
+
+ // MANAGE SECURITY ENVIRONMENT : SET DST
+ exec_MSE(channel, AlgID);
+
+ // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATRE
+ return exec_sign(channel, digest);
+ } catch (SecurityStatusNotSatisfiedException e) {
+ // NEED to provide PIN code ...
+
+ // SELECT application
+ exec_selectADF(channel);
+
+ // MANAGE SECURITY ENVIRONMENT : SET DST
+ exec_MSE(channel, AlgID);
+
+ // VERIFY
+ verifyPINLoop(channel, pinPinInfo, pinGUI);
+
+ // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATRE
+ return exec_sign(channel, digest);
+ }
+ } catch (CardException e) {
+ log.info("Failed to create digital signature", e);
+ throw new SignatureCardException("Failed to create digital signature", e);
+ }
+
}
@Override
public PinInfo[] getPinInfos() throws SignatureCardException {
- // TODO Auto-generated method stub
- return new PinInfo[] { pinPinInfo };
+ PinInfo[] pinInfos = new PinInfo[] { pinPinInfo, pukPinInfo };
+
+ CardChannel channel = getCardChannel();
+ for (PinInfo pinInfo : pinInfos) {
+ if (pinInfo.getState() == PinInfo.STATE.UNKNOWN ) {
+ try {
+ log.debug("Query pin status for {}.", pinInfo.getLocalizedName());
+ testPIN(channel, pinInfo);
+ } catch (Exception e) {
+ log.trace("Failed to execute command.", e);
+ // status already set by verifyPIN
+ }
+ } else if (log.isTraceEnabled()) {
+ log.trace("assume pin status {} to be up to date", pinInfo.getState());
+ }
+ }
+
+ return pinInfos;
}
@Override
@@ -209,12 +247,23 @@ public class CypriotEID extends AbstractSignatureCard implements
}
@Override
+ public String toString() {
+ return ("Oberthur Thechnologies ID-ONE Token SLIM");
+ }
+
+ @Override
public void changePIN(PinInfo pinInfo, ModifyPINGUI changePINGUI)
throws LockedException, NotActivatedException, CancelledException,
PINFormatException, SignatureCardException, InterruptedException {
+
CardChannel channel = getCardChannel();
- exec_unblockPIN(channel, changePINGUI);
+ try {
+ unblockPINLoop(channel, changePINGUI, pinInfo);
+ } catch (CardException e) {
+ log.info("Failed to change PIN.", e);
+ throw new SignatureCardException("Failed to change PIN.", e);
+ }
}
@Override
@@ -222,7 +271,8 @@ public class CypriotEID extends AbstractSignatureCard implements
throws CancelledException, SignatureCardException,
InterruptedException {
log.error("ACTIVATE PIN not supported by Cypriotic EID");
- throw new SignatureCardException("PIN activation not supported by this card.");
+ throw new SignatureCardException(
+ "PIN activation not supported by this card.");
}
@Override
@@ -231,7 +281,12 @@ public class CypriotEID extends AbstractSignatureCard implements
InterruptedException {
CardChannel channel = getCardChannel();
- exec_unblockPIN(channel, pukGUI);
+ try {
+ unblockPINLoop(channel, pukGUI, pinInfo);
+ } catch (CardException e) {
+ log.info("Failed to unblock PIN.", e);
+ throw new SignatureCardException("Failed to unblock PIN.", e);
+ }
}
// //////////////////////////////////////////////////////////////////////
@@ -248,16 +303,28 @@ public class CypriotEID extends AbstractSignatureCard implements
} while (retries > 0);
}
+ protected void unblockPINLoop(CardChannel channel,
+ ModifyPINGUI provider, PinInfo pin) throws InterruptedException, CardException, SignatureCardException{
+
+ int retries = -1;
+ do {
+ retries = exec_unblockPIN(channel, provider, pin);
+ } while (retries > 0);
+ }
+
+ /*
+ * Verify PIN/PUK entry
+ */
protected int verifyPIN(CardChannel channel, PinInfo pinInfo,
PINGUI provider, int retries) throws InterruptedException,
CardException, SignatureCardException {
char[] pin = provider.providePIN(pinInfo, pinInfo.retries);
-
+
byte[] ascii_pin = encodePIN(pin);
exec_selectADF(channel);
- ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00,
+ ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00,
pinInfo.getKID(), ascii_pin));
if (resp.getSW() == 0x9000) {
@@ -284,99 +351,149 @@ public class CypriotEID extends AbstractSignatureCard implements
}
}
+
+ protected int testPIN(CardChannel channel, PinInfo pinInfo) throws InterruptedException,
+ CardException, SignatureCardException {
+
+ exec_selectADF(channel);
+
+ ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00,
+ pinInfo.getKID()));
+
+ if (resp.getSW() == 0x9000) {
+ pinInfo.setActive(pinInfo.maxRetries);
+ return -1;
+ }
+ if (resp.getSW() >> 4 == 0x63c) {
+ pinInfo.setActive(0x0f & resp.getSW());
+ return 0x0f & resp.getSW();
+ }
+
+ switch (resp.getSW()) {
+ case 0x6983:
+ // authentication method blocked
+ pinInfo.setBlocked();
+ throw new LockedException();
+
+ default:
+ String msg = "VERIFY failed. SW="
+ + Integer.toHexString(resp.getSW());
+ log.info(msg);
+ pinInfo.setUnknown();
+ throw new SignatureCardException(msg);
+ }
+
+ }
- private byte[] encodePIN(char[] pin)
- {
- return Charset.forName("ASCII").encode(
- CharBuffer.wrap(pin)).array();
+ private byte[] encodePIN(char[] pin) {
+ return Charset.forName("ASCII").encode(CharBuffer.wrap(pin)).array();
}
-
- protected void exec_unblockPIN(CardChannel channel, ModifyPINGUI changePINGUI) throws CancelledException, InterruptedException
- {
- char[] PUK = changePINGUI.provideCurrentPIN(pukPinInfo, pukPinInfo.retries);
- char[] newPIN = changePINGUI.provideNewPIN(pinPinInfo);
+
+ /*
+ * Unblock PIN with PUK code
+ */
+ protected int exec_unblockPIN(CardChannel channel, ModifyPINGUI changePINGUI, PinInfo pin)
+ throws InterruptedException, CardException, SignatureCardException {
- byte[] ascii_puk = encodePIN(PUK);
- byte[] ascii_pin = encodePIN(newPIN);
+ char[] PUK = changePINGUI.providePUK(pin, pukPinInfo,
+ pukPinInfo.retries);
- try {
- log.debug("PUK: " + new String(PUK) + "(" + getHexString(ascii_puk) + ") NEW PIN: " +
- new String(newPIN) + "(" + getHexString(ascii_pin) + ")");
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
+ char[] newPIN = changePINGUI.provideNewPIN(pin);
- // TODO: INPUT checking PIN SIZES etc.
- /*
- try {
- exec_selectADF(channel);
+ byte[] ascii_puk = encodePIN(PUK);
- ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00,
- pukPinInfo.getKID(), ascii_puk));
-
- if (resp.getSW() == 0x9000) {
- pukPinInfo.setActive(pukPinInfo.maxRetries);
- }
- else
- {
- log.debug("WRONG PUK CODE!! SW=" + resp.getSW());
- return;
- }
-
- resp = channel.transmit(new CommandAPDU(0x00, 0x2C, 0x02,
- pinPinInfo.getKID(), ascii_pin));
-
- if (resp.getSW() == 0x9000) {
- pinPinInfo.setActive(pinPinInfo.maxRetries);
- }
- else
- {
- log.debug("FAILED TO SET PIN! SW=" + resp.getSW());
- }
-
- } catch (CardException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (SignatureCardException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ byte[] ascii_pin = encodePIN(newPIN);
+
+ exec_selectADF(channel);
+
+ ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00,
+ pukPinInfo.getKID(), ascii_puk));
+
+ if (resp.getSW() == 0x9000) {
+ pukPinInfo.setActive(pukPinInfo.maxRetries);
+ } else if (resp.getSW() >> 4 == 0x63c) {
+ pukPinInfo.setActive(0x0f & resp.getSW());
+ return 0x0f & resp.getSW();
+ } else if (resp.getSW() == 0x6983) {
+ // authentication method blocked
+ pukPinInfo.setBlocked();
+ throw new LockedException();
+ } else {
+ String msg = "VERIFY failed. SW="
+ + Integer.toHexString(resp.getSW());
+ log.info(msg);
+ pukPinInfo.setUnknown();
+ throw new SignatureCardException(msg);
+ }
+
+ resp = channel.transmit(new CommandAPDU(0x00, 0x2C, 0x02, pin
+ .getKID(), ascii_pin));
+
+ if (resp.getSW() == 0x9000) {
+ pin.setActive(pin.maxRetries);
+ return -1;
+ } else {
+ String msg = "SET PIN failed. SW="
+ + Integer.toHexString(resp.getSW());
+ log.info(msg);
+ pin.setUnknown();
+ throw new SignatureCardException(msg);
}
- */
+
}
-
+
+ /*
+ * Read certificate based on certificate ID
+ */
protected byte[] exec_readcert(CardChannel channel) throws CardException,
- SignatureCardException, IOException {
- if(cert_id == null)
- {
+ SignatureCardException, IOException {
+ if (cert_id == null) {
exec_readcd(channel);
}
-
- /*if(cert_id == null)
- {
- throw CardException("Failed to read the certificate id");
- }*/
-
+
+ if (cert_id == null) {
+ throw new CardException("Failed to read the certificate id");
+ }
+
exec_selectADF(channel);
exec_selectFILE(channel, cert_id);
-
+
return exec_readBinary(channel);
}
-
+
+ /*
+ * Read and parse CD file to determine certificate ID
+ */
protected void exec_readcd(CardChannel channel) throws CardException,
- SignatureCardException, IOException
- {
+ SignatureCardException, IOException {
exec_selectADF(channel);
exec_selectFILE(channel, CD_ID);
-
+
byte[] cd_buffer = exec_readBinary(channel);
-
- // TODO interpret CD => get CERT ID
-
- cert_id = new byte[] { (byte) 0x34, (byte) 0x01 };
+
+ ASN1 asn = new ASN1(cd_buffer);
+
+ for (int i = 0; i < asn.getSize(); i++) {
+
+ ASN1 element = asn.getElementAt(i);
+
+ if (element.getTagClass() == ASN1.TAG_CONTEXT_SPECIFIC
+ && element.getTypeOnly() == ASN1.TYPE_BOOLEAN) {
+ ASN1 ele = element.gvASN1().getElementAt(0).getElementAt(0);
+ if (ele.getTypeOnly() == ASN1.TYPE_OCTET_STRING) {
+ cert_id = ele.gvByteArray();
+ return;
+ }
+ }
+ }
+ cert_id = null;
+ throw new CardException("Failed to read the certificate ID.");
}
-
+
+ /*
+ * Select the ADF application
+ */
protected void exec_selectADF(CardChannel channel) throws CardException,
SignatureCardException {
@@ -385,8 +502,9 @@ public class CypriotEID extends AbstractSignatureCard implements
}
-
-
+ /*
+ * Select a file in the current context by its id
+ */
protected byte[] exec_selectFILE(CardChannel channel, byte[] file_id)
throws CardException, SignatureCardException {
ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0xA4, 0x01,
@@ -403,23 +521,43 @@ public class CypriotEID extends AbstractSignatureCard implements
return resp.getBytes();
}
- protected void exec_MSE(CardChannel channel, byte algoID) throws CardException
- {
- byte[] secure_setup = new byte[] { (byte) 0x80, (byte) 0x01, algoID, // Algorithm setup
+ /*
+ * Setup Manage Security Environment (MSE) for cryptographic signatur !fixed
+ * key id
+ */
+ protected void exec_MSE(CardChannel channel, byte algoID)
+ throws CardException {
+ byte[] secure_setup = new byte[] { (byte) 0x80, (byte) 0x01, algoID, // Algorithm
+ // setup
(byte) 0x84, (byte) 0x01, (byte) 0x81 }; // Key setup
-
- ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x22,
- 0x41, 0xB6, secure_setup));
+
+ ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x22, 0x41,
+ 0xB6, secure_setup));
}
-
- protected byte[] exec_sign(CardChannel channel, byte[] hash) throws CardException
- {
- ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x2A,
- 0x9E, 0x9A, hash));
-
- return resp.getData();
+
+ /*
+ * Execute signature command
+ */
+ protected byte[] exec_sign(CardChannel channel, byte[] hash)
+ throws CardException, SignatureCardException {
+ ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x2A, 0x9E,
+ 0x9A, hash));
+
+ if (resp.getSW() == 0x6982) {
+ throw new SecurityStatusNotSatisfiedException();
+ }
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException(
+ "PSO - COMPUTE DIGITAL SIGNATRE failed: SW="
+ + Integer.toHexString(resp.getSW()));
+ } else {
+ return resp.getData();
+ }
}
+ /*
+ * Read current binary information
+ */
protected byte[] exec_readBinary(CardChannel channel) throws CardException,
IOException, SignatureCardException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@@ -428,8 +566,8 @@ public class CypriotEID extends AbstractSignatureCard implements
int offset = 0;
do {
- int offset_lo = offset % 0xFF;
- int offset_hi = offset / 0xFF;
+ int offset_lo = (byte) offset;
+ int offset_hi = (byte) (offset >> 8);
ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0xB0,
offset_hi, offset_lo, 0x00));
@@ -451,26 +589,8 @@ public class CypriotEID extends AbstractSignatureCard implements
} while (repeat);
byte[] buf = buffer.toByteArray();
-
- log.debug("BINARY READ: ");
-
- try {
- log.debug(getHexString(buf));
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
+
return buf;
}
-
-
- public static String getHexString(byte[] b) throws Exception {
- String result = "";
- for (int i=0; i < b.length; i++) {
- result += ":" +
- Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
- }
- return result;
- }
+
}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java b/smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java
index c06c3296..1f0f5c4c 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java
@@ -69,6 +69,7 @@ public class LogCardChannel extends CardChannel {
switch (command.getINS()) {
case 0x20: // VERIFY
case 0x21: // VERIFY
+ case 0x2C: // RESET RETRY COUNTER
case 0x24: { // CHANGE REFERENCE DATA
// Don't log possibly sensitive command data
StringBuilder sb = new StringBuilder();
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java
index 5a29e6ce..2d1ec970 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java
@@ -53,4 +53,6 @@ public interface ModifyPINProvider {
public char[] provideNewPIN(PinInfo pinInfo)
throws CancelledException, InterruptedException;
+ public char[] providePUK(PinInfo pinInfo, PinInfo pukInfo, int retries)
+ throws CancelledException, InterruptedException;
}