outerTlvs = outer.getList();
- if (outerTlvs.size() == 1 && outerTlvs.get(0).isTag(new BerTag(0x53))) {
- m_tlvBuf = outerTlvs.get(0).getBytesValue();
- outer = tlvp.parse(m_tlvBuf);
- }
- for (BerTlv tlv : outer.getList()) {
- byte[] tag = tlv.getTag().bytes;
- if (Arrays.equals(tag, TagConstants.KEYS_WITH_ON_CARD_CERTS_TAG)) {
- m_keysWithOnCardCerts = tlv.getIntValue();
- m_content.put(tlv.getTag(), tlv.getBytesValue());
- } else if (Arrays.equals(tag, TagConstants.KEYS_WITH_OFF_CARD_CERTS_TAG)) {
- m_keysWithOffCardCerts = tlv.getIntValue();
- m_content.put(tlv.getTag(), tlv.getBytesValue());
- } else if (Arrays.equals(tag, TagConstants.OFF_CARD_CERT_URL_TAG)) {
- m_offCardCertUrl = tlv.getBytesValue();
- m_content.put(tlv.getTag(), tlv.getBytesValue());
- } else if (!Arrays.equals(tag, TagConstants.ERROR_DETECTION_CODE_TAG) && tlv.getBytesValue().length != 0) {
- m_content.put(tlv.getTag(), tlv.getBytesValue());
- s_logger.warn("Unexpected tag: {} with value: {}", Hex.encodeHexString(tlv.getTag().bytes),
- Hex.encodeHexString(tlv.getBytesValue()));
- }
- s_logger.info("found tag: {}", Hex.encodeHexString(tag));
- }
-
- if (m_keysWithOnCardCerts == -1 || m_keysWithOffCardCerts == -1)
- return false;
-
- dump(this.getClass());
- return true;
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/MiddlewareStatus.java b/cardlib/src/main/java/gov/gsa/pivconformance/card/client/MiddlewareStatus.java
deleted file mode 100644
index fe2e6677..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/MiddlewareStatus.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package gov.gsa.pivconformance.card.client;
-
-/**
- *
- * Enumeration containing PIV Client Application Programming Interface return
- * codes
- *
- */
-public enum MiddlewareStatus {
- PIV_OK, PIV_CONNECTION_DESCRIPTION_MALFORMED, PIV_CONNECTION_FAILURE, PIV_CONNECTION_LOCKED,
- PIV_INVALID_CARD_HANDLE, PIV_CARD_READER_ERROR, PIV_INVALID_OID, PIV_DATA_OBJECT_NOT_FOUND,
- PIV_SECURITY_CONDITIONS_NOT_SATISFIED, PIV_SM_FAILED, PIV_INSUFFICIENT_BUFFER, PIV_CARD_APPLICATION_NOT_FOUND,
- PIV_AUTHENTICATION_FAILURE, PIV_AUTHENTICATOR_MALFORMED, PIV_UNSUPPORTED_CRYPTOGRAPHIC_MECHANISM,
- PIV_INVALID_KEY_OR_KEYALG_COMBINATION, PIV_FUNCTION_NOT_SUPPORTED, PIV_INSUFFICIENT_CARD_RESOURCE
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/OtherName.java b/cardlib/src/main/java/gov/gsa/pivconformance/card/client/OtherName.java
deleted file mode 100644
index 69ba2e0a..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/OtherName.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package gov.gsa.pivconformance.card.client;
-
-// Used from the bouncy castle git repo under the same license as bouncycastle itself.
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1Object;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.DERTaggedObject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The OtherName object.
- *
- *
- * OtherName ::= SEQUENCE {
- * type-id OBJECT IDENTIFIER,
- * value [0] EXPLICIT ANY DEFINED BY type-id }
- *
- */
-public class OtherName extends ASN1Object {
- private static final Logger s_logger = LoggerFactory.getLogger(OtherName.class);
-
- private final ASN1ObjectIdentifier typeID;
- private final ASN1Encodable value;
-
- /**
- * OtherName factory method.
- *
- * @param obj the object used to construct an instance of
- * OtherName
. It must be an instance of OtherName
- *
or ASN1Sequence
.
- * @return the instance of OtherName
built from the supplied
- * object.
- * @throws java.lang.IllegalArgumentException if the object passed to the
- * factory is not an instance of
- * OtherName
or
- * something that can be converted
- * into an appropriate
- * ASN1Sequence
.
- */
- public static OtherName getInstance(Object obj) {
-
- if (obj instanceof OtherName) {
- return (OtherName) obj;
- } else if (obj != null) {
- return new OtherName(ASN1Sequence.getInstance(obj));
- }
-
- return null;
- }
-
- /**
- * Base constructor.
- *
- * @param typeID the type of the other name.
- * @param value the ANY object that represents the value.
- */
- public OtherName(ASN1ObjectIdentifier typeID, ASN1Encodable value) {
- this.typeID = typeID;
- this.value = value;
- }
-
- private OtherName(ASN1Sequence seq) {
- this.typeID = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
- this.value = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getObject(); // explicitly tagged
- }
-
- public ASN1ObjectIdentifier getTypeID() {
- return typeID;
- }
-
- public ASN1Encodable getValue() {
- return value;
- }
-
- @Override
- public ASN1Primitive toASN1Primitive() {
- ASN1EncodableVector v = new ASN1EncodableVector();
-
- v.add(typeID);
- v.add(new DERTaggedObject(true, 0, value));
-
- return new DERSequence(v);
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVApplicationException.java b/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVApplicationException.java
deleted file mode 100644
index 9578cf63..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVApplicationException.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package gov.gsa.pivconformance.card.client;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A base class for exceptions thrown by PIV application methods
- */
-public class PIVApplicationException extends Exception {
- private static final Logger s_logger = LoggerFactory.getLogger(PIVApplicationException.class);
-
- /**
- *
- */
- private static final long serialVersionUID = 1L;
-
- /**
- *
- * Default constructor for PIVApplicationException class
- *
- */
- public PIVApplicationException() {
- super();
- }
-
- /**
- *
- * Constructor for PIVApplicationException class that takes a string with
- * exception message
- *
- * @param message String with the exception message
- */
- public PIVApplicationException(String message) {
- super(message);
- }
-
- /**
- *
- * Constructor for PIVApplicationException class that takes a string with
- * exception message and a Throwable cause
- *
- * @param message String with the exception message
- * @param cause Throwable cause
- */
- public PIVApplicationException(String message, Throwable cause) {
- super(message, cause);
- }
-
- /**
- *
- * Constructor for PIVApplicationException class that takes a Throwable cause
- *
- * @param cause Throwable cause
- */
- public PIVApplicationException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVAuthenticator.java b/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVAuthenticator.java
deleted file mode 100644
index e4323444..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVAuthenticator.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package gov.gsa.pivconformance.card.client;
-
-import gov.gsa.pivconformance.tlv.TagConstants;
-
-import java.util.Arrays;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A class that serves the function of the handle objects passed around that
- * encapsulate authenticator information
- */
-public class PIVAuthenticator {
- private static final Logger s_logger = LoggerFactory.getLogger(PIVAuthenticator.class);
-
- byte m_type;
- byte[] m_data;
-
- /**
- *
- * Constructor that initializes PIVAuthenticator object based on passed in
- * parameter
- *
- * @param type Authenticator type either an Application Pin or a Global PIN
- * @param data String object containing the pin information
- */
- public PIVAuthenticator(byte type, String data) {
- this(type, data.getBytes());
- }
-
- /**
- *
- * Constructor that initializes PIVAuthenticator object based on passed in
- * parameter
- *
- * @param type Authenticator type either an Application Pin or a Global PIN
- * @param data Byte array object containing the pin information
- */
- public PIVAuthenticator(byte type, byte[] data) {
- m_type = type;
- if (m_type == TagConstants.KEY_REFERENCE_APPLICATION_PIN_TAG
- || m_type == TagConstants.KEY_REFERENCE_GLOBAL_PIN_TAG) {
- if (data.length == 0) {
- m_data = new byte[0];
- } else {
- if (data.length > 8 || data.length < 6) {
- throw new IllegalArgumentException("PIN must be between 6 and 8 digits");
- }
- m_data = Arrays.copyOf(data, 8);
- Arrays.fill(m_data, data.length, m_data.length, (byte) 0xff);
- }
- }
- }
-
- /**
- *
- * Get the authenticator type
- *
- * @return Byte identifying authenticator type
- */
- public byte getType() {
- return m_type;
- }
-
- /**
- *
- * Set the authenticator type
- *
- * @param type byte containing authenticator type
- */
- public void setType(byte type) {
- m_type = type;
- }
-
- /**
- *
- * Get the pin information
- *
- * @return Byte array containing pin information
- */
- public byte[] getData() {
- return m_data;
- }
-
- /**
- *
- * Set the pin
- *
- * @param data byte array containing pin information
- */
- public void setData(byte[] data) {
- m_data = data;
- }
-
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVAuthenticators.java b/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVAuthenticators.java
deleted file mode 100644
index 86824c5d..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVAuthenticators.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package gov.gsa.pivconformance.card.client;
-
-import gov.gsa.pivconformance.tlv.*;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A class that serves the function of the handle to a list of authenticator
- * objects in SP800-73
- */
-public class PIVAuthenticators {
- private static final Logger s_logger = LoggerFactory.getLogger(PIVAuthenticators.class);
-
- private ArrayList m_authenticators = new ArrayList();
-
- /**
- *
- * Get the list of authenticators
- *
- * @return List of PIVAuthenticator objects
- */
- public List getAuthenticators() {
- return m_authenticators;
- }
-
- /**
- *
- * Add a global pin authenticator object
- *
- * @param pin String containing pin value
- */
- public void addGlobalPin(String pin) {
- PIVAuthenticator a = new PIVAuthenticator(TagConstants.KEY_REFERENCE_GLOBAL_PIN_TAG, pin);
- m_authenticators.add(a);
- }
-
- /**
- *
- * Add an application pin authenticator object
- *
- * @param pin String containing pin value
- */
- public void addApplicationPin(String pin) {
- PIVAuthenticator a = new PIVAuthenticator(TagConstants.KEY_REFERENCE_APPLICATION_PIN_TAG, pin);
- m_authenticators.add(a);
- }
-
- /**
- *
- * Returns a byte array representation of a list of authenticator objects
- *
- * @return Byte array containing authenticator list
- */
- public byte[] getBytes() {
- byte[] rv = {};
- if (m_authenticators.size() == 0)
- return rv;
- BerTlvBuilder b = new BerTlvBuilder();
- for (PIVAuthenticator authenticator : m_authenticators) {
- b.addBytes(new BerTag(TagConstants.REFERENCE_DATA_TAG), authenticator.getData());
- b.addByte(new BerTag(TagConstants.KEY_REFERENCE_TAG), authenticator.getType());
- }
- rv = b.buildArray();
- // s_logger.debug("Encoded authenticators: {}", Hex.encodeHexString(rv));
- return rv;
- }
-
- /**
- *
- * Helper function that decodes byte array containing authenticator list and
- * populates various class fields.
- *
- * @param authenticators Byte array containing authenticator list
- */
- public boolean decode(byte[] authenticators) {
- m_authenticators.clear();
- if (authenticators.length == 0)
- return true;
- BerTlvParser p = new BerTlvParser(new CCTTlvLogger(this.getClass()));
- BerTlvs tlvs = p.parse(authenticators);
- byte[] refData = null;
- byte refId = 0x00;
- for (BerTlv t : tlvs.getList()) {
- switch (t.getTag().bytes[0]) {
- case (byte) 0x81: {
- refData = t.getBytesValue();
- break;
- }
- case (byte) 0x83: {
- if (refData == null) {
- throw new IllegalStateException(
- "Unexpected 0x83 tag without having seen 0x81 tag while parsing authenticator");
- }
- refId = t.getBytesValue()[0];
- PIVAuthenticator parsed = new PIVAuthenticator(refId, refData);
- m_authenticators.add(parsed);
- refData = null;
- refId = 0x00;
- break;
- }
- default:
- throw new IllegalStateException("Unexpected tag in authenticator");
- }
- }
- return true;
- }
-
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVDataObjectFactory.java b/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVDataObjectFactory.java
deleted file mode 100644
index f966299a..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVDataObjectFactory.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package gov.gsa.pivconformance.card.client;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class PIVDataObjectFactory {
- // slf4j will thunk this through to an appropriately configured logging library
- private static final Logger s_logger = LoggerFactory.getLogger(PIVDataObjectFactory.class);
-
- /**
- * Instantiate an appropriate PIVDataObject class given an OID, or a generic one
- * in the absence of an OID
- *
- * @param OID
- * @return
- */
- public static PIVDataObject createDataObjectForOid(String OID) {
- PIVDataObject rv = null;
-
- if (OID.equals(APDUConstants.CARD_CAPABILITY_CONTAINER_OID))
- rv = new CardCapabilityContainer();
- else if (OID.equals(APDUConstants.CARD_HOLDER_UNIQUE_IDENTIFIER_OID))
- rv = new CardHolderUniqueIdentifier();
- else if (OID.equals(APDUConstants.SECURITY_OBJECT_OID))
- rv = new SecurityObject();
- else if (OID.equals(APDUConstants.CARDHOLDER_FACIAL_IMAGE_OID))
- rv = new CardHolderBiometricData();
- else if (OID.equals(APDUConstants.CARDHOLDER_FINGERPRINTS_OID))
- rv = new CardHolderBiometricData();
- else if (OID.equals(APDUConstants.X509_CERTIFICATE_FOR_CARD_AUTHENTICATION_OID))
- rv = new X509CertificateDataObject();
- else if (OID.equals(APDUConstants.X509_CERTIFICATE_FOR_PIV_AUTHENTICATION_OID))
- rv = new X509CertificateDataObject();
- else if (OID.equals(APDUConstants.DISCOVERY_OBJECT_OID))
- rv = new DiscoveryObject();
- else if (OID.equals(APDUConstants.KEY_HISTORY_OBJECT_OID))
- rv = new KeyHistoryObject();
- else if (OID.equals(APDUConstants.BIOMETRIC_INFORMATION_TEMPLATES_GROUP_TEMPLATE_OID))
- rv = new BiometricInformationTemplatesGroupTemplate();
- else if (OID.equals(APDUConstants.CARDHOLDER_IRIS_IMAGES_OID))
- rv = new CardHolderBiometricData();
- else if (OID.equals(APDUConstants.PAIRING_CODE_REFERENCE_DATA_CONTAINER_OID))
- rv = new PairingCodeReferenceDataContainer();
- else if (OID.equals(APDUConstants.SECURE_MESSAGING_CERTIFICATE_SIGNER_OID))
- rv = new SecureMessagingCertificateSigner();
- else if (OID.equals(APDUConstants.X509_CERTIFICATE_FOR_DIGITAL_SIGNATURE_OID))
- rv = new X509CertificateDataObject();
- else if (OID.equals(APDUConstants.X509_CERTIFICATE_FOR_KEY_MANAGEMENT_OID))
- rv = new X509CertificateDataObject();
- else if (OID.equals(APDUConstants.PRINTED_INFORMATION_OID))
- rv = new PrintedInformation();
-
- if (rv == null) {
- s_logger.warn("Unrecognized data object type: {}. Using generic.", OID);
- rv = new PIVDataObject();
- return rv;
- }
- rv.setOID(OID);
- return rv;
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVMiddleware.java b/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVMiddleware.java
deleted file mode 100644
index e99aaf09..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVMiddleware.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package gov.gsa.pivconformance.card.client;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import gov.gsa.pivconformance.utils.PCSCWrapper;
-
-import javax.smartcardio.CardTerminal;
-import javax.smartcardio.Card;
-
-public class PIVMiddleware {
- // slf4j will thunk this through to an appropriately configured logging library
- private static final Logger s_logger = LoggerFactory.getLogger(PIVMiddleware.class);
- public static final String PIV_MIDDLEWARE_VERSION = "800-73-4 Client API";
-
- /**
- * pivMiddlewareVersion from section 3.1.1 of SP 800-73-4
- *
- * @param version
- * @return PIV_OK if version was successfully retrieved
- */
- public static MiddlewareStatus pivMiddlewareVersion(PIVMiddlewareVersion version) {
- version.setVersion(PIV_MIDDLEWARE_VERSION);
- return MiddlewareStatus.PIV_OK;
- }
-
- /**
- * pivConnect from section 3.1.2 of SP 800-73-4
- *
- * @param sharedConnection
- * @param connectionDescription
- * @param cardHandle
- * @return PIV_OK if connection was successful,
- * PIV_CONNECTION_DESCRIPTION_MALFORMED if an invalid
- * ConnectionDescription object was passed in, PIV_CONNECTION_FAILURE if
- * no connection could be established, or PIV_CONNECTION_LOCKED if an
- * exclusive connection was requested but another exclusive connection
- * to the card in the specified reader is already in progress.
- */
- public static MiddlewareStatus pivConnect(boolean sharedConnection, ConnectionDescription connectionDescription,
- CardHandle cardHandle) {
-
- // Need to figure out what to do with sharedConnection in context of JAVA
- CardTerminal t = connectionDescription.getTerminal();
- if (cardHandle == null)
- cardHandle = new CardHandle();
-
- if (connectionDescription.getTerminal() == null)
- return MiddlewareStatus.PIV_CONNECTION_DESCRIPTION_MALFORMED;
-
- try {
-
- PCSCWrapper pcsc = PCSCWrapper.getInstance();
-
- Card card = pcsc.connect(t);
-
- if (card != null) {
- cardHandle.setConnectionDescription(connectionDescription);
- cardHandle.setCard(card);
- cardHandle.setValid(true);
- cardHandle.setCurrentChannel(card.getBasicChannel());
- }
-
- } catch (Exception ex) {
- s_logger.error("Unable to establish connection to the card : ", ex.getMessage(), ex);
- return MiddlewareStatus.PIV_CONNECTION_FAILURE;
- }
-
- return MiddlewareStatus.PIV_OK;
- }
-
- /**
- * pivDisconnect from section 3.1.2 of SP 800-73-4
- *
- * @param cardHandle
- * @return PIV_OK if the connection was disconnected and the CardHandle
- * invalidated, PIV_INVALID_CARD_HANDLE if cardHandle was invalid,
- * PIV_CARD_READER_ERROR if the connection could not be destroyed due to
- * a reader failure.
- */
- public static MiddlewareStatus pivDisconnect(CardHandle cardHandle) {
-
- try {
-
- Card card = cardHandle.getCard();
-
- if (card == null || !cardHandle.isValid()) {
- return MiddlewareStatus.PIV_INVALID_CARD_HANDLE;
- }
-
- // XXX Need to figure out if connections needs to be reset or not
- card.disconnect(false);
- // Invalidate cardHandle object
- cardHandle = new CardHandle();
-
- } catch (Exception ex) {
-
- s_logger.error("Unable to disconnect from the card : ", ex.getMessage());
- return MiddlewareStatus.PIV_CARD_READER_ERROR;
- }
-
- return MiddlewareStatus.PIV_OK;
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVMiddlewareVersion.java b/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVMiddlewareVersion.java
deleted file mode 100644
index 5c6c7f7c..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVMiddlewareVersion.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package gov.gsa.pivconformance.card.client;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Class for atoring PIV middleware version information.
- */
-public class PIVMiddlewareVersion {
- private static final Logger s_logger = LoggerFactory.getLogger(PIVMiddlewareVersion.class);
-
- /**
- *
- * Default constructor that creates an invalid PIVMiddlewareVersion object
- *
- */
- public PIVMiddlewareVersion() {
- version = "NOT SET";
- }
-
- /**
- *
- * Returns a String with PIV middleware version info
- *
- * @return
- */
- public String getVersion() {
- return version;
- }
-
- /**
- *
- * Sets the PIV middleware version info
- *
- * @param version String with PIV middleware version info
- */
- public void setVersion(String version) {
- this.version = version;
- }
-
- /**
- *
- * Returns a String with PIV middleware version info
- *
- * @return
- */
- @Override
- public String toString() {
- return version;
- }
-
- private String version;
-
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PairingCodeReferenceDataContainer.java b/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PairingCodeReferenceDataContainer.java
deleted file mode 100644
index 73befcc3..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PairingCodeReferenceDataContainer.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package gov.gsa.pivconformance.card.client;
-
-import gov.gsa.pivconformance.tlv.*;
-import org.apache.commons.codec.binary.Hex;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- *
- * Encapsulates a Pairing Code Reference Data Container data object as defined
- * by SP800-73-4 Part 2 Appendix A Table 43
- *
- */
-public class PairingCodeReferenceDataContainer extends PIVDataObject {
- // slf4j will thunk this through to an appropriately configured logging library
- private static final Logger s_logger = LoggerFactory.getLogger(PairingCodeReferenceDataContainer.class);
-
- private String m_pairingCode;
- private boolean m_errorDetectionCode;
-
- /**
- * PairingCodeReferenceDataContainer class constructor, initializes all the
- * class fields.
- */
- public PairingCodeReferenceDataContainer() {
- m_pairingCode = "";
- m_errorDetectionCode = false;
- m_content = new HashMap();
- }
-
- /**
- *
- * Returns a String with pairing code name
- *
- * @return String containing pairing code name
- */
- public String getName() {
- return m_pairingCode;
- }
-
- /**
- *
- * Sets the pairing code name
- *
- * @param pairingCode String containing pairing code name
- */
- public void setName(String pairingCode) {
- m_pairingCode = pairingCode;
- }
-
- /**
- *
- * Returns True if error Error Detection Code is present, false otherwise
- *
- * @return True if error Error Detection Code is present, false otherwise
- */
- @Override
- public boolean getErrorDetectionCode() {
- return m_errorDetectionCode;
- }
-
- /**
- *
- * Sets if error Error Detection Code is present
- *
- * @param errorDetectionCode True if error Error Detection Code is present,
- * false otherwise
- */
- @Override
- public void setErrorDetectionCode(boolean errorDetectionCode) {
- m_errorDetectionCode = errorDetectionCode;
- }
-
- /**
- *
- * Decode function that decodes Pairing Code Reference Data Container object
- * retrieved from the card and populates various class fields.
- *
- * @return True if decode was successful, false otherwise
- */
- @Override
- public boolean decode() {
-
- try {
- byte[] rawBytes = this.getBytes();
-
- if (rawBytes == null) {
- s_logger.error("No buffer to decode for {}.", APDUConstants.oidNameMap.get(super.getOID()));
- return false;
- }
-
- BerTlvParser tlvp = new BerTlvParser(new CCTTlvLogger(this.getClass()));
- BerTlvs outer = tlvp.parse(rawBytes);
-
- if (outer == null) {
- s_logger.error("Error parsing {}, unable to parse TLV value.",
- APDUConstants.oidNameMap.get(super.getOID()));
- return false;
- }
-
- List values = outer.getList();
- for (BerTlv tlv : values) {
- if (tlv.isPrimitive()) {
- s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv.getTag().bytes),
- Hex.encodeHexString(tlv.getBytesValue()));
-
- BerTlvs outer2 = tlvp.parse(tlv.getBytesValue());
-
- if (outer2 == null) {
- s_logger.error("Error parsing {}, unable to parse TLV value.",
- APDUConstants.oidNameMap.get(super.getOID()));
- return false;
- }
-
- List values2 = outer2.getList();
- for (BerTlv tlv2 : values2) {
- if (tlv2.isPrimitive()) {
- if (Arrays.equals(tlv2.getTag().bytes, TagConstants.PAIRING_CODE_TAG)) {
-
- m_pairingCode = new String(tlv2.getBytesValue());
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
-
- } else {
- s_logger.warn("Unexpected tag: {} with value: {}",
- Hex.encodeHexString(tlv2.getTag().bytes),
- Hex.encodeHexString(tlv2.getBytesValue()));
- }
- } else {
- if (Arrays.equals(tlv2.getTag().bytes, TagConstants.ERROR_DETECTION_CODE_TAG)) {
-
- m_errorDetectionCode = true;
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
-
- } else {
- s_logger.warn("Unexpected tag: {} with value: {}",
- Hex.encodeHexString(tlv2.getTag().bytes),
- Hex.encodeHexString(tlv2.getBytesValue()));
- }
- }
- }
- }
- }
- } catch (Exception ex) {
-
- s_logger.error("Error parsing {}: {}", APDUConstants.oidNameMap.get(super.getOID()), ex.getMessage());
- return false;
- }
-
- if (m_pairingCode == "")
- return false;
-
- dump(this.getClass());
- return true;
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PrintedInformation.java b/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PrintedInformation.java
deleted file mode 100644
index a8f46cd7..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PrintedInformation.java
+++ /dev/null
@@ -1,373 +0,0 @@
-package gov.gsa.pivconformance.card.client;
-
-import gov.gsa.pivconformance.tlv.*;
-import org.apache.commons.codec.binary.Hex;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.ByteArrayOutputStream;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- *
- * Encapsulates a Printed Information data object as defined by SP800-73-4 Part
- * 2 Appendix A Table 9
- *
- */
-public class PrintedInformation extends PIVDataObject {
- // slf4j will thunk this through to an appropriately configured logging library
- private static final Logger s_logger = LoggerFactory.getLogger(PrintedInformation.class);
-
- private String m_name;
- private String m_employeeAffiliation;
- private String m_expirationDate;
- private String m_agencyCardSerialNumber;
- private String m_issuerIdentification;
- private String m_organizationAffiliation1;
- private String m_organizationAffiliation2;
- private boolean m_errorDetectionCode;
- private byte[] m_signedContent;
-
- /**
- * PrintedInformation class constructor, initializes all the class fields.
- */
- public PrintedInformation() {
- m_name = "";
- m_employeeAffiliation = "";
- m_expirationDate = "";
- m_agencyCardSerialNumber = "";
- m_issuerIdentification = "";
- m_organizationAffiliation1 = "";
- m_organizationAffiliation2 = "";
- m_errorDetectionCode = false;
- m_signedContent = null;
- m_content = new HashMap();
- }
-
- /**
- *
- * Returns byte array with signed content
- *
- * @return Byte array with signed content buffer
- */
- public byte[] getSignedContent() {
- return m_signedContent;
- }
-
- /**
- *
- * Sets the signed content value
- *
- * @param signedContent Byte array with signed content buffer
- */
- public void setSignedContent(byte[] signedContent) {
- m_signedContent = signedContent;
- }
-
- /**
- *
- * Returns the Name value as String
- *
- * @return String with the Name value
- */
- public String getName() {
- return m_name;
- }
-
- /**
- *
- * Sets the name value
- *
- * @param name String with the Name value
- */
- public void setName(String name) {
- m_name = name;
- }
-
- /**
- *
- * Returns the Employee Affiliation value as String
- *
- * @return String with the Employee Affiliation value
- */
- public String getEmployeeAffiliation() {
- return m_employeeAffiliation;
- }
-
- /**
- *
- * Sets the Employee Affiliation value
- *
- * @param employeeAffiliation String with the Employee Affiliation value
- */
- public void setEmployeeAffiliation(String employeeAffiliation) {
- m_employeeAffiliation = employeeAffiliation;
- }
-
- /**
- *
- * Returns the Expiration Date value as String
- *
- * @return String with the Expiration Date value
- */
- public String getExpirationDate() {
- return m_expirationDate;
- }
-
- /**
- *
- * Sets the Expiration Date value
- *
- * @param expirationDate String with the Expiration Date value
- */
- public void setExpirationDate(String expirationDate) {
- m_expirationDate = expirationDate;
- }
-
- /**
- *
- * Returns the Agency Card Serial Number value as String
- *
- * @return String with the Agency Card Serial Number value
- */
- public String getAgencyCardSerialNumber() {
- return m_agencyCardSerialNumber;
- }
-
- /**
- *
- * Sets the Agency Card Serial Number value
- *
- * @param agencyCardSerialNumber String with the Agency Card Serial Number value
- */
- public void setAgencyCardSerialNumber(String agencyCardSerialNumber) {
- m_agencyCardSerialNumber = agencyCardSerialNumber;
- }
-
- /**
- *
- * Returns the Issuer Identification value as String
- *
- * @return String with the Issuer Identification value
- */
- public String getIssuerIdentification() {
- return m_issuerIdentification;
- }
-
- /**
- *
- * Sets the Issuer Identification value
- *
- * @param issuerIdentification String with the Issuer Identification value
- */
- public void setIssuerIdentification(String issuerIdentification) {
- m_issuerIdentification = issuerIdentification;
- }
-
- /**
- *
- * Returns the Organization Affiliation1 value as String
- *
- * @return String with the Organization Affiliation1 value
- */
- public String getOrganizationAffiliation1() {
- return m_organizationAffiliation1;
- }
-
- /**
- *
- * Sets the Organization Affiliation1 value
- *
- * @param organizationAffiliation1 String with the Organization Affiliation1
- * value
- */
- public void setOrganizationAffiliation1(String organizationAffiliation1) {
- m_organizationAffiliation1 = organizationAffiliation1;
- }
-
- /**
- *
- * Returns the Organization Affiliation2 value as String
- *
- * @return String with the Organization Affiliation2 value
- */
- public String getOrganizationAffiliation2() {
- return m_organizationAffiliation2;
- }
-
- /**
- *
- * Sets the Organization Affiliation2 value
- *
- * @param organizationAffiliation2 String with the Organization Affiliation2
- * value
- */
- public void setOrganizationAffiliation2(String organizationAffiliation2) {
- m_organizationAffiliation2 = organizationAffiliation2;
- }
-
- /**
- *
- * Returns True if error Error Detection Code is present, false otherwise
- *
- * @return True if error Error Detection Code is present, false otherwise
- */
- @Override
- public boolean getErrorDetectionCode() {
- return m_errorDetectionCode;
- }
-
- /**
- *
- * Sets if error Error Detection Code is present
- *
- * @param errorDetectionCode True if error Error Detection Code is present,
- * false otherwise
- */
- @Override
- public void setErrorDetectionCode(boolean errorDetectionCode) {
- m_errorDetectionCode = errorDetectionCode;
- }
-
- /**
- *
- * Decode function that decodes Printed Information object retrieved from the
- * card and populates various class fields.
- *
- * @return True if decode was successful, false otherwise
- */
- @Override
- public boolean decode() {
-
- try {
- byte[] rawBytes = this.getBytes();
-
- if (rawBytes == null) {
- s_logger.error("No buffer to decode for {}.", APDUConstants.oidNameMap.get(super.getOID()));
- return false;
- }
-
- BerTlvParser tlvp = new BerTlvParser(new CCTTlvLogger(this.getClass()));
- BerTlvs outer = tlvp.parse(rawBytes);
-
- if (outer == null) {
- s_logger.error("Error parsing {}, unable to parse TLV value.",
- APDUConstants.oidNameMap.get(super.getOID()));
- return false;
- }
-
- List values = outer.getList();
- for (BerTlv tlv : values) {
- if (tlv.isPrimitive()) {
- s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv.getTag().bytes),
- Hex.encodeHexString(tlv.getBytesValue()));
-
- BerTlvs outer2 = tlvp.parse(tlv.getBytesValue());
-
- if (outer2 == null) {
- s_logger.error("Error parsing {}, unable to parse TLV value.",
- APDUConstants.oidNameMap.get(super.getOID()));
- return false;
- }
-
- ByteArrayOutputStream scos = new ByteArrayOutputStream();
-
- List values2 = outer2.getList();
- for (BerTlv tlv2 : values2) {
- if (tlv2.isPrimitive()) {
-
- super.m_tagList.add(tlv2.getTag());
- if (Arrays.equals(tlv2.getTag().bytes, TagConstants.NAME_TAG)) {
-
- m_name = new String(tlv2.getBytesValue());
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- scos.write(APDUUtils.getTLV(TagConstants.NAME_TAG, tlv2.getBytesValue()));
-
- } else if (Arrays.equals(tlv2.getTag().bytes, TagConstants.EMPLOYEE_AFFILIATION_TAG)) {
-
- m_employeeAffiliation = new String(tlv2.getBytesValue());
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- scos.write(
- APDUUtils.getTLV(TagConstants.EMPLOYEE_AFFILIATION_TAG, tlv2.getBytesValue()));
-
- } else if (Arrays.equals(tlv2.getTag().bytes,
- TagConstants.PRINTED_INFORMATION_EXPIRATION_DATE_TAG)) {
-
- m_expirationDate = new String(tlv2.getBytesValue());
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- scos.write(APDUUtils.getTLV(TagConstants.PRINTED_INFORMATION_EXPIRATION_DATE_TAG,
- tlv2.getBytesValue()));
-
- } else if (Arrays.equals(tlv2.getTag().bytes, TagConstants.AGENCY_CARD_SERIAL_NUMBER_TAG)) {
-
- m_agencyCardSerialNumber = new String(tlv2.getBytesValue());
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- scos.write(APDUUtils.getTLV(TagConstants.AGENCY_CARD_SERIAL_NUMBER_TAG,
- tlv2.getBytesValue()));
-
- } else if (Arrays.equals(tlv2.getTag().bytes, TagConstants.ISSUER_IDENTIFICATION_TAG)) {
-
- m_issuerIdentification = new String(tlv2.getBytesValue());
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- scos.write(
- APDUUtils.getTLV(TagConstants.ISSUER_IDENTIFICATION_TAG, tlv2.getBytesValue()));
-
- } else if (Arrays.equals(tlv2.getTag().bytes,
- TagConstants.ORGANIZATIONAL_AFFILIATION_L1_TAG)) {
-
- m_organizationAffiliation1 = new String(tlv2.getBytesValue());
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- scos.write(APDUUtils.getTLV(TagConstants.ORGANIZATIONAL_AFFILIATION_L1_TAG,
- tlv2.getBytesValue()));
-
- } else if (Arrays.equals(tlv2.getTag().bytes,
- TagConstants.ORGANIZATIONAL_AFFILIATION_L2_TAG)) {
-
- m_organizationAffiliation2 = new String(tlv2.getBytesValue());
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- scos.write(APDUUtils.getTLV(TagConstants.ORGANIZATIONAL_AFFILIATION_L2_TAG,
- tlv2.getBytesValue()));
-
- } else {
- s_logger.warn("Unexpected tag: {} with value: {}",
- Hex.encodeHexString(tlv2.getTag().bytes),
- Hex.encodeHexString(tlv2.getBytesValue()));
- }
- } else {
- super.m_tagList.add(tlv2.getTag());
- if (Arrays.equals(tlv2.getTag().bytes, TagConstants.ERROR_DETECTION_CODE_TAG)) {
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- m_errorDetectionCode = true;
-
- scos.write(TagConstants.ERROR_DETECTION_CODE_TAG);
- scos.write((byte) 0x00);
-
- } else {
- s_logger.warn("Unexpected tag: {} with value: {}",
- Hex.encodeHexString(tlv2.getTag().bytes),
- Hex.encodeHexString(tlv2.getBytesValue()));
- }
- }
- }
-
- m_signedContent = scos.toByteArray();
- }
- }
- } catch (Exception ex) {
-
- s_logger.error("Error parsing {}: {}", APDUConstants.oidNameMap.get(super.getOID()), ex.getMessage());
- return false;
- }
-
- if (m_name == "" || m_employeeAffiliation == "" || m_expirationDate == "" || m_agencyCardSerialNumber == ""
- || m_issuerIdentification == "")
- return false;
-
- super.setRequiresPin(true);
-
- dump(this.getClass());
- return true;
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/SecureMessagingCertificateSigner.java b/cardlib/src/main/java/gov/gsa/pivconformance/card/client/SecureMessagingCertificateSigner.java
deleted file mode 100644
index b075756a..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/SecureMessagingCertificateSigner.java
+++ /dev/null
@@ -1,185 +0,0 @@
-package gov.gsa.pivconformance.card.client;
-
-import gov.gsa.pivconformance.tlv.*;
-import org.apache.commons.codec.binary.Hex;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.zip.GZIPInputStream;
-
-/**
- *
- * Encapsulates a Card Holder Unique Identifier data object as defined by
- * SP800-73-4 Part 2 Appendix A Table 42
- *
- */
-public class SecureMessagingCertificateSigner extends PIVDataObject { // slf4j will thunk this through to an
- // appropriately configured logging library
- private static final Logger s_logger = LoggerFactory.getLogger(SecureMessagingCertificateSigner.class);
-
- private X509Certificate m_pivAuthCert;
- private byte[] m_intermediateCVC;
- private boolean m_error_Detection_Code;
-
- /**
- * SecureMessagingCertificateSigner class constructor, initializes all the class
- * fields.
- */
- public SecureMessagingCertificateSigner() {
-
- m_pivAuthCert = null;
- m_intermediateCVC = null;
- m_error_Detection_Code = false;
- m_content = new HashMap();
- }
-
- /**
- *
- * Returns True if error Error Detection Code is present, false otherwise
- *
- * @return True if error Error Detection Code is present, false otherwise
- */
- @Override
- public boolean getErrorDetectionCode() {
-
- return m_error_Detection_Code;
- }
-
- /**
- *
- * Returns X509Certificate object containing X.509 Certificate for Content
- * Signing
- *
- * @return X509Certificate object containing X.509 Certificate for Content
- * Signing
- */
- public X509Certificate getCertificate() {
- return m_pivAuthCert;
- }
-
- /**
- *
- * Returns byte array with Intermediate CVC value
- *
- * @return Byte array containing Intermediate CVC value
- */
- public byte[] getIntermediateCVC() {
- return m_intermediateCVC;
- }
-
- /**
- *
- * Sets the Intermediate CVC value
- *
- * @param intermediateCVC Byte array containing Intermediate CVC value
- */
- public void setIntermediateCVC(byte[] intermediateCVC) {
- m_intermediateCVC = intermediateCVC;
- }
-
- /**
- *
- * Decode function that decodes Secure Messaging Certificate Signer object
- * retrieved from the card and populates various class fields.
- *
- * @return True if decode was successful, false otherwise
- */
- @Override
- public boolean decode() {
-
- if (m_pivAuthCert == null) {
-
- try {
- byte[] raw = super.getBytes();
-
- BerTlvParser tp = new BerTlvParser(new CCTTlvLogger(this.getClass()));
- BerTlvs outer = tp.parse(raw);
-
- if (outer == null) {
- s_logger.error("Error parsing {}, unable to parse TLV value.",
- APDUConstants.oidNameMap.get(super.getOID()));
- return false;
- }
-
- List values = outer.getList();
- for (BerTlv tlv : values) {
- if (tlv.isPrimitive()) {
- s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv.getTag().bytes),
- Hex.encodeHexString(tlv.getBytesValue()));
-
- BerTlvs outer2 = tp.parse(tlv.getBytesValue());
-
- if (outer2 == null) {
- s_logger.error("Error parsing {}, unable to parse TLV value.",
- APDUConstants.oidNameMap.get(super.getOID()));
- return false;
- }
-
- List values2 = outer2.getList();
- byte[] rawCertBuf = null;
- byte[] certInfoBuf = null;
- for (BerTlv tlv2 : values2) {
- if (tlv2.isPrimitive()) {
- s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv2.getTag().bytes),
- Hex.encodeHexString(tlv2.getBytesValue()));
- } else {
- if (Arrays.equals(tlv2.getTag().bytes, TagConstants.CERTIFICATE_TAG)) {
- if (tlv2.hasRawValue()) {
- rawCertBuf = tlv2.getBytesValue();
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- }
- }
- if (Arrays.equals(tlv2.getTag().bytes, TagConstants.ERROR_DETECTION_CODE_TAG)) {
- if (tlv2.hasRawValue()) {
- m_error_Detection_Code = true;
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- }
- }
- if (Arrays.equals(tlv2.getTag().bytes, TagConstants.CERTINFO_TAG)) {
- certInfoBuf = tlv2.getBytesValue();
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- }
-
- if (Arrays.equals(tlv2.getTag().bytes, TagConstants.INTERMEDIATE_CVC_TAG)) {
- m_intermediateCVC = tlv2.getBytesValue();
- m_content.put(tlv2.getTag(), tlv2.getBytesValue());
- }
- }
- }
-
- InputStream certIS = null;
- // Check if the certificate buffer is compressed
- if (certInfoBuf != null && Arrays.equals(certInfoBuf, TagConstants.COMPRESSED_TAG)) {
- certIS = new GZIPInputStream(new ByteArrayInputStream(rawCertBuf));
- } else {
- certIS = new ByteArrayInputStream(rawCertBuf);
- }
-
- CertificateFactory cf = CertificateFactory.getInstance("X509");
- m_pivAuthCert = (X509Certificate) cf.generateCertificate(certIS);
- s_logger.info(m_pivAuthCert.getSubjectDN().toString());
- } else {
- s_logger.info("Object: {}", Hex.encodeHexString(tlv.getTag().bytes));
- }
- }
- } catch (Exception ex) {
-
- s_logger.error("Error parsing {}: {}", APDUConstants.oidNameMap.get(super.getOID()), ex.getMessage());
- return false;
- }
-
- if (m_pivAuthCert == null)
- return false;
- }
-
- dump(this.getClass());
- return true;
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/APDUConstants.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/APDUConstants.java
similarity index 98%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/APDUConstants.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/APDUConstants.java
index ee4c5d87..3f9e4b10 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/APDUConstants.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/APDUConstants.java
@@ -1,4 +1,4 @@
-package gov.gsa.pivconformance.card.client;
+package gov.gsa.pivconformance.cardlib.card.client;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -13,7 +13,7 @@
import java.lang.reflect.Field;
/**
- * Helper class that containes helper variables and functions for APDU
+ * Helper class that contains helper variables and functions for APDU
* generation
*
*/
@@ -308,7 +308,6 @@ public class APDUConstants {
put("2.16.840.1.101.3.7.2.16.22", "BIOMETRIC_INFORMATION_TEMPLATES_GROUP_TEMPLATE_OID");
put("2.16.840.1.101.3.7.2.16.23", "SECURE_MESSAGING_CERTIFICATE_SIGNER_OID");
put("2.16.840.1.101.3.7.2.16.24", "PAIRING_CODE_REFERENCE_DATA_CONTAINER_OID");
- put("2.16.840.1.101.3.7.2.16.23", "SECURE_MESSAGING_CERTIFICATE_SIGNER_OID");
}
};
@@ -361,9 +360,15 @@ public class APDUConstants {
* @return Array of String values containing OIDs for all mandatory containers
*/
public static final String[] MandatoryContainers() {
- final String[] rv = { CARD_CAPABILITY_CONTAINER_OID, CARD_HOLDER_UNIQUE_IDENTIFIER_OID,
- X509_CERTIFICATE_FOR_PIV_AUTHENTICATION_OID, CARDHOLDER_FINGERPRINTS_OID, SECURITY_OBJECT_OID,
- CARDHOLDER_FACIAL_IMAGE_OID, X509_CERTIFICATE_FOR_CARD_AUTHENTICATION_OID };
+ final String[] rv = {
+ CARD_CAPABILITY_CONTAINER_OID,
+ CARD_HOLDER_UNIQUE_IDENTIFIER_OID,
+ X509_CERTIFICATE_FOR_PIV_AUTHENTICATION_OID,
+ CARDHOLDER_FINGERPRINTS_OID,
+ SECURITY_OBJECT_OID,
+ CARDHOLDER_FACIAL_IMAGE_OID,
+ X509_CERTIFICATE_FOR_CARD_AUTHENTICATION_OID
+ };
return rv;
}
@@ -380,8 +385,12 @@ public static boolean isContainerMandatory(String oid) {
* by a PIN
*/
public static final String[] ProtectedContainers() {
- final String[] rv = { CARDHOLDER_FINGERPRINTS_OID, PRINTED_INFORMATION_OID, CARDHOLDER_FACIAL_IMAGE_OID,
- CARDHOLDER_IRIS_IMAGES_OID };
+ final String[] rv = {
+ CARDHOLDER_FINGERPRINTS_OID,
+ PRINTED_INFORMATION_OID,
+ CARDHOLDER_FACIAL_IMAGE_OID,
+ CARDHOLDER_IRIS_IMAGES_OID
+ };
return rv;
}
@@ -767,11 +776,6 @@ public static final String getKeyManagmentCertOID(int number) {
return oid;
}
-
- public static final String getFileNameForOid(String oid) {
- String rv = oidNameMap.get(oid).replaceAll(" ", "_");
- return rv;
- }
public static final String getStringForFieldNamed(String fieldName) {
String rv = null;
@@ -836,5 +840,15 @@ public static final byte[] getKeyManagmentCertID(int number) {
return arr;
}
-
+
+ /**
+ * Helper function to generate a file name from an OID
+ * @param oid the container OID
+ * @return a file name consisting of the container name appended with ".dat"
+ */
+
+ public static final String getFileNameForOid(String oid) {
+ String rv = oidNameMap.get(oid).replaceAll(" ", "_");
+ return rv;
+ }
}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/APDUUtils.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/APDUUtils.java
new file mode 100644
index 00000000..835fc45f
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/APDUUtils.java
@@ -0,0 +1,338 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.TagConstants;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Arrays;
+
+/**
+ *
+ * Helper class that facilitates creation of APDU values
+ *
+ */
+public class APDUUtils {
+ private static byte[] s_pivSelect = null;
+ private static final Logger s_logger = LoggerFactory.getLogger(APDUUtils.class);
+
+ /**
+ *
+ * Return APDU value for SELECT card operation
+ *
+ * @return Byte array with SELECT APDU
+ */
+ public static byte[] PIVSelectAPDU() {
+ if(s_pivSelect == null) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(APDUConstants.COMMAND);
+ baos.write(APDUConstants.SELECT);
+ byte[] p1p2 = {0x04, 0x00};
+ baos.write(p1p2);
+ baos.write((byte) APDUConstants.PIV_APPID.length);
+ baos.write(APDUConstants.PIV_APPID);
+ s_pivSelect = baos.toByteArray();
+ } catch(IOException ioe) {
+ // if we ever hit this, OOM is coming soon
+ s_logger.error("Unable to populate static PIV select APDU field.", ioe);
+ s_pivSelect = new byte[0];
+ }
+ }
+ return s_pivSelect;
+ }
+
+ /**
+ *
+ * Return APDU value for SELECT card operation based on a specific APP ID value
+ *
+ * @param appid Byte array with APP ID
+ * @return Byte array with SELECT APDU
+ */
+ public static byte[] PIVSelectAPDU(byte[] appid) {
+ byte[] rv_pivSelect = null;
+ if(rv_pivSelect == null) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(APDUConstants.COMMAND);
+ baos.write(APDUConstants.SELECT);
+ byte[] p1p2 = {0x04, 0x00};
+ baos.write(p1p2);
+ baos.write(appid.length);
+ baos.write(appid);
+ byte[] Le = {0x00};
+ baos.write(Le);
+ rv_pivSelect = baos.toByteArray();
+ } catch(IOException ioe) {
+ // if we ever hit this, OOM is coming soon
+ s_logger.error("Unable to populate static PIV select APDU field.", ioe);
+ rv_pivSelect = new byte[0];
+ }
+ }
+ return rv_pivSelect;
+ }
+
+ /**
+ *
+ * Return APDU value for GENERAL AUTHENTICATE
+ *
+ * @param keyReference Byte value identifying key reference of the generated key pair
+ * @param algorithmIdentifier Byte value identifying algorithm to be performed on card
+ * @param parameter Byte array containing the parameter value
+ * @return Byte array with GENERATE APDU
+ */
+ public static byte[] PIVGeneralAuthenticateAPDU(byte keyReference, byte algorithmIdentifier, byte[] parameter) {
+ byte[] rv_pivGeneralAuthenticate = null;
+ if(rv_pivGeneralAuthenticate == null) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(APDUConstants.COMMAND);
+ baos.write(APDUConstants.GENERAL_AUTHENTICATE);
+ byte[] p1 = {algorithmIdentifier};
+ baos.write(p1);
+ baos.write(keyReference);
+
+ //If parameter is present data length will be 1 (Tag 'AC') + length + 1 (cryptographic mechanism tag) + 1 (length) + 1 (cryptographic mechanism) + 1 (parameter tag) + parameter length length+ parameter length .
+ //If parameter is absent data length will be 1 (Tag 'AC') + length + 1 (cryptographic mechanism tag) + 1 (cryptographic mechanism length) + 1 (cryptographic mechanism)
+ if(parameter != null) {
+ baos.write(parameter.length);
+ }
+ else {
+ baos.write(0);
+ }
+
+ if(parameter != null) {
+ baos.write(parameter);
+ }
+ byte[] Le = {0x00};
+ baos.write(Le);
+ rv_pivGeneralAuthenticate = baos.toByteArray();
+ } catch(IOException ioe) {
+ // if we ever hit this, OOM is coming soon
+ s_logger.error("Unable to populate static PIV Generate APDU field.", ioe);
+ rv_pivGeneralAuthenticate = new byte[0];
+ }
+ }
+ return rv_pivGeneralAuthenticate;
+ }
+
+ /**
+ *
+ * Return APDU value for GENERATE card operation based on a specific APP ID value
+ *
+ * @param keyReference Byte value identifying key reference of the generated key pair
+ * @param cryptoMechanism Byte value identifying the type of key pair to be generated
+ * @param parameter Byte array containing the parameter value
+ * @return Byte array with GENERATE APDU
+ */
+ public static byte[] PIVGenerateKeyPairAPDU(byte keyReference, byte cryptoMechanism, byte[] parameter) {
+ byte[] rv_pivGenerate = null;
+ if(rv_pivGenerate == null) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(APDUConstants.COMMAND);
+ baos.write(APDUConstants.GENERATE);
+ byte[] p1 = {0x00};
+ baos.write(p1);
+ baos.write(keyReference);
+
+ //If parameter is present data length will be 1 (Tag 'AC') + length + 1 (cryptographic mechanism tag) + 1 (length) + 1 (cryptographic mechanism) + 1 (parameter tag) + parameter length length+ parameter length .
+ //If parameter is absent data length will be 1 (Tag 'AC') + length + 1 (cryptographic mechanism tag) + 1 (cryptographic mechanism length) + 1 (cryptographic mechanism)
+ if(parameter != null) {
+ baos.write(1 + 1 + 1 + 1 + 1 + 1 + 1 + parameter.length);
+ }
+ else {
+ baos.write(1 + 1 + 1 + 1 + 1);
+ }
+
+ //Write Control reference template tag
+ baos.write(APDUConstants.CONTROL_REFERENCE_TEMPLATE_TAG);
+
+ //Write length value for Control reference template
+
+ if(parameter != null) {
+
+ //Add length of Crypto Mechanism TLV
+ baos.write(3 + 2 + parameter.length);
+
+ }
+ else {
+
+ //Add length of Crypto Mechanism TLV
+ baos.write(3);
+ }
+
+ baos.write(TagConstants.CRYPTO_MECHANISM_TAG);
+ //Add length of crypto mechanism which will be 1
+ baos.write(1);
+ baos.write(cryptoMechanism);
+ if(parameter != null) {
+ byte[] parameterTag = {TagConstants.PARAMETER_TAG};
+ baos.write(parameterTag);
+ baos.write(parameter.length);
+ baos.write(parameter);
+ }
+ byte[] Le = {0x00};
+ baos.write(Le);
+ rv_pivGenerate = baos.toByteArray();
+ } catch(IOException ioe) {
+ // if we ever hit this, OOM is coming soon
+ s_logger.error("Unable to populate static PIV Generate APDU field.", ioe);
+ rv_pivGenerate = new byte[0];
+ }
+ }
+ return rv_pivGenerate;
+ }
+
+ /**
+ *
+ * @param data
+ * @return
+ */
+ public static byte[] PIVGetDataAPDU(byte[] data) {
+
+ byte[] rv_pivGetData = null;
+
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(APDUConstants.COMMAND);
+ baos.write(APDUConstants.GET);
+ byte[] p1p2 = {0x3f, (byte) 0xff};
+ baos.write(p1p2);
+ byte[] Lc = {(byte)(data.length & 0xff)};
+ baos.write(Lc);
+ baos.write(data);
+ byte[] Le = {0x00};
+ baos.write(Le);
+ rv_pivGetData = baos.toByteArray();
+ } catch(IOException ioe) {
+ // if we ever hit this, OOM is coming soon
+ s_logger.error("Unable to populate PIV get data APDU field.", ioe);
+ rv_pivGetData = new byte[0];
+ }
+
+ return rv_pivGetData;
+ }
+ /**
+ *
+ * @param data
+ * @return
+ */
+ public static byte[] PIVGetDataAPDU_Broken(byte[] data) {
+
+ byte[] rv_pivGetData = null;
+
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(APDUConstants.COMMAND);
+ baos.write(APDUConstants.GET);
+ byte[] p1p2 = {0x3f, (byte) 0xff};
+ baos.write(p1p2);
+ byte[] Lc = {(byte)(data.length & 0xff)};
+ baos.write(Lc);
+ baos.write(data);
+ byte[] Le = {0x08};
+ baos.write(Le);
+ rv_pivGetData = baos.toByteArray();
+ } catch(IOException ioe) {
+ // if we ever hit this, OOM is coming soon
+ s_logger.error("Unable to populate PIV get data APDU field.", ioe);
+ rv_pivGetData = new byte[0];
+ }
+
+ return rv_pivGetData;
+ }
+
+ /**
+ *
+ * Helper fuction that converts byte[] into unsigned int
+ *
+ * @param b Byte array to be converter to unsigned int
+ * @return Unsigned int value
+ */
+ public static final int bytesToInt(byte[] b) {
+
+ if(b.length != 2){
+ throw new IllegalArgumentException("Invalid buffer length passed in.");
+ }
+
+ int l = 0;
+ l |= b[0] & 0xFF;
+ l <<= 8;
+ l |= b[1] & 0xFF;
+ return l;
+ }
+
+ /**
+ *
+ * Helper function that constructs a TLV buffer based on passed in tag and value buffer
+ *
+ * @param tag Byte array with tag info
+ * @param value Byte array with value
+ * @return Byte array with resulting TLV value
+ */
+ public static final byte[] getTLV(byte[] tag, byte[] value) {
+
+ if(tag == null || value == null)
+ throw new IllegalArgumentException("Null buffer passed into getTLV().");
+ byte[] rv = null;
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ int numberLenBytes = (value == null) ? 0 : (value.length > 127) ? 2 : 1;
+ try {
+ // Tag
+ os.write(tag);
+ // Length & value
+ if (numberLenBytes == 2) {
+ os.write((byte) ((0x80 + numberLenBytes) & 0xff));
+ os.write((byte) (((value.length & 0xff00) >> 8) & 0xff));
+ os.write((byte) (value.length & 0x00ff));
+ os.write(value);
+ } else if (numberLenBytes == 1) {
+ os.write((byte) (value.length & 0xff));
+ os.write(value);
+ } else if (numberLenBytes == 0) {
+ os.write(0x00);
+ }
+ } catch (IOException e) {
+ s_logger.error("Failed to create TLV value: {}" , e.getMessage());
+ return rv;
+ }
+
+ rv = os.toByteArray();
+ return rv;
+ }
+
+ /**
+ *
+ * Helper function that creates ASN1ObjectIdentifier object based on OID value and a service name
+ *
+ * @param serviceName String value identifying the service
+ * @param name String value identifying OID by name
+ * @return
+ */
+ public static ASN1ObjectIdentifier getAlgorithmIdentifier(String serviceName, String name) {
+ ASN1ObjectIdentifier oid = null;
+ Provider provider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
+ Provider.Service service = provider.getService(serviceName, name);
+ if (service != null) {
+ String string = service.toString();
+ String array[] = string.split("\n");
+ if (array.length > 1) {
+ string = array[array.length - 1];
+ array = string.split("[\\[\\]]");
+ if (array.length > 2) {
+ string = array[array.length - 2];
+ array = string.split(", ");
+ Arrays.sort(array);
+ oid = new ASN1ObjectIdentifier(array[0]);
+ }
+ }
+ }
+ return oid;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/AbstractPIVApplication.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/AbstractPIVApplication.java
new file mode 100644
index 00000000..17ae9cc6
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/AbstractPIVApplication.java
@@ -0,0 +1,703 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.BerTag;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlv;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlvParser;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlvs;
+import gov.gsa.pivconformance.cardlib.tlv.CCTTlvLogger;
+import gov.gsa.pivconformance.cardlib.tlv.TagConstants;
+import gov.gsa.pivconformance.cardlib.utils.PCSCWrapper;
+
+import org.apache.commons.codec.binary.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.smartcardio.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A base class for items that will implement the IPIVApplication interface, to allow those methods that can be
+ * common across implementations to be shared
+ */
+abstract public class AbstractPIVApplication implements IPIVApplication {
+
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(AbstractPIVApplication.class);
+ private CommandAPDU m_lastCommandAPDU = null;
+ private ResponseAPDU m_lastResponseAPDU;
+
+ /**
+ *
+ * Set the PIV Card Application as the currently selected card application and establish
+ * the PIV Card Applicationâs security state.
+ *
+ * @param cardHandle CardHandle object that encapsulates connection to a card
+ * @param applicationAID ApplicationAID object containing the AID of the PIV Card Application
+ * @param applicationProperties ApplicationProperties object containing application properties of the selected PIV
+ * Card Application
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ @Override
+ public MiddlewareStatus pivSelectCardApplication(CardHandle cardHandle, ApplicationAID applicationAID, ApplicationProperties applicationProperties) {
+ s_logger.debug("pivSelectCardApplication()");
+ try {
+ // Establishing channel
+ Card card = cardHandle.getCard();
+ if (card == null)
+ return MiddlewareStatus.PIV_INVALID_CARD_HANDLE;
+
+ // Establishing channel
+ CardChannel channel = card.getBasicChannel();
+ cardHandle.setCurrentChannel(channel);
+
+ PCSCWrapper pcsc = PCSCWrapper.getInstance();
+
+ //Construct APDU command using APDUUtils and applicationAID that was passed in.
+ CommandAPDU cmd = new CommandAPDU(APDUUtils.PIVSelectAPDU(applicationAID.getBytes()));
+ m_lastCommandAPDU = cmd; m_lastResponseAPDU = null;
+ // Transmit command and get response
+ ResponseAPDU response = pcsc.transmit(channel, cmd);
+ m_lastResponseAPDU = response;
+ s_logger.debug("Response to SELECT command: {} {}", String.format("0x%02X", response.getSW1()), String.format("0x%02X", response.getSW2()));
+
+ //Check for Successful execution status word
+ if(response.getSW() != APDUConstants.SUCCESSFUL_EXEC) {
+
+ // XXX *** TODO: handle 61XX
+ if(response.getSW1() == 0x61) {
+ s_logger.info("SW1 == 61");
+ return MiddlewareStatus.PIV_OK;
+ }
+
+ if(response.getSW() == APDUConstants.APP_NOT_FOUND){
+ s_logger.info("Card application not found");
+ return MiddlewareStatus.PIV_CARD_APPLICATION_NOT_FOUND;
+ }
+
+ s_logger.error("Error selecting card application, failed with error: {}", Integer.toHexString(response.getSW()));
+ return MiddlewareStatus.PIV_CONNECTION_FAILURE;
+ }
+
+ // Populated the response in ApplicationProperties
+ byte[] properties = response.getData();
+ if(properties != null) applicationProperties.setBytes(properties);
+
+ }
+ catch (Exception ex) {
+
+ s_logger.error("Error selecting card application: {}", ex.getMessage());
+ return MiddlewareStatus.PIV_CONNECTION_FAILURE;
+ }
+ s_logger.debug("pivSelectCardApplication returning {}", MiddlewareStatus.PIV_OK);
+ return MiddlewareStatus.PIV_OK;
+ }
+
+ /**
+ *
+ * Sets security state within the PIV Card Application.
+ *
+ * @param cardHandle CardHandle object that encapsulates connection to a card
+ * @param authenticators Byte array cotaining authenticators to be used to authenticate and set security
+ * state/status in the PIV Card Application context
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ @Override
+ public MiddlewareStatus pivLogIntoCardApplication(CardHandle cardHandle, byte[] authenticators) {
+ PIVAuthenticators pas = new PIVAuthenticators();
+ pas.decode(authenticators);
+ for(PIVAuthenticator authenticator : pas.getAuthenticators()) {
+ if(authenticator.getType() != TagConstants.KEY_REFERENCE_APPLICATION_PIN_TAG &&
+ authenticator.getType() != TagConstants.KEY_REFERENCE_GLOBAL_PIN_TAG ) {
+ s_logger.warn("Skipping authenticator of type {}. Currently unsupported.", authenticator.getType());
+ continue;
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ baos.write(APDUConstants.COMMAND);
+ baos.write(APDUConstants.VERIFY);
+ baos.write((byte) 0x00); // logging in
+ baos.write(authenticator.getType());
+ baos.write(authenticator.getData().length == 0 ? 0x00 : (byte) 0x08); // PIN
+ baos.write(authenticator.getData());
+ } catch(IOException ioe) {
+ s_logger.error("Failed to populate VERIFY APDU buffer");
+ }
+ byte[] rawAPDU = baos.toByteArray();
+ //s_logger.error("VERIFY APDU: {}", Hex.encodeHexString(rawAPDU));
+ CardChannel channel = cardHandle.getCurrentChannel();
+ CommandAPDU verifyApdu = new CommandAPDU(rawAPDU);
+ ResponseAPDU resp = null;
+ try {
+ PCSCWrapper pcsc = PCSCWrapper.getInstance();
+ m_lastCommandAPDU = verifyApdu; m_lastResponseAPDU = null;
+ resp = pcsc.transmit(channel, verifyApdu);
+ m_lastResponseAPDU = resp;
+ } catch (CardException e) {
+ s_logger.error("Failed to transmit VERIFY APDU to card", e);
+ return MiddlewareStatus.PIV_CARD_READER_ERROR;
+ }
+ if(resp.getSW() == 0x9000) {
+ cardHandle.setCurrentChannel(channel);
+ s_logger.debug("Successfully logged into card application");
+ } else {
+ s_logger.error("Login failed: {}", Hex.encodeHexString(resp.getBytes()));
+ s_logger.error("Card: {}", cardHandle.getCard());
+ //s_logger.error("Last command APDU: {}", Hex.encodeHexString(m_lastCommandAPDU.getBytes()));
+ s_logger.error("Last response APDU: {}", Hex.encodeHexString(m_lastResponseAPDU.getBytes()));
+ return MiddlewareStatus.PIV_AUTHENTICATION_FAILURE;
+ }
+
+ }
+ return MiddlewareStatus.PIV_OK;
+ }
+
+ /**
+ *
+ * Retrieves all the data containers of the PIV Card Application
+ *
+ * @param cardHandle CardHandle object that encapsulates connection to a card
+ * @param dataList List of PIVDataObject objects containing all the data containers of PIV Card Application
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ public MiddlewareStatus pivGetAllDataContainers(CardHandle cardHandle, List dataList) {
+
+ MiddlewareStatus result = MiddlewareStatus.PIV_OK;
+
+ if (cardHandle == null)
+ return MiddlewareStatus.PIV_INVALID_CARD_HANDLE;
+
+ try {
+ if(dataList == null)
+ dataList = new ArrayList();
+
+ for(String containerOID : APDUConstants.AllContainers()){
+
+ //Create object from the OID
+ PIVDataObject dataObject = PIVDataObjectFactory.createDataObjectForOid(containerOID);
+ s_logger.info("Attempting to read data object for OID {} ({})", containerOID, APDUConstants.oidNameMap.get(containerOID));
+
+ result = this.pivGetData(cardHandle, containerOID, dataObject);
+
+ //Add the data object to the list if successful return code
+ if(result == MiddlewareStatus.PIV_OK)
+ dataList.add(dataObject);
+ }
+
+
+ }catch (SecurityException ex) {
+
+ s_logger.info("Error retrieving data from the card application: {}", ex.getMessage());
+ return MiddlewareStatus.PIV_SECURITY_CONDITIONS_NOT_SATISFIED;
+ }
+ catch (Exception ex) {
+
+ s_logger.info("Error retrieving data from the card application: {}", ex.getMessage());
+ return MiddlewareStatus.PIV_CONNECTION_FAILURE;
+ }
+
+ return MiddlewareStatus.PIV_OK;
+ }
+
+ /**
+ *
+ * @param cardHandle CardHandle object that encapsulates connection to a card
+ * @param OID String containing OID value identifying data object whose data content is to be
+ * retrieved
+ * @param data PIVDataObject object that will store retrieved data content
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ @Override
+ public MiddlewareStatus pivGetData(CardHandle cardHandle, String OID, PIVDataObject data) {
+ byte [] oidBytes = APDUConstants.oidMAP.get(OID);
+ if(oidBytes == null) {
+ s_logger.error("OID {} is not recognized by oidMAP.", OID);
+ return MiddlewareStatus.PIV_DATA_OBJECT_NOT_FOUND;
+ }
+
+ try {
+ // Establishing channel
+ Card card = cardHandle.getCard();
+ if (card == null)
+ return MiddlewareStatus.PIV_INVALID_CARD_HANDLE;
+
+ CardChannel channel = cardHandle.getCurrentChannel();
+ if(channel == null) {
+ throw new IllegalStateException("Must select PIV application before calling pivGetData");
+ }
+
+ //Construct data field based on the data field oid and the tag for the specific oid
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(TagConstants.DATA_FIELD_TAG);
+ baos.write(oidBytes.length);
+ baos.write(oidBytes);
+
+ //Construct APDU command using APDUUtils and applicationAID that was passed in.
+ CommandAPDU cmd = new CommandAPDU(APDUUtils.PIVGetDataAPDU(baos.toByteArray()));
+
+ PCSCWrapper pcsc = PCSCWrapper.getInstance();
+ // Transmit command and get response
+ m_lastCommandAPDU = cmd; m_lastResponseAPDU = null;
+ ResponseAPDU response = pcsc.transmit(channel, cmd);
+ m_lastResponseAPDU = response;
+
+ //Check for Successful execution status word
+ if(response.getSW() != APDUConstants.SUCCESSFUL_EXEC) {
+
+ if(response.getSW() == APDUConstants.APP_NOT_FOUND){
+ s_logger.info("Data object not found");
+ return MiddlewareStatus.PIV_DATA_OBJECT_NOT_FOUND;
+ }
+ else if(response.getSW() == APDUConstants.SECURITY_STATUS_NOT_SATISFIED){
+ s_logger.info("Security status not satisfied");
+ return MiddlewareStatus.PIV_SECURITY_CONDITIONS_NOT_SATISFIED;
+ }
+
+ s_logger.error("Error getting object {}, failed with error: {}", OID, Integer.toHexString(response.getSW()));
+ return MiddlewareStatus.PIV_CONNECTION_FAILURE;
+ }
+ byte[] responseData = response.getData();
+ BerTlvParser lengthCheckTlvParser = new BerTlvParser(new CCTTlvLogger(this.getClass()));
+ BerTlvs outer = lengthCheckTlvParser.parse(response.getData());
+ if(outer != null) {
+ if (outer.getList() == null) {
+ s_logger.warn("GET DATA returned status of 90 00 but tag list is null");
+ return MiddlewareStatus.PIV_DATA_OBJECT_NOT_FOUND;
+ }
+ BerTlv tlv = outer.getList().get(0);
+ if (tlv == null) {
+ s_logger.warn("GET DATA returned status of 90 00 but TLV at 0 is null");
+ return MiddlewareStatus.PIV_DATA_OBJECT_NOT_FOUND;
+ }
+ BerTag tag = tlv.getTag();
+ if (tag == null) {
+ s_logger.warn("GET DATA returned status of 90 00 but tag {} is null", tlv.getHexValue());
+ return MiddlewareStatus.PIV_DATA_OBJECT_NOT_FOUND;
+ }
+ if(tag.bytes.length == 1 && tag.bytes[0] == 0x53 && responseData.length == 2 && responseData[1] == 0x00) {
+ s_logger.debug("GET DATA returned status of 90 00 but a tag of 0x53 with a length of 0." +
+ " Per SP800-73-4, PIV middleware should return PIV_DATA_OBJECT_NOT_FOUND." );
+ return MiddlewareStatus.PIV_DATA_OBJECT_NOT_FOUND;
+ }
+ }
+
+ // Populate the response in PIVDataObject
+ data.setOID(OID);
+ data.setBytes(response.getData());
+
+ }catch (SecurityException ex) {
+
+ s_logger.error("Error retrieving data from the card application: {}", ex.getMessage(), ex);
+ return MiddlewareStatus.PIV_SECURITY_CONDITIONS_NOT_SATISFIED;
+ }
+ catch (Exception ex) {
+
+ s_logger.error("Error retrieving data from the card application: {}", ex.getMessage(), ex);
+ return MiddlewareStatus.PIV_CONNECTION_FAILURE;
+ }
+
+ return MiddlewareStatus.PIV_OK;
+ }
+
+ /**
+ *
+ * @param cardHandle CardHandle object that encapsulates connection to a card
+ * @param OID String containing OID value identifying data object whose data content is to be
+ * retrieved
+ * @param data PIVDataObject object that will store retrieved data content
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ public MiddlewareStatus pivGetAllData(CardHandle cardHandle, String OID, PIVDataObject data) {
+
+ try {
+ // Establishing channel
+ Card card = cardHandle.getCard();
+ if (card == null)
+ return MiddlewareStatus.PIV_INVALID_CARD_HANDLE;
+
+ CardChannel channel = cardHandle.getCurrentChannel();
+ if(channel == null) {
+ throw new IllegalStateException("Must select PIV application before calling pivGetData");
+ }
+
+ //Construct data field based on the data field oid and the tag for the specific oid
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(TagConstants.DATA_FIELD_TAG);
+ baos.write(0x00);
+ baos.write(APDUConstants.oidMAP.get(OID));
+
+ //Construct APDU command using APDUUtils and applicationAID that was passed in.
+ CommandAPDU cmd = new CommandAPDU(APDUUtils.PIVGetDataAPDU(baos.toByteArray()));
+
+ PCSCWrapper pcsc = PCSCWrapper.getInstance();
+ // Transmit command and get response
+ m_lastCommandAPDU = cmd; m_lastResponseAPDU = null;
+ ResponseAPDU response = pcsc.transmit(channel, cmd);
+ m_lastResponseAPDU = response;
+
+ //Check for Successful execution status word
+ if(response.getSW() != APDUConstants.SUCCESSFUL_EXEC) {
+
+ if(response.getSW() == APDUConstants.APP_NOT_FOUND){
+ s_logger.info("Data object not found");
+ return MiddlewareStatus.PIV_DATA_OBJECT_NOT_FOUND;
+ }
+ else if(response.getSW() == APDUConstants.SECURITY_STATUS_NOT_SATISFIED){
+ s_logger.info("Security status not satisfied");
+ return MiddlewareStatus.PIV_SECURITY_CONDITIONS_NOT_SATISFIED;
+ }
+
+ s_logger.error("Error getting object {}, failed with error: {}", OID, Integer.toHexString(response.getSW()));
+ return MiddlewareStatus.PIV_CONNECTION_FAILURE;
+ }
+
+ // Populate the response in PIVDataObject
+ data.setOID(OID);
+ data.setBytes(response.getData());
+
+ }catch (SecurityException ex) {
+
+ s_logger.info("Error retrieving data from the card application: {}", ex.getMessage());
+ return MiddlewareStatus.PIV_SECURITY_CONDITIONS_NOT_SATISFIED;
+ }
+ catch (Exception ex) {
+
+ s_logger.info("Error retrieving data from the card application: {}", ex.getMessage());
+ return MiddlewareStatus.PIV_CONNECTION_FAILURE;
+ }
+
+ return MiddlewareStatus.PIV_OK;
+ }
+
+ /**
+ *
+ * Reses the application security state/status of the PIV Card Application.
+ *
+ * @param cardHandle CardHandle object that encapsulates connection to a card
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ @Override
+ public MiddlewareStatus pivLogoutOfCardApplication(CardHandle cardHandle) {
+ return null;
+ }
+
+ /**
+ *
+ * @param cardHandle CardHandle object that encapsulates connection to a card
+ * @param algorithmIdentifier Byte value identifying the cryptographic algorithm to be used for
+ * the cryptographic operation
+ * @param keyReference Byte value identifying the on-card key to be used for the
+ * cryptographic operation.
+ * @param algorithmInput PIVDataObject object containing sequence of bytes used as the input to the cryptographic
+ * operation
+ * @param algorithmOutput PIVDataObject object containing sequence of bytes used as the output to the cryptographic
+ * * operation
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ @Override
+ public MiddlewareStatus pivCrypt(CardHandle cardHandle, byte algorithmIdentifier, byte keyReference,
+ PIVDataObject algorithmInput, PIVDataObject algorithmOutput) {
+ try {
+ Card card = cardHandle.getCard();
+ if (card == null)
+ return MiddlewareStatus.PIV_INVALID_CARD_HANDLE;
+
+ CardChannel channel = card.getBasicChannel();
+ if(channel == null)
+ return MiddlewareStatus.PIV_INVALID_CARD_HANDLE;
+
+ byte[] rawAPDU = APDUUtils.PIVGeneralAuthenticateAPDU(keyReference, algorithmIdentifier, algorithmInput.getBytes());
+ s_logger.info("GENERAL AUTHENTICATE APDU: {}", Hex.encodeHexString(rawAPDU));
+
+
+ CommandAPDU cmd = new CommandAPDU(rawAPDU);
+ // Transmit command and get response
+ m_lastCommandAPDU = cmd; m_lastResponseAPDU = null;
+ ResponseAPDU response = channel.transmit(cmd);
+ m_lastResponseAPDU = response;
+
+ s_logger.debug("Response to GENERAL AUTHENTICATE command: {} {}", String.format("0x%02X", response.getSW1()), String.format("0x%02X", response.getSW2()));
+
+ //Check for Successful execution status word
+ if(response.getSW() != APDUConstants.SUCCESSFUL_EXEC) {
+
+ if(response.getSW() == APDUConstants.SECURITY_STATUS_NOT_SATISFIED){
+ s_logger.error("Security condition not satisfied");
+ return MiddlewareStatus.PIV_SECURITY_CONDITIONS_NOT_SATISFIED;
+ }
+ else if(response.getSW() == APDUConstants.INCORREECT_PARAMETER){
+ s_logger.error("Incorrect parameter in command data field");
+ return MiddlewareStatus.PIV_UNSUPPORTED_CRYPTOGRAPHIC_MECHANISM;
+ }
+ else if(response.getSW() == APDUConstants.FUNCTION_NOT_SUPPORTED){
+ s_logger.error("Function not supported");
+ return MiddlewareStatus.PIV_FUNCTION_NOT_SUPPORTED;
+ }
+ else if(response.getSW() == APDUConstants.INCORREECT_PARAMETER_P2){
+ s_logger.error("Invalid key or key algorithm combination");
+ return MiddlewareStatus.PIV_INVALID_KEY_OR_KEYALG_COMBINATION;
+ }
+ else {
+ s_logger.error("Error in GENERAL AUTHENTICATE command, failed with error: {}", Integer.toHexString(response.getSW()));
+ return MiddlewareStatus.PIV_CONNECTION_FAILURE;
+ }
+ }
+ algorithmOutput.setBytes(response.getData());
+ cardHandle.setCurrentChannel(channel);
+ } catch(Exception e) {
+ s_logger.error("Failed to complete pivCrypt operation for algorithm {} (key {}",
+ Hex.encodeHexString(new byte[] {algorithmIdentifier}), Hex.encodeHexString(new byte[] {keyReference}), e);
+ }
+ return null;
+ }
+
+ /**
+ *
+ * Generates an asymmetric key pair in the currently selected card application.
+ *
+ * @param cardHandle CardHandle object that encapsulates connection to a card
+ * @param keyReference Byte value identifying key reference of the generated key pair
+ * @param cryptographicMechanism Byte value identifying the type of key pair to be generated
+ * @param publicKey PIVDataObject object defining the public key of the generated key pair
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ @Override
+ public MiddlewareStatus pivGenerateKeyPair(CardHandle cardHandle, byte keyReference, byte cryptographicMechanism, PIVDataObject publicKey){
+ try {
+ // Establishing channel
+ Card card = cardHandle.getCard();
+ if (card == null)
+ return MiddlewareStatus.PIV_INVALID_CARD_HANDLE;
+
+ // Establishing channel
+ CardChannel channel = card.getBasicChannel();
+
+ //Construct APDU command using APDUUtils and keyReference, cryptographicMechanism that was passed in.
+ byte[] rawAPDU = APDUUtils.PIVGenerateKeyPairAPDU(keyReference, cryptographicMechanism, null);
+ s_logger.info("GENERATE APDU: {}", Hex.encodeHexString(rawAPDU));
+
+ CommandAPDU cmd = new CommandAPDU(rawAPDU);
+
+ PCSCWrapper pcsc = PCSCWrapper.getInstance();
+ // Transmit command and get response
+ m_lastCommandAPDU = cmd; m_lastResponseAPDU = null;
+ ResponseAPDU response = pcsc.transmit(channel, cmd);
+ m_lastResponseAPDU = response;
+
+ s_logger.debug("Response to GENERATE command: {} {}", String.format("0x%02X", response.getSW1()), String.format("0x%02X", response.getSW2()));
+
+ //Check for Successful execution status word
+ if(response.getSW() != APDUConstants.SUCCESSFUL_EXEC) {
+
+ if(response.getSW() == APDUConstants.SECURITY_STATUS_NOT_SATISFIED){
+ s_logger.error("Security condition not satisfied");
+ return MiddlewareStatus.PIV_SECURITY_CONDITIONS_NOT_SATISFIED;
+ }
+ else if(response.getSW() == APDUConstants.INCORREECT_PARAMETER){
+ s_logger.error("Incorrect parameter in command data field");
+ return MiddlewareStatus.PIV_UNSUPPORTED_CRYPTOGRAPHIC_MECHANISM;
+ }
+ else if(response.getSW() == APDUConstants.FUNCTION_NOT_SUPPORTED){
+ s_logger.error("Function not supported");
+ return MiddlewareStatus.PIV_FUNCTION_NOT_SUPPORTED;
+ }
+ else if(response.getSW() == APDUConstants.INCORREECT_PARAMETER_P2){
+ s_logger.error("Invalid key or key algorithm combination");
+ return MiddlewareStatus.PIV_INVALID_KEY_OR_KEYALG_COMBINATION;
+ }
+ else {
+ s_logger.error("Error generating key pair, failed with error: {}", Integer.toHexString(response.getSW()));
+ return MiddlewareStatus.PIV_CONNECTION_FAILURE;
+ }
+ }
+
+ // Populated the response in ApplicationProperties
+ publicKey.setBytes(response.getData());
+ cardHandle.setCurrentChannel(channel);
+
+ }
+ catch (Exception ex) {
+
+ s_logger.error("Error generating key pair: {}", ex.getMessage());
+ return MiddlewareStatus.PIV_CONNECTION_FAILURE;
+ }
+ s_logger.debug("pivGenerateKeyPair returning {}", MiddlewareStatus.PIV_OK);
+ return MiddlewareStatus.PIV_OK;
+ }
+
+ /**
+ * Establishes secure messaging with the PIV Card Application.
+ *
+ * @param cardHandle CardHandle object that encapsulates connection to a card
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ @Override
+ public MiddlewareStatus pivEstablishSecureMessaging(CardHandle cardHandle) {
+ s_logger.debug("pivEstablishSecureMessaging()");
+ try {
+ // Establishing channel
+ Card card = cardHandle.getCard();
+ if (card == null)
+ return MiddlewareStatus.PIV_INVALID_CARD_HANDLE;
+
+
+ byte[] dataField = { (byte) 0x7C, 0x05, (byte) 0x81, 0x01, 0x00, (byte) 0x82, 0x00 };
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ baos.write(APDUConstants.COMMAND);
+ baos.write(APDUConstants.SM);
+ baos.write(APDUConstants.CIPHER_SUITE_1); // Algorithm Reference for algs that support SM.
+ baos.write(APDUConstants.PIV_SECURE_MESSAGING_KEY);
+ baos.write(dataField.length);
+ baos.write(dataField);
+ baos.write(0x00); //Le
+ } catch(IOException ioe) {
+ s_logger.error("Failed to populate SM APDU buffer");
+ }
+ byte[] rawAPDU = baos.toByteArray();
+ s_logger.info("SM APDU: {}", Hex.encodeHexString(rawAPDU));
+ CardChannel channel = cardHandle.getCurrentChannel();
+ CommandAPDU smApdu = new CommandAPDU(rawAPDU);
+ ResponseAPDU resp = null;
+ try {
+ PCSCWrapper pcsc = PCSCWrapper.getInstance();
+ m_lastCommandAPDU = smApdu; m_lastResponseAPDU = null;
+ resp = pcsc.transmit(channel, smApdu);
+ m_lastResponseAPDU = resp;
+ } catch (CardException e) {
+ s_logger.error("Failed to transmit SM APDU to card", e);
+ return MiddlewareStatus.PIV_CARD_READER_ERROR;
+ }
+ if(resp.getSW() == 0x9000) {
+ cardHandle.setCurrentChannel(channel);
+ s_logger.info("Successfully established secure messaging");
+ } else {
+ s_logger.error("Error establishing secure messaging: {}", Hex.encodeHexString(resp.getBytes()));
+ return MiddlewareStatus.PIV_SM_FAILED;
+ }
+
+ }
+ catch (Exception ex) {
+
+ s_logger.error("Error establishing secure messaging: {}", ex.getMessage());
+ return MiddlewareStatus.PIV_CARD_READER_ERROR;
+ }
+ s_logger.debug("pivSelectCardApplication returning {}", MiddlewareStatus.PIV_OK);
+ return MiddlewareStatus.PIV_OK;
+ }
+
+ /**
+ * Replaces the entire data content of the data object specified by the OID parameter with the provided data.
+ *
+ * @param cardHandle CardHandle object that encapsulates connection to a card
+ * @param OID String containing OID value identifying data object
+ * @param data PIVDataObject object containing data that will be written to the card
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ @Override
+ public MiddlewareStatus pivPutData(CardHandle cardHandle, String OID, PIVDataObject data) {
+
+ s_logger.debug("pivPutData()");
+ try {
+ // Establishing channel
+ Card card = cardHandle.getCard();
+ if (card == null)
+ return MiddlewareStatus.PIV_INVALID_CARD_HANDLE;
+
+ if (OID == null)
+ return MiddlewareStatus.PIV_INVALID_OID;
+
+
+
+ ByteArrayOutputStream baosDataField = new ByteArrayOutputStream();
+ if(data.getOID().equals(APDUConstants.DISCOVERY_OBJECT_OID) || data.getOID().equals(APDUConstants.BIOMETRIC_INFORMATION_TEMPLATES_GROUP_TEMPLATE_OID)){
+
+ baosDataField.write(data.getBytes());
+ }
+ else {
+ baosDataField.write(TagConstants.TAG_LIST);
+ baosDataField.write(APDUConstants.oidMAP.get(OID).length);
+ baosDataField.write(APDUConstants.oidMAP.get(OID));
+ baosDataField.write(data.getBytes());
+ }
+
+ s_logger.debug("dataField: {}", Hex.encodeHexString(baosDataField.toByteArray()));
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ baos.write(APDUConstants.COMMAND);
+ baos.write(APDUConstants.INS_DB);
+ baos.write(APDUConstants.P1_3F);
+ baos.write(APDUConstants.P2_FF);
+ baos.write(baosDataField.toByteArray().length);
+ baos.write(baosDataField.toByteArray());
+ } catch(IOException ioe) {
+ s_logger.error("Failed to populate PUT DATA APDU buffer");
+ }
+ byte[] rawAPDU = baos.toByteArray();
+ s_logger.info("PUT DATA APDU: {}", Hex.encodeHexString(rawAPDU));
+ CardChannel channel = cardHandle.getCurrentChannel();
+ CommandAPDU smApdu = new CommandAPDU(rawAPDU);
+ ResponseAPDU resp = null;
+ try {
+ PCSCWrapper pcsc = PCSCWrapper.getInstance();
+ m_lastCommandAPDU = smApdu; m_lastResponseAPDU = null;
+ resp = pcsc.transmit(channel, smApdu);
+ m_lastResponseAPDU = resp;
+ } catch (CardException e) {
+ s_logger.error("Failed to transmit PUT DATA APDU to card", e);
+ return MiddlewareStatus.PIV_CARD_READER_ERROR;
+ }
+ if(resp.getSW() == 0x9000) {
+ cardHandle.setCurrentChannel(channel);
+ s_logger.info("Successfully wrote data object to the card.");
+ } else if(resp.getSW() == 0x6A82){
+ s_logger.error("Failed to write object to the card, security condition not satisfied: {}", Hex.encodeHexString(resp.getBytes()));
+ return MiddlewareStatus.PIV_SECURITY_CONDITIONS_NOT_SATISFIED;
+ } else if(resp.getSW() == 0x6A81){
+ s_logger.error("Failed to write object to the card, function is not supported: {}", Hex.encodeHexString(resp.getBytes()));
+ return MiddlewareStatus.PIV_FUNCTION_NOT_SUPPORTED;
+ } else if(resp.getSW() == 0x6A84){
+ s_logger.error("Failed to write object to the card, not enough memory: {}", Hex.encodeHexString(resp.getBytes()));
+ return MiddlewareStatus.PIV_INSUFFICIENT_CARD_RESOURCE;
+ } else {
+ s_logger.error("Failed to write object to the card: {}", Hex.encodeHexString(resp.getBytes()));
+ return MiddlewareStatus.PIV_CARD_READER_ERROR;
+ }
+
+ }
+ catch (Exception ex) {
+
+ s_logger.error("Error writing data object to the card: {}", ex.getMessage());
+ return MiddlewareStatus.PIV_CARD_READER_ERROR;
+ }
+ s_logger.debug("pivPutData returning {}", MiddlewareStatus.PIV_OK);
+ return MiddlewareStatus.PIV_OK;
+ }
+
+ public byte[] getLastCommandAPDUBytes()
+ {
+ byte[] apduBytes = null;
+ if(m_lastCommandAPDU == null) {
+ s_logger.error("getLastCommandAPDUBytes() called without any command APDU having been sent.");
+ return apduBytes;
+ }
+ apduBytes = m_lastCommandAPDU.getBytes();
+ return apduBytes;
+ }
+
+ public byte[] getLastResponseAPDUBytes()
+ {
+ byte[] apduBytes = null;
+ if(m_lastResponseAPDU == null) {
+ s_logger.error("getLastResponseAPDUBytes() called without any command APDU having been sent.");
+ return apduBytes;
+ }
+ apduBytes = m_lastResponseAPDU.getBytes();
+ return apduBytes;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/Algorithm.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/Algorithm.java
similarity index 90%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/Algorithm.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/Algorithm.java
index 488b94fd..3d0d3e3d 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/Algorithm.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/Algorithm.java
@@ -1,74 +1,73 @@
-/**
- *
- */
-package gov.gsa.pivconformance.card.client;
-
-import java.util.HashMap;
-import java.util.Iterator;
-
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.CMSSignedData;
-
-/**
- *
- * Helpers for converting algorithm OIDs to standard names used in PIV
- *
- */
-public class Algorithm {
-
- public static final HashMap sigAlgOidToNameMap = new HashMap() {
- /**
- * Signature
- */
- private static final long serialVersionUID = 1L;
-
- {
- put("1.2.840.113549.1.1.1O", "SHA526withRSA"); // RSA-PKCS1.5 with SHA-256 and PSS padding
- put("1.2.840.113549.1.1.11", "RSASSA-PSS"); // RSA-PSS with SHA256
- put("1.2.840.10045.4.3.2", "SHA256withECDSA"); // SHA-256 with ECDSA
- put("1.2.840.10045.4.3.3", "SHA384withECDSA"); // SHA-384 with ECDSA
- }
- };
-
- public static final HashMap digAlgOidToNameMap = new HashMap() {
- /**
- * Digest
- */
- private static final long serialVersionUID = 1L;
-
- {
- put("2.16.840.1.101.3.4.2.1", "SHA256");
- put("2.16.840.1.101.3.4.2.2", "SHA384");
- }
- };
- public static final HashMap encAlgOidToNameMap = new HashMap() {
- /**
- * Encryption
- */
- private static final long serialVersionUID = 1L;
-
- {
- put("1.2.840.113549.1.1.1", "RSA");
- put("1.2.840.10045.2.1", "ECDSA");
- }
- };
-
- /**
- * Indicates whether this signature block contains only digest algorithms in
- * Table 3-2 of SP 800-78-4
- *
- * @param asymmetricSignature the signature to be checked
- * @return true if the digest is in Table 3-2 and false if not
- */
-
- public static boolean isDigestAlgInTable32(CMSSignedData asymmetricSignature) {
- Iterator ih = asymmetricSignature.getDigestAlgorithmIDs().iterator();
- while (ih.hasNext()) {
- AlgorithmIdentifier ai = ih.next();
- String digAlgOid = ai.getAlgorithm().getId();
- if (!Algorithm.digAlgOidToNameMap.containsKey(digAlgOid))
- return false;
- }
- return true;
- }
-}
+/**
+ *
+ */
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cms.CMSSignedData;
+
+/**
+ *
+ * Helpers for converting algorithm OIDs to standard names used in PIV
+ *
+ */
+public class Algorithm {
+
+ public static final HashMap sigAlgOidToNameMap = new HashMap() {
+ /**
+ * Signature
+ */
+ private static final long serialVersionUID = 1L;
+
+ {
+ put("1.2.840.113549.1.1.1O", "SHA526withRSA"); // RSA-PKCS1.5 with SHA-256 and PSS padding
+ put("1.2.840.113549.1.1.11", "RSASSA-PSS"); // RSA-PSS with SHA256
+ put("1.2.840.10045.4.3.2", "SHA256withECDSA"); // SHA-256 with ECDSA
+ put("1.2.840.10045.4.3.3", "SHA384withECDSA"); // SHA-384 with ECDSA
+ }
+ };
+
+ public static final HashMap digAlgOidToNameMap = new HashMap() {
+ /**
+ * Digest
+ */
+ private static final long serialVersionUID = 1L;
+
+ {
+ put("2.16.840.1.101.3.4.2.1", "SHA256");
+ put("2.16.840.1.101.3.4.2.2", "SHA384");
+ }
+ };
+ public static final HashMap encAlgOidToNameMap = new HashMap() {
+ /**
+ * Encryption
+ */
+ private static final long serialVersionUID = 1L;
+
+ {
+ put("1.2.840.113549.1.1.1", "RSA");
+ put("1.2.840.10045.2.1", "ECDSA");
+ }
+ };
+
+ /**
+ * Indicates whether this signature block contains only digest algorithms in Table 3-2 of
+ * SP 800-78-4
+ * @param asymmetricSignature the signature to be checked
+ * @return true if the digest is in Table 3-2 and false if not
+ */
+
+ public static boolean isDigestAlgInTable32(CMSSignedData asymmetricSignature) {
+ Iterator ih = asymmetricSignature.getDigestAlgorithmIDs().iterator();
+ while (ih.hasNext()) {
+ AlgorithmIdentifier ai = ih.next();
+ String digAlgOid = ai.getAlgorithm().getId();
+ if (!Algorithm.digAlgOidToNameMap.containsKey(digAlgOid))
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ApplicationAID.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ApplicationAID.java
new file mode 100644
index 00000000..2651d7e3
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ApplicationAID.java
@@ -0,0 +1,49 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Encapsulates a PIV application identifier
+ */
+public class ApplicationAID {
+ private static final Logger s_logger = LoggerFactory.getLogger(ApplicationAID.class);
+
+ /**
+ * ApplicationAID class constructor, initializes all the class fields.
+ */
+ public ApplicationAID() {
+ m_appIDBytes = null;
+ }
+
+ /**
+ *
+ * ApplicationAID class constructor, sets the app ID value bassed on the passed in buffer
+ *
+ * @param appIDBytes
+ */
+ public ApplicationAID(byte[] appIDBytes) {
+ m_appIDBytes = appIDBytes;
+ }
+
+
+ private byte[] m_appIDBytes;
+
+ /**
+ *
+ * Sets the app id value
+ *
+ * @param appIDBytes Byte array with app id value
+ */
+ public void setBytes(byte[] appIDBytes) {
+ m_appIDBytes = appIDBytes;
+ }
+
+ /**
+ *
+ * Returns app id value
+ *
+ * @return Byte array with app id value
+ */
+ public byte[] getBytes() { return m_appIDBytes; }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ApplicationProperties.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ApplicationProperties.java
new file mode 100644
index 00000000..1ddf56d6
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ApplicationProperties.java
@@ -0,0 +1,163 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates the application properties record for a PIV application, as described in SP800-73-4 part 2, table 3
+ */
+public class ApplicationProperties {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(ApplicationProperties.class);
+
+ private byte[] m_appPropertiesBytes;
+ private String m_url;
+ private String m_appLabel;
+ private List m_cryptoAlgs;
+ private byte[] m_coexistentTagAllocationAuthority;
+ private byte[] m_appID;
+
+ /**
+ * ApplicationAID class constructor, initializes all the class fields.
+ */
+ public ApplicationProperties() {
+
+ m_appPropertiesBytes = null;
+ m_url = "";
+ m_appLabel = "";
+ m_cryptoAlgs = null;
+ m_cryptoAlgs = null;
+ m_coexistentTagAllocationAuthority = null;
+ m_appID = null;
+ }
+
+ /**
+ *
+ * Sets the application properties value based on passed in parameter
+ *
+ * @param appPropertiesBytes Byte array with application properties value
+ */
+ public void setBytes(byte[] appPropertiesBytes) {
+
+ m_appPropertiesBytes = appPropertiesBytes;
+
+ try
+ {
+ BerTlvParser parser = new BerTlvParser();
+ BerTlvs tlvs = parser.parse(appPropertiesBytes, 0, appPropertiesBytes.length);
+
+
+ BerTag berAIDTag = new BerTag(TagConstants.AID_TAG);
+ BerTag berAppLabelTag = new BerTag(TagConstants.APPLICATION_LABEL);
+ BerTag berURLTag = new BerTag(TagConstants.UNIFORM_RESOURCE_LOCATOR);
+ BerTag berCryptAlgsTag = new BerTag(TagConstants.CRYPTOGRAPHIC_ALGORITHMS);
+ BerTag berCoexistentTagAllocationAuthorityTag = new BerTag(TagConstants.COEXISTENT_TAG_ALLOCATION_AUTHORITY);
+
+ BerTlv aidTlv = tlvs.find(berAIDTag);
+ BerTlv appLabelTlv = tlvs.find(berAppLabelTag);
+ BerTlv urlTlv = tlvs.find(berURLTag);
+ BerTlv CryptAlgsTlv = tlvs.find(berCryptAlgsTag);
+ BerTlv CoexistentTagAllocationAuthorityTlv = tlvs.find(berCoexistentTagAllocationAuthorityTag);
+
+ if(aidTlv != null){
+ m_appID = aidTlv.getBytesValue();
+ }
+
+ if(appLabelTlv != null){
+ m_appLabel = new String(appLabelTlv.getBytesValue());
+ }
+
+ if(urlTlv != null){
+ m_url = new String(urlTlv.getBytesValue());
+ }
+
+ if(CryptAlgsTlv != null){
+
+ m_cryptoAlgs = new ArrayList<>();
+ List berTlvsList = CryptAlgsTlv.getValues();
+ for(BerTlv tlv : berTlvsList) {
+ BerTag tag = tlv.getTag();
+ m_cryptoAlgs.add(tag.bytes);
+ }
+ }
+
+ if(CoexistentTagAllocationAuthorityTlv != null){
+ List berTlvsList = CoexistentTagAllocationAuthorityTlv.getValues();
+ for(BerTlv tlv : berTlvsList) {
+ if(tlv.isPrimitive() && tlv.isTag(berAIDTag)) {
+ m_coexistentTagAllocationAuthority = tlv.getBytesValue();
+ }
+ }
+ }
+
+ }catch (Exception ex) {
+
+ s_logger.error("Unable to parse application properties data structure: {}", ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ *
+ * Returns a byte array with application properties value
+ *
+ * @return Byte array with application properties value
+ */
+ public byte[] getBytes() {
+ return m_appPropertiesBytes;
+ }
+
+ /**
+ *
+ * Returns application URL value
+ *
+ * @return String with application URL
+ */
+ public String getURL() {
+ return m_url;
+ }
+
+ /**
+ *
+ * Returns application label value
+ *
+ * @return String with application label
+ */
+ public String getAppLabel() {
+ return m_appLabel;
+ }
+
+ /**
+ *
+ * Returns list of cryptographic algorithms
+ *
+ * @return List of cryptographic algorithms
+ */
+ public List getCryptoAlgs() {
+ return m_cryptoAlgs;
+ }
+
+ /**
+ *
+ * Returns coexistent tag allocation authority
+ *
+ * @return Byte array with coexistent tag allocation authority
+ */
+ public byte[] getCoexistentTagAllocationAuthority() {
+ return m_coexistentTagAllocationAuthority;
+ }
+
+ /**
+ *
+ * Returns Application ID
+ *
+ * @return Byte array with Application ID
+ */
+ public byte[] getAppID() {
+ return m_appID;
+ }
+
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ArtifactWriter.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ArtifactWriter.java
new file mode 100644
index 00000000..d3bb8b98
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ArtifactWriter.java
@@ -0,0 +1,123 @@
+/**
+ *
+ */
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages the local artifact cache to minimize disk I/O
+ *
+ */
+
+public class ArtifactWriter {
+ private static final Logger s_logger = LoggerFactory.getLogger(ArtifactWriter.class);
+ private static final String m_artifactDir = Paths.get(".").toAbsolutePath().normalize().toString();
+ static HashMap> m_artifactCache = new HashMap>();
+
+ public ArtifactWriter(String subDir) {
+ init(subDir);
+ }
+
+ void init(String artifactSubDir) {
+ String sep = File.separator;
+ String cwd = m_artifactDir;
+ String artifactPath = null;
+
+ artifactPath = cwd + sep + artifactSubDir;
+
+ if (!Files.exists(Paths.get(artifactPath))) {
+ File file = new File(artifactPath);
+ boolean exists = file.mkdir();
+ if (exists){
+ s_logger.debug("Artifact subdirectory " + artifactSubDir + " created successfully");
+ } else{
+ System.out.println("Couldn’t create directory " + artifactSubDir);
+ }
+ }
+
+ if (m_artifactCache.get(artifactSubDir) == null)
+ m_artifactCache.put(artifactSubDir, new ArrayList());
+ }
+
+ /**
+ * Exports a container
+ *
+ * @param containerName
+ * @param bytes
+ * @return
+ */
+
+ public boolean saveObject(String artifactSubDir, String containerName, byte[] bytes) {
+ boolean result = false;
+ String filePath = m_artifactDir + File.separator + artifactSubDir + File.separator + containerName;
+ if (!m_artifactCache.containsKey(artifactSubDir))
+ init(artifactSubDir);
+
+ if (!m_artifactCache.get(artifactSubDir).contains(filePath)) {
+ try {
+ FileOutputStream fos = new FileOutputStream(filePath);
+ fos.write(bytes);
+ fos.flush();
+ fos.close();
+ s_logger.debug("Wrote " + filePath);
+ m_artifactCache.get(artifactSubDir).add(filePath);
+ result = true;
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ } else {
+ result = true;
+ }
+ return result;
+ }
+
+ /**
+ * Prepends artifact file names with a time stamp
+ * @param timeStamp time stamp to prepend
+ */
+
+ public static boolean prependNames(String timeStamp) {
+ boolean result = false;
+ Iterator> it = m_artifactCache.entrySet().iterator();
+ while (it.hasNext()) {
+ @SuppressWarnings("rawtypes")
+ Map.Entry mapElement = (Map.Entry)it.next();
+ @SuppressWarnings("unchecked")
+ ArrayList pathList = (ArrayList) mapElement.getValue();
+ for (String p : pathList) {
+ int index = p.lastIndexOf(File.separator) + 1;
+ String baseName = p.substring(index);
+ String newBaseName = timeStamp + "-" + baseName;
+ File f = new File(p);
+ File g = new File(m_artifactDir + File.separator + (String) mapElement.getKey() + File.separator + newBaseName);
+ try {
+ result = f.renameTo(g);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public static void clean() {
+ m_artifactCache = new HashMap>();
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/BiometricInformationTemplatesGroupTemplate.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/BiometricInformationTemplatesGroupTemplate.java
new file mode 100644
index 00000000..80a9dea3
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/BiometricInformationTemplatesGroupTemplate.java
@@ -0,0 +1,145 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.*;
+import org.apache.commons.codec.binary.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+
+/**
+ *
+ * Encapsulates a Biometric Information Templates Group Template data object as defined by SP800-73-4 Part 2 Appendix A Table 41
+ *
+ */
+public class BiometricInformationTemplatesGroupTemplate extends PIVDataObject {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(BiometricInformationTemplatesGroupTemplate.class);
+
+ private byte[] m_numberOfFingers;
+ private byte[] m_bITForFirstFinger;
+ private byte[] m_bITForSecondFinger;
+
+
+ /**
+ * BiometricInformationTemplatesGroupTemplate class constructor, initializes all the class fields.
+ */
+ public BiometricInformationTemplatesGroupTemplate() {
+ m_numberOfFingers = null;
+ m_bITForFirstFinger = null;
+ m_bITForSecondFinger = null;
+ }
+
+ /**
+ *
+ * Returns byte array containing number of fingers information
+ *
+ * @return Byte array containing number of fingers information
+ */
+ public byte[] getNumberOfFingers() {
+ return m_numberOfFingers;
+ }
+
+ /**
+ *
+ * Sets the number of fingers information
+ *
+ * @param numberOfFingers Byte array containing number of fingers information
+ */
+ public void setNumberOfFingers(byte[] numberOfFingers) {
+ m_numberOfFingers = numberOfFingers;
+ }
+
+ /**
+ *
+ * Returns the BIT information for the first finger
+ *
+ * @return Byte array containing BIT information for the first finger
+ */
+ public byte[] getbITForFirstFinger() {
+ return m_bITForFirstFinger;
+ }
+
+ /**
+ *
+ * Sets the BIT information for the first finger
+ *
+ * @param bITForFirstFinger Byte array containing BIT information for the first finger
+ */
+ public void setbITForFirstFinger(byte[] bITForFirstFinger) {
+ m_bITForFirstFinger = bITForFirstFinger;
+ }
+
+ /**
+ *
+ * Returns the BIT information for the second finger
+ *
+ * @return Byte array containing BIT information for the second finger
+ */
+ public byte[] getbITForSecondFinger() {
+ return m_bITForSecondFinger;
+ }
+
+ /**
+ *
+ * Sets the BIT information for the second finger
+ *
+ * @param bITForSecondFinger Byte array containing BIT information for the second finger
+ */
+ public void setbITForSecondFinger(byte[] bITForSecondFinger) {
+ m_bITForSecondFinger = bITForSecondFinger;
+ }
+
+ /**
+ *
+ * Decode function that decodes Biometric Information Templates Group Template object retrieved from the card and populates various class fields.
+ *
+ * @return True if decode was successful, false otherwise
+ */
+ @Override
+ public boolean decode() {
+
+ try{
+ byte[] rawBytes = this.getBytes();
+
+ if(rawBytes == null){
+ s_logger.error("No buffer to decode for {}.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ BerTlvParser tlvp = new BerTlvParser(new CCTTlvLogger(this.getClass()));
+ BerTlv outer = tlvp.parseConstructed(rawBytes);
+
+ if(outer == null){
+ s_logger.error("Error parsing {}, unable to parse TLV value.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ for(BerTlv tlv : outer.getValues()) {
+ byte[] tag = tlv.getTag().bytes;
+ if(Arrays.equals(tag, TagConstants.NUMBER_OF_FINGERS_TAG)) {
+ m_numberOfFingers = tlv.getBytesValue();
+ } else if(Arrays.equals(tag, TagConstants.BIT_FOR_FIRST_FINGER_TAG)) {
+
+ if(m_bITForFirstFinger == null)
+ m_bITForFirstFinger = tlv.getBytesValue();
+ else
+ m_bITForSecondFinger = tlv.getBytesValue();
+
+ } else {
+ s_logger.warn("Unexpected tag: {} with value: {}", Hex.encodeHexString(tlv.getTag().bytes), Hex.encodeHexString(tlv.getBytesValue()));
+ }
+ }
+ }catch (Exception ex) {
+
+ s_logger.error("Error parsing {}: {}", APDUConstants.oidNameMap.get(super.getOID()), ex.getMessage());
+ }
+
+ if(m_numberOfFingers == null || m_bITForFirstFinger == null)
+ return false;
+
+ dump(this.getClass())
+;
+ return true;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CachingDefaultPIVApplication.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CachingDefaultPIVApplication.java
new file mode 100644
index 00000000..3507a95a
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CachingDefaultPIVApplication.java
@@ -0,0 +1,36 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import java.util.HashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CachingDefaultPIVApplication extends DefaultPIVApplication {
+ private static final Logger s_logger = LoggerFactory.getLogger(CachingDefaultPIVApplication.class);
+
+ static HashMap m_containerMap = new HashMap();
+
+ // Cache the buffers coming back from pivGetData to minimize churn
+ @Override
+ public MiddlewareStatus pivGetData(CardHandle cardHandle, String OID, PIVDataObject data) {
+ MiddlewareStatus result = MiddlewareStatus.PIV_OK;
+ byte[] dataBytes = m_containerMap.get(OID);
+ if(dataBytes == null) {
+ result = super.pivGetData(cardHandle, OID, data);
+ if(result == MiddlewareStatus.PIV_OK) {
+ m_containerMap.put(OID, data.getBytes());
+ }
+ } else {
+ data.setOID(OID);
+ data.setBytes(dataBytes);
+ }
+ return result;
+ }
+
+ /**
+ * Clear cache
+ */
+ public void clearCache() {
+ m_containerMap.clear();
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardCapabilityContainer.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardCapabilityContainer.java
new file mode 100644
index 00000000..0eb22519
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardCapabilityContainer.java
@@ -0,0 +1,407 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.*;
+import org.apache.commons.codec.binary.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Arrays;
+import java.util.HashMap;
+
+/**
+ *
+ * Encapsulates a Card Capability Container data object as defined by SP800-73-4 Part 2 Appendix A Table 8
+ *
+ */
+public class CardCapabilityContainer extends PIVDataObject {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(CardCapabilityContainer.class);
+
+ private byte[] m_cardIdentifier;
+ private byte[] m_capabilityContainerVersionNumber;
+ private byte[] m_capabilityGrammarVersionNumber;
+ private List m_appCardURL;
+ private byte[] m_pkcs15;
+ private byte[] m_registeredDataModelNumber;
+ private byte[] m_accessControlRuleTable;
+ private boolean m_cardAPDUs;
+ private boolean m_redirectionTag;
+ private boolean m_capabilityTuples;
+ private boolean m_statusTuples;
+ private boolean m_nextCCC;
+ private List m_extendedApplicationCardURL;
+ private byte[] m_securityObjectBuffer;
+ private byte[] m_signedContent;
+ //private HashMap m_content;
+
+
+ /**
+ * CardCapabilityContainer class constructor, initializes all the class fields.
+ */
+ public CardCapabilityContainer() {
+
+ m_cardIdentifier = null;
+ m_capabilityContainerVersionNumber = null;
+ m_capabilityGrammarVersionNumber = null;
+ m_appCardURL = null;
+ m_pkcs15 = null;
+ m_registeredDataModelNumber = null;
+ m_accessControlRuleTable = null;
+ m_cardAPDUs = false;
+ m_redirectionTag = false;
+ m_capabilityTuples = false;
+ m_statusTuples = false;
+ m_nextCCC = false;
+ m_extendedApplicationCardURL = null;
+ m_securityObjectBuffer = null;
+ setErrorDetectionCode(false);
+ setErrorDetectionCodeHasData(false);
+ m_content = new HashMap();
+ }
+
+ /**
+ *
+ * Returns byte array with signed content buffer
+ *
+ * @return Byte array with signed content buffer
+ */
+ public byte[] getSignedContent() {
+ return m_signedContent;
+ }
+
+ /**
+ *
+ * Sets the signed content buffer
+ *
+ * @param signedContent Byte array with signed content buffer
+ */
+ public void setSignedContent(byte[] signedContent) {
+ m_signedContent = signedContent;
+ }
+
+ /**
+ *
+ * Returns card identifier
+ *
+ * @return Byte array containing card identifier
+ */
+ public byte[] getCardIdentifier() {
+
+ return m_cardIdentifier;
+ }
+
+ /**
+ *
+ * Returns capability container version number
+ *
+ * @return Byte array containing capability container version number
+ */
+ public byte[] getCapabilityContainerVersionNumber() {
+
+ return m_capabilityContainerVersionNumber;
+ }
+
+ /**
+ *
+ * Returns capability grammar version number
+ *
+ * @return Byte array containing capability grammar version number
+ */
+ public byte[] getCapabilityGrammarVersionNumber() {
+
+ return m_capabilityGrammarVersionNumber;
+ }
+
+ /**
+ *
+ * Returns a list of application card urls
+ *
+ * @return List of application card urls
+ */
+ public List getAppCardURL() {
+
+ return m_appCardURL;
+ }
+
+ /**
+ *
+ * Returns PKCS15 value
+ *
+ * @return Byte array containing PKCS15 value
+ */
+ public byte[] getPkcs15() {
+
+ return m_pkcs15;
+ }
+
+ /**
+ *
+ * Returns Registered Data Model number value
+ *
+ * @return Byte array containing Registered Data Model number
+ */
+ public byte[] getRegisteredDataModelNumber() {
+
+ return m_registeredDataModelNumber;
+ }
+
+ /**
+ *
+ * Returns Access Control Rule Table value
+ *
+ * @return Byte array containing Access Control Rule Table value
+ */
+ public byte[] getAccessControlRuleTable() {
+
+ return m_accessControlRuleTable;
+ }
+
+ /**
+ *
+ * Returns Card APDUs value
+ *
+ * @return Byte array containing Card APDUs value
+ */
+ public boolean getCardAPDUs() {
+
+ return m_cardAPDUs;
+ }
+
+ /**
+ *
+ * Returns Redirection Tag value
+ *
+ * @return Byte array containing Redirection Tag value
+ */
+ public boolean getRedirectionTag() {
+
+ return m_redirectionTag;
+ }
+
+ /**
+ *
+ * Returns Capability Tuples value
+ *
+ * @return Byte array containing Capability Tuples value
+ */
+ public boolean getCapabilityTuples() {
+
+ return m_capabilityTuples;
+ }
+
+ /**
+ *
+ * Returns Status Tuples value
+ *
+ * @return Byte array containing Status Tuples value
+ */
+ public boolean getStatusTuples() {
+
+ return m_statusTuples;
+ }
+
+ /**
+ *
+ * Returns Next CCC value
+ *
+ * @return Byte array containing Next CCC value
+ */
+ public boolean getNextCCC() {
+
+ return m_nextCCC;
+ }
+
+ /**
+ *
+ * Returns a list of if Extended Application CardURL
+ *
+ * @return List of if Extended Application CardURL
+ */
+ public List getExtendedApplicationCardURL() {
+
+ return m_extendedApplicationCardURL;
+ }
+
+ /**
+ *
+ * Returns Security Object Buffer value
+ *
+ * @return Byte array containing Security Object Buffer
+ */
+ public byte[] getSecurityObjectBuffer() {
+
+ return m_securityObjectBuffer;
+ }
+
+
+ /**
+ *
+ * Decode function that decodes Card Capability Container object retrieved from the card and populates various class fields.
+ *
+ * @return True if decode was successful, false otherwise
+ */
+ @Override
+ public boolean decode() {
+
+ try{
+ byte [] raw = super.getBytes();
+
+ if(raw == null){
+ s_logger.error("No buffer to decode for {}.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ BerTlvParser tp = new BerTlvParser(new CCTTlvLogger(CardCapabilityContainer.class));
+ BerTlvs outer = tp.parse(raw);
+
+ if(outer == null){
+ s_logger.error("Error parsing CCC container, unable to parse TLV value 1.");
+ return false;
+ }
+
+ List values = outer.getList();
+ for(BerTlv tlv : values) {
+ if(tlv.isPrimitive()) {
+ s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv.getTag().bytes), Hex.encodeHexString(tlv.getBytesValue()));
+
+ BerTlvs outer2 = tp.parse(tlv.getBytesValue());
+
+ if(outer2 == null){
+ s_logger.error("Error parsing CCC, unable to parse TLV value 2.");
+ return false;
+ }
+
+ ByteArrayOutputStream scos = new ByteArrayOutputStream();
+ List values2 = outer2.getList();
+ for(BerTlv tlv2 : values2) {
+ if(tlv2.isPrimitive()) {
+ s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv2.getTag().bytes), Hex.encodeHexString(tlv2.getBytesValue()));
+ } else {
+ super.m_tagList.add(tlv2.getTag());
+ if(Arrays.equals(tlv2.getTag().bytes,TagConstants.CARD_IDENTIFIER_TAG)) {
+ if (tlv2.hasRawValue()) {
+ m_cardIdentifier = tlv2.getBytesValue();
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.CARD_IDENTIFIER_TAG, m_cardIdentifier));
+ }
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.CAPABILITY_CONTAINER_VERSION_NUMBER_TAG)) {
+ if (tlv2.hasRawValue()) {
+ m_capabilityContainerVersionNumber = tlv2.getBytesValue();
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.CAPABILITY_CONTAINER_VERSION_NUMBER_TAG, m_capabilityContainerVersionNumber));
+ }
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.CAPABILITY_GRAMMAR_VERSION_NUMBER_TAG)) {
+ if (tlv2.hasRawValue()) {
+ m_capabilityGrammarVersionNumber = tlv2.getBytesValue();
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.CAPABILITY_GRAMMAR_VERSION_NUMBER_TAG, m_capabilityContainerVersionNumber));
+ }
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.APPLICATIONS_CARDURL_TAG)) {
+ if (tlv2.hasRawValue()) {
+
+ if(m_appCardURL == null)
+ m_appCardURL = new ArrayList<>();
+ m_appCardURL.add(tlv2.getBytesValue());
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.APPLICATIONS_CARDURL_TAG, tlv2.getBytesValue()));
+ }
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.PKCS15_TAG)) {
+ if (tlv2.hasRawValue()) {
+ m_pkcs15 = tlv2.getBytesValue();
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.PKCS15_TAG, m_pkcs15));
+ }
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.REGISTERED_DATA_MODEL_NUMBER_TAG)) {
+ if (tlv2.hasRawValue()) {
+ m_registeredDataModelNumber = tlv2.getBytesValue();
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.REGISTERED_DATA_MODEL_NUMBER_TAG, m_registeredDataModelNumber));
+ }
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.ACCESS_CONTROL_RULE_TABLE_TAG)) {
+ if (tlv2.hasRawValue()) {
+ m_accessControlRuleTable = tlv2.getBytesValue();
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.ACCESS_CONTROL_RULE_TABLE_TAG, m_accessControlRuleTable));
+ }
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.CARD_APDUS_TAG)) {
+ m_cardAPDUs = true;
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.CARD_APDUS_TAG, tlv2.getBytesValue()));
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.REDIRECTION_TAG_TAG)) {
+ m_redirectionTag = true;
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.REDIRECTION_TAG_TAG, tlv2.getBytesValue()));
+
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.CAPABILITY_TUPLES_TAG)) {
+ m_capabilityTuples = true;
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.CAPABILITY_TUPLES_TAG, tlv2.getBytesValue()));
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.STATUS_TUPLES_TAG)) {
+ m_statusTuples = true;
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.STATUS_TUPLES_TAG, tlv2.getBytesValue()));
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.NEXT_CCC_TAG)) {
+ m_nextCCC = true;
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.NEXT_CCC_TAG, tlv2.getBytesValue()));
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.EXTENDED_APPLICATION_CARDURL_TAG)) {
+ if(m_extendedApplicationCardURL == null)
+ m_extendedApplicationCardURL = new ArrayList<>();
+ m_extendedApplicationCardURL.add(tlv2.getBytesValue());
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.EXTENDED_APPLICATION_CARDURL_TAG, tlv2.getBytesValue()));
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.SECURITY_OBJECT_BUFFER_TAG)) {
+ m_securityObjectBuffer = tlv2.getBytesValue();
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.SECURITY_OBJECT_BUFFER_TAG, tlv2.getBytesValue()));
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.ERROR_DETECTION_CODE_TAG)) {
+ setErrorDetectionCode(true);
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(TagConstants.ERROR_DETECTION_CODE_TAG);
+ scos.write((byte) 0x00);
+ }
+ }
+ }
+
+
+ m_signedContent = scos.toByteArray();
+
+ } else {
+ s_logger.info("Object: {}", Hex.encodeHexString(tlv.getTag().bytes));
+ }
+ }
+ } catch (Exception ex) {
+
+ s_logger.error("Error parsing CCC: {}", ex.getMessage());
+ return false;
+ }
+
+ if (m_cardIdentifier == null || m_capabilityContainerVersionNumber == null ||
+ m_capabilityGrammarVersionNumber == null || m_appCardURL == null || m_pkcs15 == null ||
+ m_registeredDataModelNumber == null || m_accessControlRuleTable == null || m_cardAPDUs == false ||
+ m_redirectionTag == false || m_capabilityTuples == false || m_statusTuples == false ||
+ m_nextCCC == false) {
+ return false;
+ }
+
+ dump(this.getClass());
+ return true;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardClientException.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardClientException.java
new file mode 100644
index 00000000..0e5e3a47
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardClientException.java
@@ -0,0 +1,52 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+/**
+ * A class for card-related exceptions
+ */
+public class CardClientException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ * Default constructor for CardClientException class
+ *
+ */
+ public CardClientException() {
+ super();
+ }
+
+ /**
+ *
+ * Constructor for CardClientException class that takes a string with exception message
+ *
+ * @param message String with the exception message
+ */
+ public CardClientException(String message) {
+ super(message);
+ }
+
+ /**
+ *
+ * Constructor for CardClientException class that takes a string with exception message and a Throwable cause
+ *
+ * @param message String with the exception message
+ * @param cause Throwable cause
+ */
+ public CardClientException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ *
+ * Constructor for CardClientException class that takes a Throwable cause
+ *
+ * @param cause Throwable cause
+ */
+ public CardClientException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardHandle.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardHandle.java
new file mode 100644
index 00000000..a9032e05
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardHandle.java
@@ -0,0 +1,114 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import javax.smartcardio.Card;
+import javax.smartcardio.CardChannel;
+
+/**
+ * A class that serves the function of the handle objects passed around that encapsulate a connection to a card
+ * in SP800-73
+ */
+public class CardHandle {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(CardHandle.class);
+
+ /**
+ *
+ * Get the connection description object associated with this card handle
+ *
+ * @return ConnectionDescription that includes as CardTerminal object used to access the reader
+ */
+ public ConnectionDescription getConnectionDescription() {
+ return m_connectionDescription;
+ }
+
+ /**
+ *
+ * Set the connection description object that will be used by the card handle
+ *
+ * @param connectionDescription Connection description object
+ */
+ public void setConnectionDescription(ConnectionDescription connectionDescription) {
+ m_connectionDescription = connectionDescription;
+ }
+
+ /**
+ *
+ * Set the Card object that will be used by the card handle
+ *
+ * @param card Card object
+ */
+ public void setCard(Card card) {
+ m_card = card;
+ }
+
+ /**
+ *
+ * Get the Card object associated with this card handle
+ *
+ * @return Card object
+ */
+ public Card getCard() {
+ return m_card;
+ }
+
+ /**
+ * Initialize an invalid card handle object
+ */
+ public CardHandle() {
+ m_connectionDescription = null;
+ m_card = null;
+ m_currentChannel = null;
+ m_valid = false;
+ }
+
+ /**
+ *
+ * Get the current card channel
+ *
+ * @return CardChannel object
+ */
+ public CardChannel getCurrentChannel() {
+ return m_currentChannel;
+ }
+
+ /**
+ *
+ * Sets the current card channel
+ *
+ * @param currentChannel CardChannel object
+ */
+ public void setCurrentChannel(CardChannel currentChannel) {
+ m_currentChannel = currentChannel;
+ }
+
+ /**
+ *
+ * Returns trues if card handle is valid for accessing a PIV card
+ *
+ * @return true if the handle is valid for accessing a PIV card
+ */
+ public boolean isValid() {
+ return m_valid;
+ }
+
+ /**
+ *
+ * Sets the value that indicates the status of the card handle object
+ *
+ * sets the boolen value that indicates the status of card handle object
+ * @param valid
+ */
+ public void setValid(boolean valid) {
+ m_valid = valid;
+ }
+
+ private ConnectionDescription m_connectionDescription;
+ private boolean m_valid = false;
+ private Card m_card;
+
+
+ private CardChannel m_currentChannel;
+
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardHolderBiometricData.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardHolderBiometricData.java
new file mode 100644
index 00000000..a1ef01ca
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardHolderBiometricData.java
@@ -0,0 +1,426 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.*;
+import org.apache.commons.codec.binary.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Iterator;
+import java.nio.ByteBuffer;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.util.Store;
+
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+
+/**
+ *
+ * Container class that parses and stores information about Biometric Data elements. Biometric Data elements include Cardholder Fingerprints,
+ * Cardholder Facial Image and Cardholder Iris Image as defined by SP800-73-4 Part 2 Appendix A Table 11, Table 13 and Table 40
+ *
+ */
+public class CardHolderBiometricData extends SignedPIVDataObject {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(CardHolderBiometricData.class);
+
+ private byte[] m_biometricData;
+ private String m_biometricCreationDate;
+ private boolean m_errorDetectionCode;
+ private String m_validityPeriodFrom;
+ private String m_validityPeriodTo;
+ private byte[] m_biometricDataBlock;
+ private byte[] m_signatureBlock;
+ private byte[] m_cbeffContainer;
+
+ /**
+ * CardholderBiometricData class constructor, initializes all the class fields.
+ */
+ public CardHolderBiometricData() {
+ super();
+ m_biometricData = null;
+ m_errorDetectionCode = false;
+ m_biometricCreationDate = null;
+ m_validityPeriodFrom = null;
+ m_validityPeriodTo = null;
+ m_biometricDataBlock = null;
+ m_signatureBlock = null;
+ m_cbeffContainer = null;
+ m_content = new HashMap();
+ }
+
+ /**
+ *
+ * Returns the CBEFF container value
+ *
+ * @return Byte array with CBEFF container value
+ */
+ public byte[] getCbeffContainer() {
+ return m_cbeffContainer;
+ }
+
+ /**
+ *
+ * Sets the CBEFF container value
+ *
+ * @param cbeffContainer Byte array with CBEFF container value
+ */
+ public void setCbeffContainer(byte[] cbeffContainer) {
+ m_cbeffContainer = cbeffContainer;
+ }
+
+ /**
+ *
+ * Returns a byte array with biometric data
+ *
+ * @return Byte array with biometric data
+ */
+ public byte[] getBiometricData() {
+ return m_biometricData;
+ }
+
+ /**
+ *
+ * Returns a byte array with the CMS
+ *
+ * @return Byte array with CMS
+ */
+ public byte[] getSignatureBlock() {
+ return m_signatureBlock;
+ }
+ /**
+ *
+ * Sets the biometric data
+ *
+ * @param biometricData Byte array with biometric data
+ */
+ public void setBiometricData(byte[] biometricData) {
+ m_biometricData = biometricData;
+ }
+
+ /**
+ *
+ * Returns true if error detection code is present, false otherwise
+ *
+ * @return Returns true if error detection code is present, false otherwise
+ */
+ @Override
+ public boolean getErrorDetectionCode() {
+ return m_errorDetectionCode;
+ }
+
+ /**
+ *
+ * Sets if error detection code is present
+ *
+ * @param errorDetectionCode Boolean indicating if error detection code is present
+ */
+ @Override
+ public void setErrorDetectionCode(boolean errorDetectionCode) {
+ m_errorDetectionCode = errorDetectionCode;
+ }
+
+
+ /**
+ *
+ * Returns biometric creation date value
+ *
+ * @return String indicating biometric creation date
+ */
+ public String getBiometricCreationDate() {
+ return m_biometricCreationDate;
+ }
+
+ /**
+ *
+ * Sets the biometric creation date
+ *
+ * @param biometricCreationDate String indicating biometric creation date
+ */
+ public void setBiometricCreationDate(String biometricCreationDate) {
+ m_biometricCreationDate = biometricCreationDate;
+ }
+
+ /**
+ *
+ * Returns the biometric data block
+ *
+ * @return Byte array containing biometric data block
+ */
+ public byte[] getBiometricDataBlock() {
+ return m_biometricDataBlock;
+ }
+
+ /**
+ *
+ * Sets the biometric data block
+ *
+ * @param biometricDataBlock Byte array containing biometric data block
+ */
+ public void setBiometricDataBlock(byte[] biometricDataBlock) {
+ m_biometricDataBlock = biometricDataBlock;
+ }
+
+ /**
+ *
+ * Returns the validity preriod from value
+ *
+ * @return String indicating validity from value
+ */
+ public String getValidityPeriodFrom() {
+ return m_validityPeriodFrom;
+ }
+
+ /**
+ *
+ * Sets the validity from value
+ *
+ * @param validityPeriodFrom String indicating validity from value
+ */
+ public void setValidityPeriodFrom(String validityPeriodFrom) {
+ m_validityPeriodFrom = validityPeriodFrom;
+ }
+
+ /**
+ *
+ * Returns the validity preriod to value
+ *
+ * @return String indicating validity to value
+ */
+ public String getValidityPeriodTo() {
+ return m_validityPeriodTo;
+ }
+
+ /**
+ *
+ * Sets the validity from value
+ *
+ * @param validityPeriodTo String indicating validity from value
+ */
+ public void setValidityPeriodTo(String validityPeriodTo) {
+ m_validityPeriodTo = validityPeriodTo;
+ }
+
+ /**
+ *
+ * Decode function that decodes biometric data object retrieved from the card and populates various class fields.
+ *
+ * @return True if decode was successful, false otherwise
+ */
+ @Override
+ public boolean decode() {
+
+ boolean certFound = false;
+ ByteArrayOutputStream signedContentOutputStream = new ByteArrayOutputStream();
+ SignerInformationStore signers = null;
+ SignerInformation signer = null;
+ try {
+ byte[] rawBytes = this.getBytes();
+
+ s_logger.trace("rawBytes: {}", Hex.encodeHexString(rawBytes));
+
+ if(rawBytes == null){
+ s_logger.error("No buffer to decode for {}.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ BerTlvParser tlvp = new BerTlvParser(new CCTTlvLogger(this.getClass()));
+ BerTlvs outer = tlvp.parse(rawBytes);
+
+ if(outer == null){
+ s_logger.error("Error parsing {}, unable to parse TLV value.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ List values = outer.getList();
+ for(BerTlv tlv : values) {
+ if(tlv.isPrimitive()) {
+ s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv.getTag().bytes), Hex.encodeHexString(tlv.getBytesValue()));
+
+ BerTlvs outer2 = tlvp.parse(tlv.getBytesValue());
+
+ if (outer2 == null) {
+ s_logger.error("Error parsing {}, unable to parse TLV value.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ List values2 = outer2.getList();
+ for (BerTlv tlv2 : values2) {
+ if (tlv2.isPrimitive()) {
+ s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv2.getTag().bytes), Hex.encodeHexString(tlv2.getBytesValue()));
+ } else {
+ BerTag tag = tlv2.getTag();
+ byte[] value = tlv2.getBytesValue();
+
+ super.m_tagList.add(tag);
+ if (Arrays.equals(tag.bytes, TagConstants.FINGERPRINT_I_AND_II_TAG) && getOID().compareTo(APDUConstants.CARDHOLDER_FINGERPRINTS_OID) == 0) {
+
+ setContainerName("Fingerprints");
+ m_biometricData = value;
+ m_content.put(tag, value);
+ if (m_biometricData != null)
+ signedContentOutputStream.write(APDUUtils.getTLV(TagConstants.FINGERPRINT_I_AND_II_TAG, m_biometricData));
+
+ } else if (Arrays.equals(tag.bytes, TagConstants.IMAGE_FOR_VISUAL_VERIFICATION_TAG) && getOID().compareTo(APDUConstants.CARDHOLDER_FACIAL_IMAGE_OID) == 0) {
+
+ setContainerName("ImageForVisualVerification");
+ m_biometricData = value;
+ m_content.put(tag, value);
+ if (m_biometricData != null)
+ signedContentOutputStream.write(APDUUtils.getTLV(TagConstants.IMAGE_FOR_VISUAL_VERIFICATION_TAG, m_biometricData));
+
+ } else if (Arrays.equals(tag.bytes, TagConstants.IMAGES_FOR_IRIS_TAG) && getOID().compareTo(APDUConstants.CARDHOLDER_IRIS_IMAGES_OID) == 0) {
+
+ setContainerName("ImagesForIris");
+ m_biometricData = value;
+ m_content.put(tag, value);
+ if (m_biometricData != null)
+ signedContentOutputStream.write(APDUUtils.getTLV(TagConstants.IMAGES_FOR_IRIS_TAG, m_biometricData));
+
+ } else if (Arrays.equals(tag.bytes, TagConstants.ERROR_DETECTION_CODE_TAG)) {
+
+ m_errorDetectionCode = true;
+ m_content.put(tag, value);
+ if (m_biometricData != null)
+ signedContentOutputStream.write(APDUUtils.getTLV(TagConstants.ERROR_DETECTION_CODE_TAG, value));
+
+ } else {
+ s_logger.warn("Unexpected tag: {} with value: {}", Hex.encodeHexString(tag.bytes), Hex.encodeHexString(tlv2.getBytesValue()));
+ }
+ m_cbeffContainer = signedContentOutputStream.toByteArray();
+ }
+ }
+
+ // Break BC tag into Patron CBEFF header + BDB + SB
+ if (m_biometricData != null) {
+ s_logger.info("m_biometricData: {}", Hex.encodeHexString(m_biometricData));
+ //Get Biometric data block (BDB) Length
+ byte[] biometricDataBlockLengthBytes = Arrays.copyOfRange(m_biometricData, 2, 6);
+ //Get Signature block (SB) Length
+ byte[] signatureDataBlockLengthBytes = Arrays.copyOfRange(m_biometricData, 6, 8);
+
+ //Get Biometric Creation Date
+ m_biometricCreationDate = BytesToDateString(Arrays.copyOfRange(m_biometricData, 12, 20));
+ //Get Validity Period From value
+ m_validityPeriodFrom = BytesToDateString(Arrays.copyOfRange(m_biometricData, 20, 28));
+ //Get Validity Period To value
+ m_validityPeriodTo = BytesToDateString(Arrays.copyOfRange(m_biometricData, 28, 36));
+
+ //Convert Biometric data block (BDB) Length byte[] value to int
+ ByteBuffer wrapped = ByteBuffer.wrap(biometricDataBlockLengthBytes);
+ int biometricDataBlockLength = wrapped.getInt();
+
+ //Convert Signature block (SB) Length byte[] value to int
+ wrapped = ByteBuffer.wrap(signatureDataBlockLengthBytes);
+ int signatureDataBlockLength = wrapped.getShort();
+
+
+ m_biometricDataBlock = Arrays.copyOfRange(m_biometricData, 88, 88 + biometricDataBlockLength);
+
+ m_signatureBlock = Arrays.copyOfRange(m_biometricData, 88 + biometricDataBlockLength, 88 + biometricDataBlockLength + signatureDataBlockLength);
+
+ // Decode the ContentInfo and get SignedData objects.
+ ByteArrayInputStream bIn = new ByteArrayInputStream(m_signatureBlock);
+ ASN1InputStream aIn = new ASN1InputStream(bIn);
+ // Set the ContentInfo structure in super class
+ setContentInfo(ContentInfo.getInstance(aIn.readObject())); aIn.close();
+ // Set the CMSSignedData object
+ setAsymmetricSignature(new CMSSignedData(getContentInfo()));
+ // Finally, see if there's a separate signer cert
+ CMSSignedData cmsSignedData = getAsymmetricSignature();
+
+ if(cmsSignedData != null) {
+ signers = cmsSignedData.getSignerInfos();
+
+ for (Iterator i = signers.getSigners().iterator(); i.hasNext();) {
+ signer = i.next();
+ }
+
+ // The biometric data block is the detached signed content
+ setSignedContent(Arrays.copyOfRange(m_biometricData, 0, 88 + biometricDataBlockLength));
+ // Grab signed digest
+ setSignedAttrsDigest(signers);
+ // Precompute digest but don't compare -- let consumers do that
+ setComputedDigest(signer, getSignedContent());
+ // Indicate this object needs a signature verification
+ setSigned(true);
+
+ //Decode the ContentInfo and get SignedData object.
+ Store certs = cmsSignedData.getCertificates();
+ signers = cmsSignedData.getSignerInfos();
+ for (Iterator i = signers.getSigners().iterator(); i.hasNext(); ) {
+ signer = i.next();
+ setDigestAlgorithmName(Algorithm.digAlgOidToNameMap.get(signer.getDigestAlgOID()));
+ setEncryptionAlgorithmName(Algorithm.encAlgOidToNameMap.get(signer.getEncryptionAlgOID()));
+ @SuppressWarnings("unchecked")
+ Collection certCollection = certs.getMatches(signer.getSID());
+ Iterator certIt = certCollection.iterator();
+ if (certIt.hasNext()) {
+ X509CertificateHolder certHolder = certIt.next();
+ // Note that setSignerCert internally increments a counter. If there are more than one
+ // cert in PKCS7 cert bags then the consumer class should throw an exception.
+ X509Certificate signerCert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
+ if (signerCert != null) {
+ setSignerCert(signerCert);
+ setHasOwnSignerCert(true);
+ certFound = true;
+ // Extract signer's signature algorithm name and hang on to it.
+ setSignatureAlgorithmName(signerCert.getSigAlgName());
+ } else {
+ s_logger.error("Can't extract signer certificate");
+ }
+ }
+ }
+ } else {
+ s_logger.error("Null CMSSignedData");
+ }
+ }
+ }
+ }
+ } catch (Exception ex) {
+ s_logger.error("Error parsing {}", APDUConstants.oidNameMap.get(super.getOID()), ex);
+ return false;
+ }
+
+ String message = APDUConstants.oidNameMap.get(super.getOID()) + (certFound ? " had" : " did not have") + " an embedded certificate";
+ s_logger.trace(message);
+
+ if(m_biometricData == null)
+ return false;
+
+ dump(this.getClass());
+ return true;
+ }
+
+
+ /**
+ *
+ * Helper function that converts byte array to a date string
+ *
+ * @param buf Byte array to be converted
+ * @return String containing date value
+ */
+ private String BytesToDateString(byte[] buf) {
+ if((char)buf[buf.length-1] != 'Z') {
+ throw new IllegalArgumentException("bcd byte array doesn't end with Z");
+ }
+ StringBuilder outsb = new StringBuilder();
+ for( int i = 0; i < buf.length-1; ++i ) {
+ int digits = buf[i] & 0xFF;
+ outsb.append(String.format("%02d", digits));
+ }
+ outsb.append('Z');
+ return outsb.toString();
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/CardHolderUniqueIdentifier.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardHolderUniqueIdentifier.java
similarity index 93%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/CardHolderUniqueIdentifier.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardHolderUniqueIdentifier.java
index 3543d81b..fa98b890 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/CardHolderUniqueIdentifier.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/CardHolderUniqueIdentifier.java
@@ -1,4 +1,4 @@
-package gov.gsa.pivconformance.card.client;
+package gov.gsa.pivconformance.cardlib.card.client;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.cms.ContentInfo;
@@ -9,14 +9,14 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import gov.gsa.pivconformance.card.client.Algorithm;
-import gov.gsa.pivconformance.card.client.SignedPIVDataObject;
-import gov.gsa.pivconformance.tlv.BerTag;
-import gov.gsa.pivconformance.tlv.BerTlv;
-import gov.gsa.pivconformance.tlv.BerTlvParser;
-import gov.gsa.pivconformance.tlv.BerTlvs;
-import gov.gsa.pivconformance.tlv.CCTTlvLogger;
-import gov.gsa.pivconformance.tlv.TagConstants;
+import gov.gsa.pivconformance.cardlib.card.client.Algorithm;
+import gov.gsa.pivconformance.cardlib.card.client.SignedPIVDataObject;
+import gov.gsa.pivconformance.cardlib.tlv.BerTag;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlv;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlvParser;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlvs;
+import gov.gsa.pivconformance.cardlib.tlv.CCTTlvLogger;
+import gov.gsa.pivconformance.cardlib.tlv.TagConstants;
import org.apache.commons.codec.binary.Hex;
@@ -321,17 +321,7 @@ public boolean decode() {
// input)
s_logger.warn("Deprecated tag: {} with value: {}", Hex.encodeHexString(tag.bytes),
Hex.encodeHexString(value));
- } else if (Arrays.equals(tag.bytes, TagConstants.DEPRECATED_AUTHENTICATION_KEY_MAP)) { // 3D
- // -
- // Don't
- // use
- // in
- // hash
- // (don't
- // add
- // to
- // digest
- // input)
+ } else if (Arrays.equals(tag.bytes, TagConstants.DEPRECATED_AUTHENTICATION_KEY_MAP)) { // 3D - Dont' use in hash (don't add to digest input)
s_logger.warn("Deprecated tag: {} with value: {}", Hex.encodeHexString(tag.bytes),
Hex.encodeHexString(value));
m_tagList.add(tag); // TODO: Re-visit this strategy
@@ -439,7 +429,7 @@ public boolean decode() {
// Hang the CHUID signer cert here so that any test runner
// consumer can access it.
setChuidSignerCert(signerCert);
- m_x509ArtifactCache.saveObject(APDUConstants.getFileNameForOid(getOID())+ ".cer", signerCert.getEncoded());
+ m_x509ArtifactCache.saveObject("x509-artifacts", APDUConstants.getFileNameForOid(getOID())+ ".cer", signerCert.getEncoded());
} else {
s_logger.error("Can't extract signer certificate");
}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/ChainingAPDUTransmitter.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ChainingAPDUTransmitter.java
similarity index 70%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/ChainingAPDUTransmitter.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ChainingAPDUTransmitter.java
index 36290321..c1e64182 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/ChainingAPDUTransmitter.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ChainingAPDUTransmitter.java
@@ -1,4 +1,4 @@
-package gov.gsa.pivconformance.card.client;
+package gov.gsa.pivconformance.cardlib.card.client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -12,24 +12,24 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import gov.gsa.pivconformance.card.client.APDUConstants;
-import gov.gsa.pivconformance.utils.ITransmitCounter;
-import gov.gsa.pivconformance.utils.PCSCWrapper;
+import gov.gsa.pivconformance.cardlib.card.client.APDUConstants;
+import gov.gsa.pivconformance.cardlib.utils.ITransmitCounter;
+import gov.gsa.pivconformance.cardlib.utils.PCSCWrapper;
// based on logic from the intarsys PCSC wrapper library, adapted to run directly on top of
// javax.smartcardio.pcsc
public class ChainingAPDUTransmitter {
-
+
private CardChannel m_channel = null;
- private static final Logger s_logger = LoggerFactory.getLogger(ChainingAPDUTransmitter.class);
- private static final Logger s_apduLogger = LoggerFactory.getLogger("gov.gsa.pivconformance.apdu");
+ private static final Logger s_logger = LoggerFactory.getLogger(ChainingAPDUTransmitter.class);
+ private static final Logger s_apduLogger = LoggerFactory.getLogger("gov.gsa.pivconformance.cardlib.apdu");
private ITransmitCounter m_counter;
-
+
public ChainingAPDUTransmitter(CardChannel c) {
m_channel = c;
m_counter = PCSCWrapper.getInstance();
}
-
+
protected RequestAPDUWrapper fixLengthExpected(RequestAPDUWrapper request, int correctLE) {
int cla = request.getCla();
int ins = request.getIns();
@@ -42,28 +42,27 @@ protected RequestAPDUWrapper fixLengthExpected(RequestAPDUWrapper request, int c
return new RequestAPDUWrapper(cla, ins, p1, p2, data, correctLE);
}
}
-
+
ResponseAPDUWrapper nativeTransmit(RequestAPDUWrapper request) throws CardException, CardClientException {
CommandAPDU cmd = new CommandAPDU(request.getBytes());
- ResponseAPDU rsp = null;
- try {
-
+ ResponseAPDU rsp = null;
+ try {
+
String apduTrace;
// Mask PIN
- if (cmd.getINS() == APDUConstants.VERIFY) {
- byte[] maskedPin = cmd.getBytes();
- for (int i = 5, end = i + cmd.getNc(); i < end; i++) {
- maskedPin[i] = (byte) 0xAA;
- }
- apduTrace = String.format("Sending Command APDU %s",
- Hex.encodeHexString(maskedPin).replaceAll("..(?=.)", "$0 "));
- } else {
- apduTrace = String.format("Sending Command APDU %s",
- Hex.encodeHexString((cmd.getBytes())).replaceAll("..(?=.)", "$0 "));
- }
- s_apduLogger.debug(apduTrace);
-
- m_counter.incrementTransmitCount();
+ if (cmd.getINS() == APDUConstants.VERIFY) {
+ byte[] maskedPin = cmd.getBytes();
+ for (int i = 5, end = i + cmd.getNc(); i < end; i++) {
+ maskedPin[i] = (byte) 0xAA;
+ }
+ apduTrace = String.format("Sending Command APDU %s", Hex.encodeHexString(maskedPin).replaceAll("..(?=.)", "$0 "));
+ }
+ else {
+ apduTrace = String.format("Sending Command APDU %s", Hex.encodeHexString((cmd.getBytes())).replaceAll("..(?=.)", "$0 "));
+ }
+ s_apduLogger.debug(apduTrace);
+
+ m_counter.incrementTransmitCount();
rsp = m_channel.transmit(cmd);
} catch (CardException e) {
@@ -72,18 +71,22 @@ ResponseAPDUWrapper nativeTransmit(RequestAPDUWrapper request) throws CardExcept
}
return new ResponseAPDUWrapper(rsp.getBytes());
}
-
- protected ResponseAPDUWrapper basicTransmit(RequestAPDUWrapper request) throws CardClientException, CardException {
+
+ protected ResponseAPDUWrapper basicTransmit(RequestAPDUWrapper request)
+ throws CardClientException, CardException {
RequestAPDUWrapper encodedRequest = encodeRequest(request);
- ResponseAPDUWrapper encodedResponse = nativeTransmit(encodedRequest);
+ ResponseAPDUWrapper encodedResponse = nativeTransmit(
+ encodedRequest);
return decodeResponse(encodedResponse);
}
- protected ResponseAPDUWrapper decodeResponse(ResponseAPDUWrapper response) throws CardException {
+ protected ResponseAPDUWrapper decodeResponse(ResponseAPDUWrapper response)
+ throws CardException {
return response;
}
- protected RequestAPDUWrapper encodeRequest(RequestAPDUWrapper request) throws CardException {
+ protected RequestAPDUWrapper encodeRequest(RequestAPDUWrapper request)
+ throws CardException {
return request;
}
@@ -107,7 +110,8 @@ public ResponseAPDUWrapper transmit(RequestAPDUWrapper request) throws CardClien
}
do {
// "GET RESPONSE" command
- RequestAPDUWrapper fixedRequest = new RequestAPDUWrapper(0, 0xC0, 0, 0, response.getSw2());
+ RequestAPDUWrapper fixedRequest = new RequestAPDUWrapper(0, 0xC0, 0, 0,
+ response.getSw2());
response = this.basicTransmit(fixedRequest);
try {
dataBaos.write(response.getData());
@@ -115,7 +119,7 @@ public ResponseAPDUWrapper transmit(RequestAPDUWrapper request) throws CardClien
s_logger.error("Caught exception appending to byte array", e);
throw new CardClientException("Unable to append to byte array", e);
}
- } while (response.getSw1() == 0x61);
+ } while(response.getSw1() == 0x61);
try {
dataBaos.flush();
@@ -125,10 +129,8 @@ public ResponseAPDUWrapper transmit(RequestAPDUWrapper request) throws CardClien
}
byte[] dataBytes = dataBaos.toByteArray();
s_logger.debug("GET RESPONSE: final size: {}", dataBytes.length);
- ResponseAPDUWrapper fixedResponse = new ResponseAPDUWrapper(dataBytes, response.getSw1(),
- response.getSw2());
- s_logger.debug("Returning status {} following GET RESPONSE",
- String.format("%1$02X %2$02X", response.getSw1(), response.getSw2()));
+ ResponseAPDUWrapper fixedResponse = new ResponseAPDUWrapper(dataBytes, response.getSw1(), response.getSw2());
+ s_logger.debug("Returning status {} following GET RESPONSE", String.format("%1$02X %2$02X", response.getSw1(), response.getSw2()));
response = fixedResponse;
}
if (request.isChainedRequest() && request.getNextRequest() != null) {
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ConnectionDescription.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ConnectionDescription.java
new file mode 100644
index 00000000..211dae0d
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ConnectionDescription.java
@@ -0,0 +1,182 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.BerTag;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlv;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.smartcardio.CardException;
+import javax.smartcardio.CardTerminal;
+import javax.smartcardio.TerminalFactory;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+
+import gov.gsa.pivconformance.cardlib.tlv.BerTlvParser;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlvs;
+
+
+/**
+ * Encapsulates a connection description data object (tag 0x7F21) as
+ * defined by SP800-73-4 table 2
+ */
+public class ConnectionDescription {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(ConnectionDescription.class);
+
+ private CardTerminal m_reader;
+
+ //Tag for Connection Description template
+ private static byte[] m_tag = new byte[]{(byte) 0x7F, (byte) 0x21};
+ //Tag for PC/SC device reader name
+ private static byte[] m_tagCRN = new byte[]{(byte) 0x81};
+ //Tag for Local Network node
+ private static byte[] m_tagLocal = new byte[]{(byte) 0x90, (byte) 0x00};
+
+ /**
+ * Default c'tor is private - initialize using static factory methods.
+ */
+ private ConnectionDescription() {
+ }
+
+ /**
+ *
+ * Populate connection description data object based on information from CardTerminal
+ *
+ * @return Byte array containing Connection Description value
+ */
+ public byte[] getBytes() {
+
+ //Get reader name and bytes from the name
+ String readerName = m_reader.getName();
+ byte[] readerNameBytes = readerName.getBytes();
+ int readerNameBytesLen = readerNameBytes.length;
+
+ //Get byte value of reader name length value
+ ByteBuffer bbuf = ByteBuffer.allocate(4);
+ bbuf.putInt(readerNameBytesLen);
+ byte[] readerNameBytesLenBuffer = bbuf.array();
+
+ //Get offset to ignore 0x00 value
+ int readerNameBytesLenBufferOffset = 0;
+ while (readerNameBytesLenBuffer[readerNameBytesLenBufferOffset] == 0x00)
+ readerNameBytesLenBufferOffset++;
+
+ //Calcuate length value for the entire Connection Description Template
+ int readerNameBytesPlusTagLen = readerNameBytesLen + 1 + readerNameBytesLenBuffer.length - readerNameBytesLenBufferOffset + m_tagLocal.length;
+
+ //Get byte value of the total field length
+ ByteBuffer bbuf2 = ByteBuffer.allocate(4);
+ bbuf2.putInt(readerNameBytesPlusTagLen);
+ byte[] readerNameBytesPlusTagLenBuffer = bbuf2.array();
+
+ //Get offset to ignore 0x00 value
+ int readerNameBytesPlusTagLenBufferOffset = 0;
+ while (readerNameBytesPlusTagLenBuffer[readerNameBytesPlusTagLenBufferOffset] == 0x00)
+ readerNameBytesPlusTagLenBufferOffset++;
+
+
+ ByteArrayOutputStream bufOut = new ByteArrayOutputStream();
+ try {
+ bufOut.write(m_tag);
+ bufOut.write(readerNameBytesPlusTagLenBuffer, readerNameBytesLenBufferOffset, readerNameBytesPlusTagLenBuffer.length - readerNameBytesPlusTagLenBufferOffset);
+ bufOut.write(m_tagCRN);
+ bufOut.write(readerNameBytesLenBuffer, readerNameBytesLenBufferOffset, readerNameBytesLenBuffer.length - readerNameBytesLenBufferOffset);
+ bufOut.write(readerNameBytes);
+ bufOut.write(m_tagLocal);
+ } catch(IOException e) {
+ s_logger.error("Failed to write to buffer", e);
+ return null;
+ }
+ return bufOut.toByteArray();
+ }
+
+ /**
+ *
+ * Create a ConnectionDescription object from a javax.smartcardio.CardTerminal
+ *
+ * @return ConnectionDescription used to interact with a PIV card in the specified terminal
+ */
+ public static ConnectionDescription createFromTerminal(CardTerminal reader) {
+ ConnectionDescription rv = new ConnectionDescription();
+ rv.m_reader = reader;
+ return rv;
+ }
+
+ /**
+ *
+ * Given the data object described in SP800-73-4 table 2, create a new connection description object
+ *
+ * @param data Byte array containing Connection Description info from which ConnectionDescription object will be constructed
+ * @return ConnectionDescription Object
+ */
+ public static ConnectionDescription createFromBuffer(byte[] data) {
+ ConnectionDescription rv = new ConnectionDescription();
+
+ byte readerNameBytes[] = null;
+
+ BerTag berTagCRN = new BerTag(m_tagCRN);
+ BerTag berTag = new BerTag(m_tag);
+
+ BerTlvParser parser = new BerTlvParser();
+ BerTlvs tlvs = parser.parse(data, 0, data.length);
+ BerTlv crnTlv = tlvs.find(berTag);
+ BerTlv crnTlvCRN = tlvs.find(berTagCRN);
+
+ if(crnTlv == null){
+ s_logger.error("Unable to find tag for ConnectionDescription");
+ return null;
+ }
+
+ if(crnTlvCRN != null)
+ readerNameBytes = crnTlvCRN.getBytesValue();
+ else {
+ s_logger.error("Unable to find card reader name in the ConnectionDescription value");
+ return null;
+ }
+
+ if(readerNameBytes == null){
+ s_logger.error("Unable to find card reader name in the ConnectionDescription value");
+ return null;
+ }
+
+ try {
+
+ TerminalFactory tf2 = TerminalFactory.getDefault();
+ s_logger.info("Attempting to list card terminals");
+ try {
+ for (CardTerminal ct : tf2.terminals().list()) {
+
+ if(Arrays.equals(readerNameBytes, ct.getName().getBytes())) {
+
+ rv.m_reader = ct;
+ }
+ }
+ } catch (CardException e) {
+ s_logger.error("Unable to enumerate card terminals", e);
+ return null;
+ }
+
+
+
+ }catch (Exception ex) {
+
+ s_logger.info("Exception : {}", ex.getMessage());
+ }
+
+ return rv;
+ }
+
+ /**
+ *
+ * Get the reader that will be used to actually send/receive APDUs from the card
+ *
+ * @return CardTerminal object
+ */
+ public CardTerminal getTerminal() {
+ return m_reader;
+ }
+
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/ContainerPurpose.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ContainerPurpose.java
similarity index 54%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/ContainerPurpose.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ContainerPurpose.java
index fb1fbded..feb24331 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/ContainerPurpose.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ContainerPurpose.java
@@ -1,8 +1,7 @@
-package gov.gsa.pivconformance.card.client;
+package gov.gsa.pivconformance.cardlib.card.client;
public enum ContainerPurpose {
SIGNATURE,
- // we may want to differentiate between key agreement and key exchange here, but
- // this is currently more expedient
+ // we may want to differentiate between key agreement and key exchange here, but this is currently more expedient
ENCRYPTION
}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/DataModelSingleton.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/DataModelSingleton.java
similarity index 50%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/DataModelSingleton.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/DataModelSingleton.java
index 79e6bcce..4138a036 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/DataModelSingleton.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/DataModelSingleton.java
@@ -1,108 +1,107 @@
-/**
- *
- */
-package gov.gsa.pivconformance.card.client;
-
-import java.security.cert.X509Certificate;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import gov.gsa.pivconformance.tlv.*;
-
-/**
- * This class is used indirectly by the test atoms. It instantiates some known
- * SP 800-73 rules. Initially being used to manage the container lengths in
- * Tables 8-43.
- *
- */
-
-public class DataModelSingleton {
- private static final Logger s_logger = LoggerFactory.getLogger(DataModelSingleton.class);
-
- TagBoundaryManager m_tagLengthRules;
- X509Certificate m_chuidSignerCert;
-
- private DataModelSingleton() {
- reset();
- }
-
- /*
- * The INSTANCE
- */
-
- private static final DataModelSingleton INSTANCE = new DataModelSingleton();
-
- /**
- * Public accessor of this so it can be instantiated by the framework
- *
- * @return
- */
-
- public static DataModelSingleton getInstance() {
- return INSTANCE;
- }
-
- /**
- * Reset
- */
-
- public void reset() {
- m_tagLengthRules = null;
- m_tagLengthRules = new TagBoundaryManager();
- m_chuidSignerCert = null;
- }
-
- /**
- * Initialize
- */
-
- public void loadLengthRules() {
- if (m_tagLengthRules == null)
- m_tagLengthRules = new TagBoundaryManager();
- }
-
- /**
- * Day-to-day public accessor
- *
- * @return the SP 800-73-* tag length rules
- */
-
- public TagBoundaryManager getLengthRules() {
- if (m_tagLengthRules == null)
- loadLengthRules();
-
- return m_tagLengthRules;
- }
-
- /**
- * Setter
- *
- * @param clf
- */
-
- public void setLengthRules(TagBoundaryManager clf) {
- m_tagLengthRules = clf;
- }
-
- /**
- * Gets the cached CHUID signer cert
- *
- * @return the cached CHUID signer cert
- */
-
- public X509Certificate getChuidSignerCert() {
- return m_chuidSignerCert;
- }
-
- /**
- * Sets the cached CHUID signer cert
- *
- * @param cert
- */
-
- public void setChuidSignerCert(X509Certificate cert) {
- if (m_chuidSignerCert == null && cert != null) // Set once and protect
- m_chuidSignerCert = cert;
- }
+/**
+ *
+ */
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import java.security.cert.X509Certificate;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import gov.gsa.pivconformance.cardlib.tlv.*;
+
+/**
+ * This class is used indirectly by the test atoms. It instantiates some
+ * known SP 800-73 rules. Initially being used to manage the container lengths
+ * in Tables 8-43.
+ *
+ */
+
+public class DataModelSingleton {
+ private static final Logger s_logger = LoggerFactory.getLogger(DataModelSingleton.class);
+
+ TagBoundaryManager m_tagLengthRules;
+ X509Certificate m_chuidSignerCert;
+
+ private DataModelSingleton() {
+ reset();
+ }
+
+ /*
+ * The INSTANCE
+ */
+
+ private static final DataModelSingleton INSTANCE = new DataModelSingleton();
+
+ /**
+ * Public accessor of this so it can be instantiated by the framework
+ *
+ * @return
+ */
+
+ public static DataModelSingleton getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Reset
+ */
+
+ public void reset() {
+ m_tagLengthRules = null;
+ m_tagLengthRules = new TagBoundaryManager();
+ m_chuidSignerCert = null;
+ }
+
+ /**
+ * Initialize
+ */
+
+ public void loadLengthRules() {
+ if (m_tagLengthRules == null)
+ m_tagLengthRules = new TagBoundaryManager();
+ }
+
+ /**
+ * Day-to-day public accessor
+ *
+ * @return the SP 800-73-* tag length rules
+ */
+
+ public TagBoundaryManager getLengthRules() {
+ if (m_tagLengthRules == null)
+ loadLengthRules();
+
+ return m_tagLengthRules;
+ }
+
+ /**
+ * Setter
+ *
+ * @param clf
+ */
+
+ public void setLengthRules(TagBoundaryManager clf) {
+ m_tagLengthRules = clf;
+ }
+
+ /**
+ * Gets the cached CHUID signer cert
+ *
+ * @return the cached CHUID signer cert
+ */
+
+ public X509Certificate getChuidSignerCert() {
+ return m_chuidSignerCert;
+ }
+
+ /**
+ * Sets the cached CHUID signer cert
+ * @param cert
+ */
+
+ public void setChuidSignerCert(X509Certificate cert) {
+ if (m_chuidSignerCert == null && cert != null) // Set once and protect
+ m_chuidSignerCert = cert;
+ }
}
\ No newline at end of file
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/DefaultPIVApplication.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/DefaultPIVApplication.java
new file mode 100644
index 00000000..baef028e
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/DefaultPIVApplication.java
@@ -0,0 +1,53 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import org.apache.commons.codec.binary.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A default implementation of the PIV application interface that will be used by the test harness in most cases.
+ */
+public class DefaultPIVApplication extends AbstractPIVApplication {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(DefaultPIVApplication.class);
+
+// @Override
+// public MiddlewareStatus pivEstablishSecureMessaging(CardHandle cardHandle) {
+// return null;
+// }
+
+// @Override
+// public MiddlewareStatus pivPutData(CardHandle cardHandle, String OID, PIVDataObject data) {
+// return null;
+// }
+
+ /**
+ *
+ * Set the PIV Card Application as the currently selected card application and establish
+ * the PIV Card Applicationâs security state.
+ *
+ * @param cardHandle CardHandle object that encapsulates connection to a card
+ * @param applicationAID ApplicationAID object containing the AID of the PIV Card Application
+ * @param applicationProperties ApplicationProperties object containing application properties of the selected PIV
+ * Card Application
+ * @return
+ */
+ @Override
+ public MiddlewareStatus pivSelectCardApplication(CardHandle cardHandle, ApplicationAID applicationAID, ApplicationProperties applicationProperties) {
+ s_logger.debug("pivSelectCardApplication()");
+ // For now, if the caller did not specify an AID, use the default.
+ byte[] aid = applicationAID.getBytes();
+ if(aid == null) {
+ s_logger.info("Using default AID ({}) to select PIV application", Hex.encodeHexString(APDUConstants.PIV_APPID));
+ applicationAID.setBytes(APDUConstants.PIV_APPID);
+ }
+ MiddlewareStatus rv = super.pivSelectCardApplication(cardHandle, applicationAID, applicationProperties);
+ s_logger.debug("pivSelectCardApplication() returning {}", rv);
+ return rv;
+ }
+
+// @Override
+// public MiddlewareStatus pivGenerateKeyPair(CardHandle cardHandle, byte keyReference, byte cryptographicMechanism, PIVDataObject publicKey) {
+// return null;
+// }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/DiscoveryObject.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/DiscoveryObject.java
new file mode 100644
index 00000000..a29bc589
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/DiscoveryObject.java
@@ -0,0 +1,273 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.*;
+import org.apache.commons.codec.binary.Hex;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+
+/**
+ *
+ * Encapsulates a Discovery Object data object as defined by SP800-73-4 Part 2 Appendix A Table 18
+ *
+ */
+public class DiscoveryObject extends PIVDataObject {
+
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(DiscoveryObject.class);
+
+ private byte[] m_aid;
+ private byte[] m_pinPolicy;
+ private boolean m_globalPINSatisfiesACR;
+ private boolean m_appPINSatisfiesACR;
+ private boolean m_globalPINisPrimary;
+ private boolean m_occSatisfiesACR;
+ private byte[] m_signedContent;
+
+ /**
+ * DiscoveryObject class constructor, initializes all the class fields.
+ */
+ public DiscoveryObject() {
+ m_aid = null;
+ m_pinPolicy = null;
+ m_signedContent = null;
+ m_content = new HashMap();
+ }
+
+ /**
+ *
+ * Returns byte array with signed content
+ *
+ * @return Byte array with signed content buffer
+ */
+ public byte[] getSignedContent() {
+ return m_signedContent;
+ }
+
+ /**
+ *
+ * Sets the signed content value
+ *
+ * @param signedContent Byte array with signed content buffer
+ */
+ public void setSignedContent(byte[] signedContent) {
+ m_signedContent = signedContent;
+ }
+
+ /**
+ *
+ * Returns PIV Card Application AID value
+ *
+ * @return Byte array containing PIV Card Application AID value
+ */
+ public byte[] getAid() {
+ return m_aid;
+ }
+
+ /**
+ *
+ * Sets the PIV Card Application AID value
+ *
+ * @param aid Byte array containing PIV Card Application AID value
+ */
+ public void setAid(byte[] aid) {
+ m_aid = aid;
+ }
+
+ /**
+ *
+ * Returns PIN Usage Policy value
+ *
+ * @return Byte array containing PIN Usage Policy value
+ */
+ public byte[] getPinPolicy() {
+ return m_pinPolicy;
+ }
+
+ /**
+ *
+ * Sets the PIN Usage Policy value
+ *
+ * @param pinPolicy Byte array containing PIN Usage Policy value
+ */
+ public void setPinPolicy(byte[] pinPolicy) {
+ m_pinPolicy = pinPolicy;
+ }
+
+
+ /**
+ *
+ * Returns true if Global PIN satisfies the PIV ACRs, false otherwise
+ *
+ * @return True if Global PIN satisfies the PIV ACRs, false otherwise
+ */
+ public boolean globalPINSatisfiesACR() {
+ return m_globalPINSatisfiesACR;
+ }
+
+ /**
+ *
+ * Sets if Global PIN satisfies the PIV ACRs, false otherwise
+ *
+ * @param globalPINSatisfiesACR True if Global PIN satisfies the PIV ACRs, false otherwise
+ */
+ public void setGlobalPINSatisfiesACR(boolean globalPINSatisfiesACR) {
+ m_globalPINSatisfiesACR = globalPINSatisfiesACR;
+ }
+
+ /**
+ *
+ * Returns true if Global PIN is primary, false otherwise
+ *
+ * @return True if Global PIN is primary, false otherwise
+ */
+ public boolean globalPINisPrimary() {
+ return m_globalPINisPrimary;
+ }
+
+ /**
+ *
+ * Sets if Global PIN is primary
+ *
+ * @param globalPINisPrimary True if Global PIN is primary, false otherwise
+ */
+ public void setGlobalPINisPrimary(boolean globalPINisPrimary) {
+ m_globalPINisPrimary = globalPINisPrimary;
+ }
+
+ // XXX *** MOVE this
+
+ /**
+ *
+ * Helper function to determine if byte if set at a given position
+ *
+ * @param field Byte value
+ * @param position Integer specifying the position to check
+ * @return True if set, false otherwise
+ */
+ private boolean is_set(byte field, int position) {
+ return ((field >> position) & 1) == 1;
+ }
+
+ /**
+ *
+ * Returns true if App PIN satisfies the PIV ACRs, false otherwise
+ *
+ * @return True if App PIN satisfies the PIV ACRs, false otherwise
+ */
+ public boolean appPINSatisfiesACR() {
+ return m_appPINSatisfiesACR;
+ }
+
+ /**
+ *
+ * Sets if App PIN satisfies the PIV ACRs
+ *
+ * @param appPINSatisfiesACR True if App PIN satisfies the PIV ACRs, false otherwise
+ */
+ public void setAppPINSatisfiesACR(boolean appPINSatisfiesACR) {
+ m_appPINSatisfiesACR = appPINSatisfiesACR;
+ }
+
+ /**
+ *
+ * Returns true if OCC satisfies the PIV ACRs, false otherwise
+ *
+ * @return True if OCC satisfies the PIV ACRs, false otherwise
+ */
+ public boolean occSatisfiesACR() {
+ return m_occSatisfiesACR;
+ }
+
+ /**
+ *
+ * Sets if OCC satisfies the PIV ACRs
+ *
+ * @param occSatisfiesACR True if OCC satisfies the PIV ACRs, false otherwise
+ */
+ public void setOccSatisfiesACR(boolean occSatisfiesACR) {
+ m_occSatisfiesACR = occSatisfiesACR;
+ }
+
+ /**
+ *
+ * Decode function that decodes Discovery Object object retrieved from the card and populates various class fields.
+ *
+ * @return True if decode was successful, false otherwise
+ */
+ @Override
+ public boolean decode() {
+ byte[] rawBytes = this.getBytes();
+ s_logger.trace("rawBytes: {}", Hex.encodeHexString(rawBytes));
+ if(rawBytes.length == 0) {
+ s_logger.info("DiscoveryObject.decode() called for empty discovery object.");
+ return false;
+ }
+
+ try {
+ BerTlvParser tlvp = new BerTlvParser(new CCTTlvLogger(this.getClass()));
+ BerTlv outer = tlvp.parseConstructed(rawBytes);
+
+ ByteArrayOutputStream scos = new ByteArrayOutputStream();
+ for (BerTlv tlv : outer.getValues()) {
+ byte[] tag = tlv.getTag().bytes;
+ super.m_tagList.add(tlv.getTag());
+ if (Arrays.equals(tag, TagConstants.PIV_CARD_APPLICATION_AID_TAG)) {
+
+ m_aid = tlv.getBytesValue();
+ m_content.put(tlv.getTag(), tlv.getBytesValue());
+
+ scos.write(APDUUtils.getTLV(TagConstants.PIV_CARD_APPLICATION_AID_TAG, m_aid));
+ } else if (Arrays.equals(tag, TagConstants.PIN_USAGE_POLICY_TAG)) {
+
+ m_pinPolicy = tlv.getBytesValue();
+ m_content.put(tlv.getTag(), tlv.getBytesValue());
+
+ scos.write(APDUUtils.getTLV(TagConstants.PIN_USAGE_POLICY_TAG, m_pinPolicy));
+
+ m_globalPINisPrimary = false;
+ m_globalPINSatisfiesACR = false;
+ m_appPINSatisfiesACR = false;
+ m_occSatisfiesACR = false;
+
+ if (is_set(m_pinPolicy[0], 8)) {
+ s_logger.error("PIN Policy bit 8 was set");
+ }
+ if (is_set(m_pinPolicy[0], 7)) {
+ m_appPINSatisfiesACR = true;
+ }
+ if (is_set(m_pinPolicy[0], 6)) {
+ m_globalPINSatisfiesACR = true;
+ if (m_pinPolicy[1] == 0x20) {
+ m_globalPINisPrimary = true;
+ }
+ }
+ if (is_set(m_pinPolicy[0], 5)) {
+ m_occSatisfiesACR = true;
+ }
+
+ } else {
+ s_logger.warn("Unexpected tag: {} with value: {}", Hex.encodeHexString(tlv.getTag().bytes), Hex.encodeHexString(tlv.getBytesValue()));
+ }
+ }
+
+ m_signedContent = scos.toByteArray();
+
+ } catch (Exception ex) {
+
+ s_logger.error("Error parsing {}: {}", APDUConstants.oidNameMap.get(super.getOID()), ex.getMessage());
+ return false;
+ }
+
+ if(m_aid == null || m_pinPolicy == null)
+ return false;
+
+ dump(this.getClass())
+;
+ return true;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/GeneralAuthenticateHelper.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/GeneralAuthenticateHelper.java
similarity index 55%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/GeneralAuthenticateHelper.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/GeneralAuthenticateHelper.java
index 3588ab90..d2fb0329 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/GeneralAuthenticateHelper.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/GeneralAuthenticateHelper.java
@@ -1,4 +1,4 @@
-package gov.gsa.pivconformance.card.client;
+package gov.gsa.pivconformance.cardlib.card.client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -26,14 +26,14 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import gov.gsa.pivconformance.tlv.BerTag;
-import gov.gsa.pivconformance.tlv.BerTlv;
-import gov.gsa.pivconformance.tlv.BerTlvBuilder;
-import gov.gsa.pivconformance.tlv.BerTlvParser;
-import gov.gsa.pivconformance.tlv.BerTlvs;
-import gov.gsa.pivconformance.tlv.CCTTlvLogger;
-import gov.gsa.pivconformance.utils.NullParameters;
-import gov.gsa.pivconformance.utils.PCSCWrapper;
+import gov.gsa.pivconformance.cardlib.tlv.BerTag;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlv;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlvBuilder;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlvParser;
+import gov.gsa.pivconformance.cardlib.tlv.BerTlvs;
+import gov.gsa.pivconformance.cardlib.tlv.CCTTlvLogger;
+import gov.gsa.pivconformance.cardlib.utils.NullParameters;
+import gov.gsa.pivconformance.cardlib.utils.PCSCWrapper;
// Moving the general authenticate stuff that needs command
// chaining off to here, so it's not sitting in the wrapper
@@ -45,12 +45,12 @@
public class GeneralAuthenticateHelper {
static Logger s_logger = LoggerFactory.getLogger(GeneralAuthenticateHelper.class);
-
+
public static final byte[] DYNAMIC_AUTHENTICATION_TEMPLATE = { (byte) 0x7C };
- public static final byte[] GA_CHALLENGE = { (byte) 0x81 };
- public static final byte[] GA_RESPONSE = { (byte) 0x82 };
- public static final int APDU_MAX = 255;
- public static final int APDU_MAX_DATA = APDU_MAX - 5;
+ public static final byte[] GA_CHALLENGE = { (byte) 0x81 };
+ public static final byte[] GA_RESPONSE = { (byte) 0x82 };
+ public static final int APDU_MAX = 255;
+ public static final int APDU_MAX_DATA = APDU_MAX - 5;
public static byte[] generateRequest(String jceKeyType, String containerOid, byte[] paddedChallenge) {
BerTlvBuilder b = new BerTlvBuilder();
@@ -63,9 +63,8 @@ public static byte[] generateRequest(String jceKeyType, String containerOid, byt
s_logger.debug("Generated challenge for {}: {}", containerOid, Hex.encodeHexString(template));
return template;
}
-
- public static ResponseAPDU sendRequest(CardHandle ch, int pivAlgId, int pivKeyId, byte[] request)
- throws CardClientException {
+
+ public static ResponseAPDU sendRequest(CardHandle ch, int pivAlgId, int pivKeyId, byte[] request) throws CardClientException {
CardChannel channel = ch.getCurrentChannel();
s_logger.debug("sendRequest called for container {} alg {} request length {}", request.length);
// here's where the chaining lands until it's worn better
@@ -75,13 +74,12 @@ public static ResponseAPDU sendRequest(CardHandle ch, int pivAlgId, int pivKeyId
ccBaos.write(APDUConstants.GENERAL_AUTHENTICATE);
ccBaos.write(pivAlgId);
ccBaos.write(pivKeyId);
- ccBaos.write(request.length <= APDU_MAX_DATA ? request.length : APDU_MAX_DATA);
+ ccBaos.write(request.length <= APDU_MAX_DATA ? request.length : APDU_MAX_DATA );
ccBaos.write(request, 0, request.length <= APDU_MAX_DATA ? request.length : APDU_MAX_DATA);
- if (request.length > APDU_MAX_DATA)
- currPos += APDU_MAX_DATA;
+ if(request.length > APDU_MAX_DATA) currPos += APDU_MAX_DATA;
CommandAPDU generalAuthApdu = new CommandAPDU(ccBaos.toByteArray());
ResponseAPDU resp = null;
-
+
try {
PCSCWrapper pcsc = PCSCWrapper.getInstance();
resp = pcsc.transmit(channel, generalAuthApdu);
@@ -89,24 +87,23 @@ public static ResponseAPDU sendRequest(CardHandle ch, int pivAlgId, int pivKeyId
s_logger.error("Failed to transmit GENERAL AUTHENTICATE APDU to card", e);
return null;
}
- if (currPos < request.length - 1) {
- while (resp.getSW1() == 0x90 && resp.getSW2() == 0x00 && currPos < request.length - 1) {
+ if(currPos < request.length - 1) {
+ while(resp.getSW1() == 0x90 && resp.getSW2() == 0x00 && currPos < request.length - 1) {
ccBaos.reset();
- ccBaos.write(
- request.length - currPos <= APDU_MAX_DATA ? APDUConstants.COMMAND : APDUConstants.COMMAND_CC);
+ ccBaos.write(request.length - currPos <= APDU_MAX_DATA ? APDUConstants.COMMAND : APDUConstants.COMMAND_CC);
ccBaos.write(APDUConstants.GENERAL_AUTHENTICATE);
ccBaos.write(pivAlgId);
ccBaos.write(pivKeyId);
- ccBaos.write(request.length - currPos <= APDU_MAX_DATA ? request.length - currPos : APDU_MAX_DATA);
- ccBaos.write(request, currPos,
- request.length - currPos <= APDU_MAX_DATA ? request.length - currPos : APDU_MAX_DATA);
- if (request.length > APDU_MAX_DATA) {
+ ccBaos.write(request.length - currPos <= APDU_MAX_DATA ? request.length - currPos : APDU_MAX_DATA );
+ ccBaos.write(request, currPos, request.length - currPos <= APDU_MAX_DATA
+ ? request.length - currPos : APDU_MAX_DATA);
+ if(request.length > APDU_MAX_DATA) {
currPos += APDU_MAX_DATA;
} else {
currPos = request.length;
}
- if (currPos >= request.length - 1)
- ccBaos.write((byte) 0x00); // Add Le
+ if(currPos >= request.length - 1)
+ ccBaos.write((byte)0x00); //Add Le
try {
CommandAPDU chainedGeneralAuthApdu = new CommandAPDU(ccBaos.toByteArray());
PCSCWrapper pcsc = PCSCWrapper.getInstance();
@@ -117,73 +114,70 @@ public static ResponseAPDU sendRequest(CardHandle ch, int pivAlgId, int pivKeyId
}
}
}
- if (resp.getSW1() != 0x90 && resp.getSW2() != 0x00) {
- s_logger.error("Got status code of {}{} for GENERAL AUTHENTICATE", Integer.toHexString(resp.getSW1()),
- Integer.toHexString(resp.getSW2()));
+ if(resp.getSW1() != 0x90 && resp.getSW2() != 0x00) {
+ s_logger.error("Got status code of {}{} for GENERAL AUTHENTICATE",
+ Integer.toHexString(resp.getSW1()), Integer.toHexString(resp.getSW2()));
}
return resp;
}
-
+
// this should live in one of the data model classes
public static byte[] getChallengeResponseFromData(byte[] apduData) {
- if (apduData == null || apduData.length == 0) {
+ if(apduData == null || apduData.length == 0) {
s_logger.error("null or empty APDU data was passed in.");
return null;
}
- BerTlvParser tlvp = new BerTlvParser(new CCTTlvLogger(GeneralAuthenticateHelper.class));
- BerTlvs outer = tlvp.parse(apduData);
- List outerValues = outer.getList();
- BerTlvs inner = tlvp.parse(outerValues.get(0).getBytesValue());
- List values = inner.getList();
- byte[] rv = null;
- BerTag responseTag = new BerTag(GA_RESPONSE);
- for (BerTlv tlv : values) {
- if (tlv.getTag().equals(responseTag)) {
- rv = tlv.getBytesValue();
- break;
- }
- }
- return rv;
+ BerTlvParser tlvp = new BerTlvParser(new CCTTlvLogger(GeneralAuthenticateHelper.class));
+ BerTlvs outer = tlvp.parse(apduData);
+ List outerValues = outer.getList();
+ BerTlvs inner = tlvp.parse(outerValues.get(0).getBytesValue());
+ List values = inner.getList();
+ byte[] rv = null;
+ BerTag responseTag = new BerTag(GA_RESPONSE);
+ for(BerTlv tlv: values) {
+ if(tlv.getTag().equals(responseTag)) {
+ rv = tlv.getBytesValue();
+ break;
+ }
+ }
+ return rv;
}
-
+
// these should be factored out into alg-specific helpers
- // digest a challenge using the specified digest OID and format it into a PKCS#1
- // v1.5 padded message
+ // digest a challenge using the specified digest OID and format it into a PKCS#1 v1.5 padded message
public static byte[] preparePKCS1Challenge(byte[] challenge, String digestOid, int modulusLen) {
- String jceDigestName = MessageDigestUtils.getDigestName(new ASN1ObjectIdentifier(digestOid));
- byte[] challengeDigest = null;
- try {
- challengeDigest = MessageDigest.getInstance(jceDigestName, "BC").digest(challenge);
- } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
- s_logger.error("Unable to digest challenge", e);
- return null;
- }
- s_logger.debug("Challenge: {}", Hex.encodeHexString(challenge));
- s_logger.debug("{} ({}) digest of challenge: {}", digestOid, jceDigestName,
- Hex.encodeHexString(challengeDigest));
-
- AlgorithmIdentifier digestAlgId = new AlgorithmIdentifier(new ASN1ObjectIdentifier(digestOid),
- new NullParameters());
- DigestInfo formattedDigest = new DigestInfo(digestAlgId, challengeDigest);
- byte[] diBuf = null;
- try {
- diBuf = formattedDigest.getEncoded();
- } catch (IOException e) {
- s_logger.error("Unable to encode DigestInfo structure for PKCS#1 signature block", e);
- return null;
- }
- return padDigestInfo(diBuf, modulusLen);
+ String jceDigestName = MessageDigestUtils.getDigestName(new ASN1ObjectIdentifier(digestOid));
+ byte[] challengeDigest = null;
+ try {
+ challengeDigest = MessageDigest.getInstance(jceDigestName, "BC").digest(challenge);
+ } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
+ s_logger.error("Unable to digest challenge", e);
+ return null;
+ }
+ s_logger.debug("Challenge: {}", Hex.encodeHexString(challenge));
+ s_logger.debug("{} ({}) digest of challenge: {}", digestOid, jceDigestName, Hex.encodeHexString(challengeDigest));
+
+ AlgorithmIdentifier digestAlgId = new AlgorithmIdentifier(new ASN1ObjectIdentifier(digestOid), new NullParameters());
+ DigestInfo formattedDigest = new DigestInfo(digestAlgId, challengeDigest);
+ byte[] diBuf = null;
+ try {
+ diBuf = formattedDigest.getEncoded();
+ } catch(IOException e) {
+ s_logger.error("Unable to encode DigestInfo structure for PKCS#1 signature block", e);
+ return null;
+ }
+ return padDigestInfo(diBuf, modulusLen);
}
// pad an encoded DigestInfo structure
- // based on steps in section 9.2 of RFC 3447
- public static byte[] padDigestInfo(byte[] digest, int modulusLen) {
- byte[] PS = new byte[modulusLen - digest.length - 3];
- Arrays.fill(PS, (byte) 0xff);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- baos.write(0x00);
- baos.write(0x01);
- try {
+ // based on steps in section 9.2 of RFC 3447
+ public static byte[] padDigestInfo(byte[] digest, int modulusLen) {
+ byte[] PS = new byte[modulusLen - digest.length - 3];
+ Arrays.fill(PS, (byte)0xff);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(0x00);
+ baos.write(0x01);
+ try {
baos.write(PS);
baos.write(0x00);
baos.write(digest);
@@ -191,25 +185,24 @@ public static byte[] padDigestInfo(byte[] digest, int modulusLen) {
s_logger.error("Unexpected error generating padded buffer", e);
return null;
}
- return baos.toByteArray();
- }
+ return baos.toByteArray();
+ }
- public static byte[] generateChallenge(int size) {
- SecureRandom rng;
+ public static byte[] generateChallenge(int size) {
+ SecureRandom rng;
try {
rng = SecureRandom.getInstanceStrong();
} catch (NoSuchAlgorithmException e) {
s_logger.error("Unable to instantiate RNG", e);
return null;
}
- byte[] challenge = new byte[size];
- rng.nextBytes(challenge);
- s_logger.debug("Challenge bytes: {}", Hex.encodeHexString(challenge));
- return challenge;
- }
-
- public static boolean verifyResponseSignature(String jceSignatureAlgName, PublicKey containerCertKey,
- byte[] signature, byte[] challenge) {
+ byte[] challenge = new byte[size];
+ rng.nextBytes(challenge);
+ s_logger.debug("Challenge bytes: {}", Hex.encodeHexString(challenge));
+ return challenge;
+ }
+
+ public static boolean verifyResponseSignature(String jceSignatureAlgName, PublicKey containerCertKey, byte[] signature, byte[] challenge) {
boolean verified = false;
try {
Signature verifier = Signature.getInstance(jceSignatureAlgName);
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/IPIVApplication.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/IPIVApplication.java
new file mode 100644
index 00000000..be4d534b
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/IPIVApplication.java
@@ -0,0 +1,101 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+/**
+ * This interface encapsulates the entry points for data access from SP800-73.
+ *
+ * Defined as an interface to allow per-card implementations to differ.
+ *
+ */
+public interface IPIVApplication {
+
+ /**
+ * pivSelectCardApplication from SP800-73-4 part 3 section 3.2.1
+ *
+ * @param cardHandle Opaque identifier of the card to be acted upon as
+ * returned by pivConnect.
+ * @param applicationAID The AID of the PIV Card Application that is to
+ * become the currently selected card application.
+ * @param applicationProperties The application properties of the selected PIV
+ * Card Application. See Part 2, Table 3.
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ MiddlewareStatus pivSelectCardApplication(CardHandle cardHandle, ApplicationAID applicationAID, ApplicationProperties applicationProperties);
+
+ /**
+ * pivEstablishSecureMessaging from SP800-73-4 part 3 section 3.2.2
+ * @param cardHandle Opaque identifier of the card to be acted upon as
+ * * returned by pivConnect.
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ MiddlewareStatus pivEstablishSecureMessaging(CardHandle cardHandle);
+
+ /**
+ * pivLogIntoCardApplication from SP800-73-4 part 3 section 3.2.3
+ *
+ * @param cardHandle Opaque identifier of the card to be acted upon as returned by pivConnect.
+ * @param authenticators A sequence of zero or more BER-TLV encoded authenticators to be used to authenticate and set security
+ * state/status in the PIV Card Application contex
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ MiddlewareStatus pivLogIntoCardApplication(CardHandle cardHandle, byte[] authenticators);
+
+ /**
+ * pivGetData from SP800-73-4 part 3 section 3.2.4
+ * @param cardHandle Opaque identifier of the card to be acted upon as returned by pivConnect.
+ * @param OID Object identifier of the object whose data content is to be
+ * retrieved coded as a string
+ * @param data Retrieved data content stored in PIVDataObject object
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ MiddlewareStatus pivGetData(CardHandle cardHandle, String OID, PIVDataObject data);
+
+ /**
+ * pivLogoutOfCardApplication from SP800-73-4 part 3 section 3.2.5 - reset application security status of PIV card application
+ *
+ * @param cardHandle Opaque identifier of the card to be acted upon as returned by pivConnect.
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ MiddlewareStatus pivLogoutOfCardApplication(CardHandle cardHandle);
+
+ /**
+ * pivCrypt from SP800-73-4 part 3 section 3.3.1
+ *
+ * @param cardHandle Opaque identifier of the card to be acted upon as returned by pivConnect.
+ * @param algorithmIdentifier Identifier of the cryptographic algorithm to be used for
+ * the cryptographic operation.
+ * @param keyReference Identifier of the on-card key to be used for the
+ * cryptographic operation.
+ * @param algorithmInput Sequence of bytes used as the input to the cryptographic
+ * operation stored in PIVDataObject object.
+ * @param algorithmOutput Sequence of bytes output by the cryptographic operation stored in PIVDataObject object.
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ MiddlewareStatus pivCrypt(CardHandle cardHandle, byte algorithmIdentifier, byte keyReference, PIVDataObject algorithmInput, PIVDataObject algorithmOutput);
+
+ /**
+ *
+ * pivPutData from SP800-73-4 part 3 section 3.4.1
+ *
+ * @param cardHandle Opaque identifier of the card to be acted upon as returned by pivConnect.
+ * @param OID Object identifier of the object whose data content is to be
+ * replaced coded as a String.
+ * @param data Data to be used to replace in its entirety the data content
+ * of the named data object stored in PIVDataObject object
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ MiddlewareStatus pivPutData(CardHandle cardHandle, String OID, PIVDataObject data);
+
+
+ /**
+ *
+ * pivGenerateKeyPair from SP800-73-4 part 3 section 3.4.2
+ *
+ * @param cardHandle Opaque identifier of the card to be acted upon as returned by pivConnect.
+ * @param keyReference The key reference of the generated key pair.
+ * @param cryptographicMechanism The type of key pair to be generated.
+ * @param publicKey BER-TLV data objects defining the public key
+ * of the generated key pair stored in PIVDataObject object.
+ * @return MiddlewareStatus value indicating the result of the function call
+ */
+ MiddlewareStatus pivGenerateKeyPair(CardHandle cardHandle, byte keyReference, byte cryptographicMechanism, PIVDataObject publicKey);
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/KeyHistoryObject.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/KeyHistoryObject.java
new file mode 100644
index 00000000..823b1af8
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/KeyHistoryObject.java
@@ -0,0 +1,138 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.*;
+import org.apache.commons.codec.binary.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ *
+ * Encapsulates a Key History data object as defined by SP800-73-4 Part 2 Appendix A Table 19
+ *
+ */
+public class KeyHistoryObject extends PIVDataObject {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(KeyHistoryObject.class);
+ // initialize to -1 so we can differentiate between no key history and failure to decode
+ private int m_keysWithOnCardCerts = -1;
+ private int m_keysWithOffCardCerts = -1;
+ private byte[] m_offCardCertUrl;
+
+ public KeyHistoryObject() {
+ m_content = new HashMap();
+ }
+
+ // XXX *** This should probably land in the base class, but at least for this test, it won't
+ private byte[] m_tlvBuf = null;
+ public byte[] getTlvBuf() {
+ return m_tlvBuf;
+ }
+
+
+ /**
+ *
+ * Returns Integer containing keysWithOnCardCerts value
+ *
+ * @return Integer containing keysWithOnCardCerts value
+ */
+ public int getKeysWithOnCardCerts() {
+ return m_keysWithOnCardCerts;
+ }
+
+ /**
+ *
+ * Sets the keysWithOnCardCerts value
+ *
+ * @param keysWithOnCardCerts Integer containing keysWithOnCardCerts value
+ */
+ public void setKeysWithOnCardCerts(int keysWithOnCardCerts) {
+ m_keysWithOnCardCerts = keysWithOnCardCerts;
+ }
+
+ /**
+ *
+ * Returns Integer containing keysWithOffCardCerts value
+ *
+ * @return Integer containing keysWithOffCardCerts value
+ */
+ public int getKeysWithOffCardCerts() {
+ return m_keysWithOffCardCerts;
+ }
+
+ /**
+ *
+ * Sets the keysWithOffCardCerts value
+ *
+ * @param keysWithOffCardCerts Integer containing keysWithOffCardCerts value
+ */
+ public void setKeysWithOffCardCerts(int keysWithOffCardCerts) {
+ m_keysWithOffCardCerts = keysWithOffCardCerts;
+ }
+
+ /**
+ *
+ * Returns byte array containing offCardCertUrl value
+ *
+ * @return Byte array containing offCardCertUrl value
+ */
+ public byte[] getOffCardCertUrl() {
+ return m_offCardCertUrl;
+ }
+
+
+ /**
+ *
+ * Sets the offCardCertUrl value
+ *
+ * @param offCardCertUrl Byte array containing offCardCertUrl value
+ */
+ public void setOffCardCertUrl(byte[] offCardCertUrl) {
+ m_offCardCertUrl = offCardCertUrl;
+ }
+
+ /**
+ *
+ * Decode function that decodes Key History Object retrieved from the card and populates various class fields.
+ *
+ * @return True if decode was successful, false otherwise
+ */
+ @Override
+ public boolean decode() {
+ byte[] rawBytes = this.getBytes();
+ BerTlvParser tlvp = new BerTlvParser(new CCTTlvLogger(this.getClass()));
+ BerTlvs outer = tlvp.parse(rawBytes);
+ List outerTlvs = outer.getList();
+ if(outerTlvs.size() == 1 && outerTlvs.get(0).isTag(new BerTag(0x53))) {
+ m_tlvBuf = outerTlvs.get(0).getBytesValue();
+ outer = tlvp.parse(m_tlvBuf);
+ }
+ for(BerTlv tlv : outer.getList()) {
+ byte[] tag = tlv.getTag().bytes;
+ if(Arrays.equals(tag, TagConstants.KEYS_WITH_ON_CARD_CERTS_TAG)) {
+ m_keysWithOnCardCerts = tlv.getIntValue();
+ m_content.put(tlv.getTag(), tlv.getBytesValue());
+ } else if(Arrays.equals(tag, TagConstants.KEYS_WITH_OFF_CARD_CERTS_TAG)) {
+ m_keysWithOffCardCerts = tlv.getIntValue();
+ m_content.put(tlv.getTag(), tlv.getBytesValue());
+ } else if(Arrays.equals(tag, TagConstants.OFF_CARD_CERT_URL_TAG)) {
+ m_offCardCertUrl = tlv.getBytesValue();
+ m_content.put(tlv.getTag(), tlv.getBytesValue());
+ } else if(!Arrays.equals(tag, TagConstants.ERROR_DETECTION_CODE_TAG) && tlv.getBytesValue().length != 0) {
+ m_content.put(tlv.getTag(), tlv.getBytesValue());
+ s_logger.warn("Unexpected tag: {} with value: {}", Hex.encodeHexString(tlv.getTag().bytes), Hex.encodeHexString(tlv.getBytesValue()));
+ }
+ s_logger.info("found tag: {}", Hex.encodeHexString(tag));
+ }
+
+ if (m_keysWithOnCardCerts == -1 || m_keysWithOffCardCerts == -1)
+ return false;
+
+ dump(this.getClass())
+;
+ return true;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/MiddlewareStatus.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/MiddlewareStatus.java
new file mode 100644
index 00000000..2bf2849f
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/MiddlewareStatus.java
@@ -0,0 +1,27 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+/**
+ *
+ * Enumeration containing PIV Client Application Programming Interface return codes
+ *
+ */
+public enum MiddlewareStatus {
+ PIV_OK,
+ PIV_CONNECTION_DESCRIPTION_MALFORMED,
+ PIV_CONNECTION_FAILURE,
+ PIV_CONNECTION_LOCKED,
+ PIV_INVALID_CARD_HANDLE,
+ PIV_CARD_READER_ERROR,
+ PIV_INVALID_OID,
+ PIV_DATA_OBJECT_NOT_FOUND,
+ PIV_SECURITY_CONDITIONS_NOT_SATISFIED,
+ PIV_SM_FAILED,
+ PIV_INSUFFICIENT_BUFFER,
+ PIV_CARD_APPLICATION_NOT_FOUND,
+ PIV_AUTHENTICATION_FAILURE,
+ PIV_AUTHENTICATOR_MALFORMED,
+ PIV_UNSUPPORTED_CRYPTOGRAPHIC_MECHANISM,
+ PIV_INVALID_KEY_OR_KEYALG_COMBINATION,
+ PIV_FUNCTION_NOT_SUPPORTED,
+ PIV_INSUFFICIENT_CARD_RESOURCE
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/OtherName.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/OtherName.java
new file mode 100644
index 00000000..39a75869
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/OtherName.java
@@ -0,0 +1,99 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+// Used from the bouncy castle git repo under the same license as bouncycastle itself.
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The OtherName object.
+ *
+ * OtherName ::= SEQUENCE {
+ * type-id OBJECT IDENTIFIER,
+ * value [0] EXPLICIT ANY DEFINED BY type-id }
+ *
+ */
+public class OtherName
+ extends ASN1Object
+{
+ private static final Logger s_logger = LoggerFactory.getLogger(OtherName.class);
+
+ private final ASN1ObjectIdentifier typeID;
+ private final ASN1Encodable value;
+
+ /**
+ * OtherName factory method.
+ * @param obj the object used to construct an instance of
+ * OtherName
. It must be an instance of OtherName
+ *
or ASN1Sequence
.
+ * @return the instance of OtherName
built from the
+ * supplied object.
+ * @throws java.lang.IllegalArgumentException if the object passed
+ * to the factory is not an instance of OtherName
or something that
+ * can be converted into an appropriate ASN1Sequence
.
+ */
+ public static OtherName getInstance(
+ Object obj)
+ {
+
+ if (obj instanceof OtherName)
+ {
+ return (OtherName)obj;
+ }
+ else if (obj != null)
+ {
+ return new OtherName(ASN1Sequence.getInstance(obj));
+ }
+
+ return null;
+ }
+
+ /**
+ * Base constructor.
+ * @param typeID the type of the other name.
+ * @param value the ANY object that represents the value.
+ */
+ public OtherName(
+ ASN1ObjectIdentifier typeID,
+ ASN1Encodable value)
+ {
+ this.typeID = typeID;
+ this.value = value;
+ }
+
+ private OtherName(ASN1Sequence seq)
+ {
+ this.typeID = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+ this.value = ASN1TaggedObject.getInstance(seq.getObjectAt(1)).getObject(); // explicitly tagged
+ }
+
+ public ASN1ObjectIdentifier getTypeID()
+ {
+ return typeID;
+ }
+
+ public ASN1Encodable getValue()
+ {
+ return value;
+ }
+
+ @Override
+ public ASN1Primitive toASN1Primitive()
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(typeID);
+ v.add(new DERTaggedObject(true, 0, value));
+
+ return new DERSequence(v);
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVApplicationException.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVApplicationException.java
new file mode 100644
index 00000000..ddd6478d
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVApplicationException.java
@@ -0,0 +1,56 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A base class for exceptions thrown by PIV application methods
+ */
+public class PIVApplicationException extends Exception {
+ private static final Logger s_logger = LoggerFactory.getLogger(PIVApplicationException.class);
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ * Default constructor for PIVApplicationException class
+ *
+ */
+ public PIVApplicationException() {
+ super();
+ }
+
+ /**
+ *
+ * Constructor for PIVApplicationException class that takes a string with exception message
+ *
+ * @param message String with the exception message
+ */
+ public PIVApplicationException(String message) {
+ super(message);
+ }
+
+ /**
+ *
+ * Constructor for PIVApplicationException class that takes a string with exception message and a Throwable cause
+ *
+ * @param message String with the exception message
+ * @param cause Throwable cause
+ */
+ public PIVApplicationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ *
+ * Constructor for PIVApplicationException class that takes a Throwable cause
+ *
+ * @param cause Throwable cause
+ */
+ public PIVApplicationException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVAuthenticator.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVAuthenticator.java
new file mode 100644
index 00000000..4bfbe9ad
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVAuthenticator.java
@@ -0,0 +1,93 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.TagConstants;
+
+import java.util.Arrays;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class that serves the function of the handle objects passed around that encapsulate authenticator information
+ */
+public class PIVAuthenticator {
+ private static final Logger s_logger = LoggerFactory.getLogger(PIVAuthenticator.class);
+
+ byte m_type;
+ byte[] m_data;
+
+ /**
+ *
+ * Constructor that initializes PIVAuthenticator object based on passed in parameter
+ *
+ * @param type Authenticator type either an Application Pin or a Global PIN
+ * @param data String object containing the pin information
+ */
+ public PIVAuthenticator(byte type, String data) {
+ this(type, data.getBytes());
+ }
+
+ /**
+ *
+ * Constructor that initializes PIVAuthenticator object based on passed in parameter
+ *
+ * @param type Authenticator type either an Application Pin or a Global PIN
+ * @param data Byte array object containing the pin information
+ */
+ public PIVAuthenticator(byte type, byte[] data) {
+ m_type = type;
+ if(m_type == TagConstants.KEY_REFERENCE_APPLICATION_PIN_TAG ||
+ m_type == TagConstants.KEY_REFERENCE_GLOBAL_PIN_TAG) {
+ if(data.length == 0) {
+ m_data = new byte[0];
+ } else {
+ if(data.length > 8 || data.length < 6) {
+ throw new IllegalArgumentException("PIN must be between 6 and 8 digits");
+ }
+ m_data = Arrays.copyOf(data, 8);
+ Arrays.fill(m_data, data.length, m_data.length, (byte)0xff);
+ }
+ }
+ }
+
+ /**
+ *
+ * Get the authenticator type
+ *
+ * @return Byte identifying authenticator type
+ */
+ public byte getType() {
+ return m_type;
+ }
+
+ /**
+ *
+ * Set the authenticator type
+ *
+ * @param type byte containing authenticator type
+ */
+ public void setType(byte type) {
+ m_type = type;
+ }
+
+ /**
+ *
+ * Get the pin information
+ *
+ * @return Byte array containing pin information
+ */
+ public byte[] getData() {
+ return m_data;
+ }
+
+ /**
+ *
+ * Set the pin
+ *
+ * @param data byte array containing pin information
+ */
+ public void setData(byte[] data) {
+ m_data = data;
+ }
+
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVAuthenticators.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVAuthenticators.java
new file mode 100644
index 00000000..1adc58b0
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVAuthenticators.java
@@ -0,0 +1,112 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.*;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+/**
+ * A class that serves the function of the handle to a list of authenticator objects
+ * in SP800-73
+ */
+public class PIVAuthenticators {
+ private static final Logger s_logger = LoggerFactory.getLogger(PIVAuthenticators.class);
+
+ private ArrayList m_authenticators = new ArrayList();
+
+ /**
+ *
+ * Get the list of authenticators
+ *
+ * @return List of PIVAuthenticator objects
+ */
+ public List getAuthenticators() {
+ return m_authenticators;
+ }
+
+
+ /**
+ *
+ * Add a global pin authenticator object
+ *
+ * @param pin String containing pin value
+ */
+ public void addGlobalPin(String pin) {
+ PIVAuthenticator a = new PIVAuthenticator(TagConstants.KEY_REFERENCE_GLOBAL_PIN_TAG, pin);
+ m_authenticators.add(a);
+ }
+
+ /**
+ *
+ * Add an application pin authenticator object
+ *
+ * @param pin String containing pin value
+ */
+ public void addApplicationPin(String pin) {
+ PIVAuthenticator a = new PIVAuthenticator(TagConstants.KEY_REFERENCE_APPLICATION_PIN_TAG, pin);
+ m_authenticators.add(a);
+ }
+
+
+ /**
+ *
+ * Returns a byte array representation of a list of authenticator objects
+ *
+ * @return Byte array containing authenticator list
+ */
+ public byte[] getBytes() {
+ byte[] rv = {};
+ if(m_authenticators.size() == 0) return rv;
+ BerTlvBuilder b = new BerTlvBuilder();
+ for(PIVAuthenticator authenticator: m_authenticators) {
+ b.addBytes(new BerTag(TagConstants.REFERENCE_DATA_TAG), authenticator.getData());
+ b.addByte(new BerTag(TagConstants.KEY_REFERENCE_TAG), authenticator.getType());
+ }
+ rv = b.buildArray();
+ //s_logger.debug("Encoded authenticators: {}", Hex.encodeHexString(rv));
+ return rv;
+ }
+
+ /**
+ *
+ * Helper function that decodes byte array containing authenticator list and populates various class fields.
+ *
+ * @param authenticators Byte array containing authenticator list
+ */
+ public boolean decode(byte[] authenticators) {
+ m_authenticators.clear();
+ if(authenticators.length == 0) return true;
+ BerTlvParser p = new BerTlvParser(new CCTTlvLogger(this.getClass()));
+ BerTlvs tlvs = p.parse(authenticators);
+ byte[] refData = null;
+ byte refId = 0x00;
+ for(BerTlv t : tlvs.getList()) {
+ switch(t.getTag().bytes[0] ) {
+ case (byte)0x81:
+ {
+ refData = t.getBytesValue();
+ break;
+ }
+ case (byte)0x83:
+ {
+ if(refData == null) {
+ throw new IllegalStateException("Unexpected 0x83 tag without having seen 0x81 tag while parsing authenticator");
+ }
+ refId = t.getBytesValue()[0];
+ PIVAuthenticator parsed = new PIVAuthenticator(refId, refData);
+ m_authenticators.add(parsed);
+ refData = null;
+ refId = 0x00;
+ break;
+ }
+ default:
+ throw new IllegalStateException("Unexpected tag in authenticator");
+ }
+ }
+ return true;
+ }
+
+
+
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVDataObject.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVDataObject.java
similarity index 96%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVDataObject.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVDataObject.java
index e96f8984..1abb2f61 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/PIVDataObject.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVDataObject.java
@@ -1,4 +1,4 @@
-package gov.gsa.pivconformance.card.client;
+package gov.gsa.pivconformance.cardlib.card.client;
import java.util.ArrayList;
import java.util.Arrays;
@@ -12,10 +12,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import gov.gsa.pivconformance.tlv.BerTag;
-import gov.gsa.pivconformance.tlv.HexUtil;
-import gov.gsa.pivconformance.tlv.TagBoundaryManager;
-import gov.gsa.pivconformance.tlv.TagConstants;
+import gov.gsa.pivconformance.cardlib.tlv.BerTag;
+import gov.gsa.pivconformance.cardlib.tlv.HexUtil;
+import gov.gsa.pivconformance.cardlib.tlv.TagBoundaryManager;
+import gov.gsa.pivconformance.cardlib.tlv.TagConstants;
/**
* Represents a PIV data object as read to or written from the card. Subclasses
@@ -236,14 +236,15 @@ public void dump(Class> classz) {
// Get the container name
String canonicalName = classz.getCanonicalName();
String containerName = getContainerName();
- if (containerName == null) containerName = APDUConstants.oidNameMap.get(m_OID).replaceAll("_", "");
+ if (containerName == null) containerName = APDUConstants.oidNameMap.get(m_OID).replaceAll(" ", "");
String className = containerName == null ? canonicalName
: classz.getPackage().toString().replace("package ", "") + "." + containerName;
// Find the path where containers are written
Logger s_containerLogger = LoggerFactory.getLogger(className);
s_containerLogger.debug("Container: {}", APDUConstants.oidNameMap.get(m_OID).replace(" ", "_"));
s_containerLogger.debug("Raw bytes: {}", Hex.encodeHexString(m_dataBytes));
- m_artifactCache.saveObject(APDUConstants.getFileNameForOid(m_OID)+ ".dat", m_dataBytes);
+
+ m_artifactCache.saveObject("piv-artifacts", APDUConstants.getFileNameForOid(m_OID)+ ".dat", m_dataBytes);
for (int i = 0; i < m_tagList.size(); i++) {
BerTag tag = m_tagList.get(i);
if (tag != null) {
@@ -411,5 +412,4 @@ public String getContainerName() {
public void setContainerName(String containerName) {
m_containerName = containerName;
}
-
-}
+}
\ No newline at end of file
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVDataObjectFactory.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVDataObjectFactory.java
new file mode 100644
index 00000000..6c5280be
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVDataObjectFactory.java
@@ -0,0 +1,60 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PIVDataObjectFactory {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(PIVDataObjectFactory.class);
+
+ /**
+ * Instantiate an appropriate PIVDataObject class given an OID, or a generic one in the absence of an OID
+ *
+ * @param OID
+ * @return
+ */
+ public static PIVDataObject createDataObjectForOid(String OID) {
+ PIVDataObject rv = null;
+
+ if(OID.equals(APDUConstants.CARD_CAPABILITY_CONTAINER_OID))
+ rv = new CardCapabilityContainer();
+ else if(OID.equals(APDUConstants.CARD_HOLDER_UNIQUE_IDENTIFIER_OID))
+ rv = new CardHolderUniqueIdentifier();
+ else if(OID.equals(APDUConstants.SECURITY_OBJECT_OID))
+ rv = new SecurityObject();
+ else if(OID.equals(APDUConstants.CARDHOLDER_FACIAL_IMAGE_OID))
+ rv = new CardHolderBiometricData();
+ else if(OID.equals(APDUConstants.CARDHOLDER_FINGERPRINTS_OID))
+ rv = new CardHolderBiometricData();
+ else if(OID.equals(APDUConstants.X509_CERTIFICATE_FOR_CARD_AUTHENTICATION_OID))
+ rv = new X509CertificateDataObject();
+ else if(OID.equals(APDUConstants.X509_CERTIFICATE_FOR_PIV_AUTHENTICATION_OID))
+ rv = new X509CertificateDataObject();
+ else if(OID.equals(APDUConstants.DISCOVERY_OBJECT_OID))
+ rv = new DiscoveryObject();
+ else if(OID.equals(APDUConstants.KEY_HISTORY_OBJECT_OID))
+ rv = new KeyHistoryObject();
+ else if(OID.equals(APDUConstants.BIOMETRIC_INFORMATION_TEMPLATES_GROUP_TEMPLATE_OID))
+ rv = new BiometricInformationTemplatesGroupTemplate();
+ else if(OID.equals(APDUConstants.CARDHOLDER_IRIS_IMAGES_OID))
+ rv = new CardHolderBiometricData();
+ else if(OID.equals(APDUConstants.PAIRING_CODE_REFERENCE_DATA_CONTAINER_OID))
+ rv = new PairingCodeReferenceDataContainer();
+ else if(OID.equals(APDUConstants.SECURE_MESSAGING_CERTIFICATE_SIGNER_OID))
+ rv = new SecureMessagingCertificateSigner();
+ else if(OID.equals(APDUConstants.X509_CERTIFICATE_FOR_DIGITAL_SIGNATURE_OID))
+ rv = new X509CertificateDataObject();
+ else if(OID.equals(APDUConstants.X509_CERTIFICATE_FOR_KEY_MANAGEMENT_OID))
+ rv = new X509CertificateDataObject();
+ else if(OID.equals(APDUConstants.PRINTED_INFORMATION_OID))
+ rv = new PrintedInformation();
+
+ if(rv == null) {
+ s_logger.warn("Unrecognized data object type: {}. Using generic.", OID);
+ rv = new PIVDataObject();
+ return rv;
+ }
+ rv.setOID(OID);
+ return rv;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVMiddleware.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVMiddleware.java
new file mode 100644
index 00000000..1aa164cb
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVMiddleware.java
@@ -0,0 +1,99 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import gov.gsa.pivconformance.cardlib.utils.PCSCWrapper;
+
+import javax.smartcardio.CardTerminal;
+import javax.smartcardio.Card;
+
+
+public class PIVMiddleware {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(PIVMiddleware.class);
+ public static final String PIV_MIDDLEWARE_VERSION = "800-73-4 Client API";
+
+ /**
+ * pivMiddlewareVersion from section 3.1.1 of SP 800-73-4
+ *
+ * @param version
+ * @return PIV_OK if version was successfully retrieved
+ */
+ public static MiddlewareStatus pivMiddlewareVersion(PIVMiddlewareVersion version) {
+ version.setVersion(PIV_MIDDLEWARE_VERSION);
+ return MiddlewareStatus.PIV_OK;
+ }
+
+ /**
+ * pivConnect from section 3.1.2 of SP 800-73-4
+ *
+ * @param sharedConnection
+ * @param connectionDescription
+ * @param cardHandle
+ * @return PIV_OK if connection was successful, PIV_CONNECTION_DESCRIPTION_MALFORMED if an invalid ConnectionDescription object was passed in,
+ * PIV_CONNECTION_FAILURE if no connection could be established, or PIV_CONNECTION_LOCKED if an exclusive connection was requested but another exclusive
+ * connection to the card in the specified reader is already in progress.
+ */
+ public static MiddlewareStatus pivConnect(boolean sharedConnection, ConnectionDescription connectionDescription, CardHandle cardHandle) {
+
+ // Need to figure out what to do with sharedConnection in context of JAVA
+ CardTerminal t = connectionDescription.getTerminal();
+ if(cardHandle == null)
+ cardHandle = new CardHandle();
+
+ if(connectionDescription.getTerminal() == null )
+ return MiddlewareStatus.PIV_CONNECTION_DESCRIPTION_MALFORMED;
+
+ try {
+
+ PCSCWrapper pcsc = PCSCWrapper.getInstance();
+
+ Card card = pcsc.connect(t);
+
+ if(card != null) {
+ cardHandle.setConnectionDescription(connectionDescription);
+ cardHandle.setCard(card);
+ cardHandle.setValid(true);
+ cardHandle.setCurrentChannel(card.getBasicChannel());
+ }
+
+ }catch (Exception ex) {
+ s_logger.error("Unable to establish connection to the card : ", ex.getMessage(), ex);
+ return MiddlewareStatus.PIV_CONNECTION_FAILURE;
+ }
+
+ return MiddlewareStatus.PIV_OK;
+ }
+
+ /**
+ * pivDisconnect from section 3.1.2 of SP 800-73-4
+ *
+ * @param cardHandle
+ * @return PIV_OK if the connection was disconnected and the CardHandle invalidated, PIV_INVALID_CARD_HANDLE if cardHandle was invalid,
+ * PIV_CARD_READER_ERROR if the connection could not be destroyed due to a reader failure.
+ */
+ public static MiddlewareStatus pivDisconnect(CardHandle cardHandle) {
+
+ try {
+
+ Card card = cardHandle.getCard();
+
+ if(card == null || !cardHandle.isValid()) {
+ return MiddlewareStatus.PIV_INVALID_CARD_HANDLE;
+ }
+
+ //XXX Need to figure out if connections needs to be reset or not
+ card.disconnect(false);
+ //Invalidate cardHandle object
+ cardHandle = new CardHandle();
+
+ }catch (Exception ex) {
+
+ s_logger.error("Unable to disconnect from the card : ", ex.getMessage());
+ return MiddlewareStatus.PIV_CARD_READER_ERROR;
+ }
+
+ return MiddlewareStatus.PIV_OK;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVMiddlewareVersion.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVMiddlewareVersion.java
new file mode 100644
index 00000000..76295cc4
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PIVMiddlewareVersion.java
@@ -0,0 +1,54 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class for atoring PIV middleware version information.
+ */
+public class PIVMiddlewareVersion {
+ private static final Logger s_logger = LoggerFactory.getLogger(PIVMiddlewareVersion.class);
+
+ /**
+ *
+ * Default constructor that creates an invalid PIVMiddlewareVersion object
+ *
+ */
+ public PIVMiddlewareVersion() {
+ version = "NOT SET";
+ }
+
+ /**
+ *
+ * Returns a String with PIV middleware version info
+ *
+ * @return
+ */
+ public String getVersion() {
+ return version;
+ }
+
+ /**
+ *
+ * Sets the PIV middleware version info
+ *
+ * @param version String with PIV middleware version info
+ */
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ /**
+ *
+ * Returns a String with PIV middleware version info
+ *
+ * @return
+ */
+ @Override
+ public String toString() {
+ return version;
+ }
+
+ private String version;
+
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PairingCodeReferenceDataContainer.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PairingCodeReferenceDataContainer.java
new file mode 100644
index 00000000..b5b9191f
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PairingCodeReferenceDataContainer.java
@@ -0,0 +1,149 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.*;
+import org.apache.commons.codec.binary.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ *
+ * Encapsulates a Pairing Code Reference Data Container data object as defined by SP800-73-4 Part 2 Appendix A Table 43
+ *
+ */
+public class PairingCodeReferenceDataContainer extends PIVDataObject {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(PairingCodeReferenceDataContainer.class);
+
+ private String m_pairingCode;
+ private boolean m_errorDetectionCode;
+
+ /**
+ * PairingCodeReferenceDataContainer class constructor, initializes all the class fields.
+ */
+ public PairingCodeReferenceDataContainer() {
+ m_pairingCode = "";
+ m_errorDetectionCode = false;
+ m_content = new HashMap();
+ }
+
+ /**
+ *
+ * Returns a String with pairing code name
+ *
+ * @return String containing pairing code name
+ */
+ public String getName() {
+ return m_pairingCode;
+ }
+
+ /**
+ *
+ * Sets the pairing code name
+ *
+ * @param pairingCode String containing pairing code name
+ */
+ public void setName(String pairingCode) {
+ m_pairingCode = pairingCode;
+ }
+
+ /**
+ *
+ * Returns True if error Error Detection Code is present, false otherwise
+ *
+ * @return True if error Error Detection Code is present, false otherwise
+ */
+ @Override
+ public boolean getErrorDetectionCode() {
+ return m_errorDetectionCode;
+ }
+
+ /**
+ *
+ * Sets if error Error Detection Code is present
+ *
+ * @param errorDetectionCode True if error Error Detection Code is present, false otherwise
+ */
+ @Override
+ public void setErrorDetectionCode(boolean errorDetectionCode) {
+ m_errorDetectionCode = errorDetectionCode;
+ }
+
+ /**
+ *
+ * Decode function that decodes Pairing Code Reference Data Container object retrieved from the card and populates various class fields.
+ *
+ * @return True if decode was successful, false otherwise
+ */
+ @Override
+ public boolean decode() {
+
+ try{
+ byte[] rawBytes = this.getBytes();
+
+ if(rawBytes == null){
+ s_logger.error("No buffer to decode for {}.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ BerTlvParser tlvp = new BerTlvParser(new CCTTlvLogger(this.getClass()));
+ BerTlvs outer = tlvp.parse(rawBytes);
+
+ if(outer == null){
+ s_logger.error("Error parsing {}, unable to parse TLV value.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ List values = outer.getList();
+ for(BerTlv tlv : values) {
+ if(tlv.isPrimitive()) {
+ s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv.getTag().bytes), Hex.encodeHexString(tlv.getBytesValue()));
+
+ BerTlvs outer2 = tlvp.parse(tlv.getBytesValue());
+
+ if (outer2 == null) {
+ s_logger.error("Error parsing {}, unable to parse TLV value.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ List values2 = outer2.getList();
+ for (BerTlv tlv2 : values2) {
+ if (tlv2.isPrimitive()) {
+ if (Arrays.equals(tlv2.getTag().bytes, TagConstants.PAIRING_CODE_TAG)) {
+
+ m_pairingCode = new String(tlv2.getBytesValue());
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+
+ } else{
+ s_logger.warn("Unexpected tag: {} with value: {}", Hex.encodeHexString(tlv2.getTag().bytes), Hex.encodeHexString(tlv2.getBytesValue()));
+ }
+ } else {
+ if (Arrays.equals(tlv2.getTag().bytes, TagConstants.ERROR_DETECTION_CODE_TAG)) {
+
+ m_errorDetectionCode = true;
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+
+ } else {
+ s_logger.warn("Unexpected tag: {} with value: {}", Hex.encodeHexString(tlv2.getTag().bytes), Hex.encodeHexString(tlv2.getBytesValue()));
+ }
+ }
+ }
+ }
+ }
+ }catch (Exception ex) {
+
+ s_logger.error("Error parsing {}: {}", APDUConstants.oidNameMap.get(super.getOID()), ex.getMessage());
+ return false;
+ }
+
+ if (m_pairingCode == "")
+ return false;
+
+ dump(this.getClass())
+;
+ return true;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PrintedInformation.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PrintedInformation.java
new file mode 100644
index 00000000..962284fc
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/PrintedInformation.java
@@ -0,0 +1,356 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.*;
+import org.apache.commons.codec.binary.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ *
+ * Encapsulates a Printed Information data object as defined by SP800-73-4 Part 2 Appendix A Table 9
+ *
+ */
+public class PrintedInformation extends PIVDataObject {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(PrintedInformation.class);
+
+ private String m_name;
+ private String m_employeeAffiliation;
+ private String m_expirationDate;
+ private String m_agencyCardSerialNumber;
+ private String m_issuerIdentification;
+ private String m_organizationAffiliation1;
+ private String m_organizationAffiliation2;
+ private boolean m_errorDetectionCode;
+ private byte[] m_signedContent;
+
+
+ /**
+ * PrintedInformation class constructor, initializes all the class fields.
+ */
+ public PrintedInformation() {
+ m_name = "";
+ m_employeeAffiliation = "";
+ m_expirationDate = "";
+ m_agencyCardSerialNumber = "";
+ m_issuerIdentification = "";
+ m_organizationAffiliation1 = "";
+ m_organizationAffiliation2 = "";
+ m_errorDetectionCode = false;
+ m_signedContent = null;
+ m_content = new HashMap();
+ }
+
+ /**
+ *
+ * Returns byte array with signed content
+ *
+ * @return Byte array with signed content buffer
+ */
+ public byte[] getSignedContent() {
+ return m_signedContent;
+ }
+
+ /**
+ *
+ * Sets the signed content value
+ *
+ * @param signedContent Byte array with signed content buffer
+ */
+ public void setSignedContent(byte[] signedContent) {
+ m_signedContent = signedContent;
+ }
+
+
+ /**
+ *
+ * Returns the Name value as String
+ *
+ * @return String with the Name value
+ */
+ public String getName() {
+ return m_name;
+ }
+
+
+ /**
+ *
+ * Sets the name value
+ *
+ * @param name String with the Name value
+ */
+ public void setName(String name) {
+ m_name = name;
+ }
+
+ /**
+ *
+ * Returns the Employee Affiliation value as String
+ *
+ * @return String with the Employee Affiliation value
+ */
+ public String getEmployeeAffiliation() {
+ return m_employeeAffiliation;
+ }
+
+ /**
+ *
+ * Sets the Employee Affiliation value
+ *
+ * @param employeeAffiliation String with the Employee Affiliation value
+ */
+ public void setEmployeeAffiliation(String employeeAffiliation) {
+ m_employeeAffiliation = employeeAffiliation;
+ }
+
+ /**
+ *
+ * Returns the Expiration Date value as String
+ *
+ * @return String with the Expiration Date value
+ */
+ public String getExpirationDate() {
+ return m_expirationDate;
+ }
+
+ /**
+ *
+ * Sets the Expiration Date value
+ *
+ * @param expirationDate String with the Expiration Date value
+ */
+ public void setExpirationDate(String expirationDate) {
+ m_expirationDate = expirationDate;
+ }
+
+ /**
+ *
+ * Returns the Agency Card Serial Number value as String
+ *
+ * @return String with the Agency Card Serial Number value
+ */
+ public String getAgencyCardSerialNumber() {
+ return m_agencyCardSerialNumber;
+ }
+
+ /**
+ *
+ * Sets the Agency Card Serial Number value
+ *
+ * @param agencyCardSerialNumber String with the Agency Card Serial Number value
+ */
+ public void setAgencyCardSerialNumber(String agencyCardSerialNumber) {
+ m_agencyCardSerialNumber = agencyCardSerialNumber;
+ }
+
+ /**
+ *
+ * Returns the Issuer Identification value as String
+ *
+ * @return String with the Issuer Identification value
+ */
+ public String getIssuerIdentification() {
+ return m_issuerIdentification;
+ }
+
+ /**
+ *
+ * Sets the Issuer Identification value
+ *
+ * @param issuerIdentification String with the Issuer Identification value
+ */
+ public void setIssuerIdentification(String issuerIdentification) {
+ m_issuerIdentification = issuerIdentification;
+ }
+
+ /**
+ *
+ * Returns the Organization Affiliation1 value as String
+ *
+ * @return String with the Organization Affiliation1 value
+ */
+ public String getOrganizationAffiliation1() {
+ return m_organizationAffiliation1;
+ }
+
+ /**
+ *
+ * Sets the Organization Affiliation1 value
+ *
+ * @param organizationAffiliation1 String with the Organization Affiliation1 value
+ */
+ public void setOrganizationAffiliation1(String organizationAffiliation1) {
+ m_organizationAffiliation1 = organizationAffiliation1;
+ }
+
+ /**
+ *
+ * Returns the Organization Affiliation2 value as String
+ *
+ * @return String with the Organization Affiliation2 value
+ */
+ public String getOrganizationAffiliation2() {
+ return m_organizationAffiliation2;
+ }
+
+ /**
+ *
+ * Sets the Organization Affiliation2 value
+ *
+ * @param organizationAffiliation2 String with the Organization Affiliation2 value
+ */
+ public void setOrganizationAffiliation2(String organizationAffiliation2) {
+ m_organizationAffiliation2 = organizationAffiliation2;
+ }
+
+ /**
+ *
+ * Returns True if error Error Detection Code is present, false otherwise
+ *
+ * @return True if error Error Detection Code is present, false otherwise
+ */
+ @Override
+ public boolean getErrorDetectionCode() {
+ return m_errorDetectionCode;
+ }
+
+ /**
+ *
+ * Sets if error Error Detection Code is present
+ *
+ * @param errorDetectionCode True if error Error Detection Code is present, false otherwise
+ */
+ @Override
+ public void setErrorDetectionCode(boolean errorDetectionCode) {
+ m_errorDetectionCode = errorDetectionCode;
+ }
+
+ /**
+ *
+ * Decode function that decodes Printed Information object retrieved from the card and populates various class fields.
+ *
+ * @return True if decode was successful, false otherwise
+ */
+ @Override
+ public boolean decode() {
+
+ try{
+ byte[] rawBytes = this.getBytes();
+
+ if(rawBytes == null){
+ s_logger.error("No buffer to decode for {}.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ BerTlvParser tlvp = new BerTlvParser(new CCTTlvLogger(this.getClass()));
+ BerTlvs outer = tlvp.parse(rawBytes);
+
+ if(outer == null){
+ s_logger.error("Error parsing {}, unable to parse TLV value.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ List values = outer.getList();
+ for(BerTlv tlv : values) {
+ if(tlv.isPrimitive()) {
+ s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv.getTag().bytes), Hex.encodeHexString(tlv.getBytesValue()));
+
+ BerTlvs outer2 = tlvp.parse(tlv.getBytesValue());
+
+ if (outer2 == null) {
+ s_logger.error("Error parsing {}, unable to parse TLV value.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ ByteArrayOutputStream scos = new ByteArrayOutputStream();
+
+ List values2 = outer2.getList();
+ for (BerTlv tlv2 : values2) {
+ if (tlv2.isPrimitive()) {
+
+ super.m_tagList.add(tlv2.getTag());
+ if (Arrays.equals(tlv2.getTag().bytes, TagConstants.NAME_TAG)) {
+
+ m_name = new String(tlv2.getBytesValue());
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.NAME_TAG, tlv2.getBytesValue()));
+
+ } else if (Arrays.equals(tlv2.getTag().bytes, TagConstants.EMPLOYEE_AFFILIATION_TAG)) {
+
+ m_employeeAffiliation = new String(tlv2.getBytesValue());
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.EMPLOYEE_AFFILIATION_TAG, tlv2.getBytesValue()));
+
+ } else if (Arrays.equals(tlv2.getTag().bytes, TagConstants.PRINTED_INFORMATION_EXPIRATION_DATE_TAG)) {
+
+ m_expirationDate = new String(tlv2.getBytesValue());
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.PRINTED_INFORMATION_EXPIRATION_DATE_TAG, tlv2.getBytesValue()));
+
+ } else if (Arrays.equals(tlv2.getTag().bytes, TagConstants.AGENCY_CARD_SERIAL_NUMBER_TAG)) {
+
+ m_agencyCardSerialNumber = new String(tlv2.getBytesValue());
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.AGENCY_CARD_SERIAL_NUMBER_TAG, tlv2.getBytesValue()));
+
+ } else if (Arrays.equals(tlv2.getTag().bytes, TagConstants.ISSUER_IDENTIFICATION_TAG)) {
+
+ m_issuerIdentification = new String(tlv2.getBytesValue());
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.ISSUER_IDENTIFICATION_TAG, tlv2.getBytesValue()));
+
+ } else if (Arrays.equals(tlv2.getTag().bytes, TagConstants.ORGANIZATIONAL_AFFILIATION_L1_TAG)) {
+
+ m_organizationAffiliation1 = new String(tlv2.getBytesValue());
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.ORGANIZATIONAL_AFFILIATION_L1_TAG, tlv2.getBytesValue()));
+
+ } else if (Arrays.equals(tlv2.getTag().bytes, TagConstants.ORGANIZATIONAL_AFFILIATION_L2_TAG)) {
+
+ m_organizationAffiliation2 = new String(tlv2.getBytesValue());
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ scos.write(APDUUtils.getTLV(TagConstants.ORGANIZATIONAL_AFFILIATION_L2_TAG, tlv2.getBytesValue()));
+
+ }else{
+ s_logger.warn("Unexpected tag: {} with value: {}", Hex.encodeHexString(tlv2.getTag().bytes), Hex.encodeHexString(tlv2.getBytesValue()));
+ }
+ } else {
+ super.m_tagList.add(tlv2.getTag());
+ if (Arrays.equals(tlv2.getTag().bytes, TagConstants.ERROR_DETECTION_CODE_TAG)) {
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ m_errorDetectionCode = true;
+
+ scos.write(TagConstants.ERROR_DETECTION_CODE_TAG);
+ scos.write((byte) 0x00);
+
+ } else {
+ s_logger.warn("Unexpected tag: {} with value: {}", Hex.encodeHexString(tlv2.getTag().bytes), Hex.encodeHexString(tlv2.getBytesValue()));
+ }
+ }
+ }
+
+ m_signedContent = scos.toByteArray();
+ }
+ }
+ } catch (Exception ex) {
+
+ s_logger.error("Error parsing {}: {}", APDUConstants.oidNameMap.get(super.getOID()), ex.getMessage());
+ return false;
+ }
+
+ if (m_name == "" || m_employeeAffiliation == "" || m_expirationDate == "" ||
+ m_agencyCardSerialNumber == "" || m_issuerIdentification == "")
+ return false;
+
+ super.setRequiresPin(true);
+
+ dump(this.getClass())
+;
+ return true;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/RequestAPDUWrapper.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/RequestAPDUWrapper.java
similarity index 94%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/RequestAPDUWrapper.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/RequestAPDUWrapper.java
index ccf3e34a..744b6666 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/RequestAPDUWrapper.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/RequestAPDUWrapper.java
@@ -1,4 +1,4 @@
-package gov.gsa.pivconformance.card.client;
+package gov.gsa.pivconformance.cardlib.card.client;
import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
@@ -6,7 +6,7 @@
// derived from the intarsys ReqeuestAPDU class
public class RequestAPDUWrapper {
- private static final Logger s_logger = LoggerFactory.getLogger(RequestAPDUWrapper.class);
+ private static final Logger s_logger = LoggerFactory.getLogger(RequestAPDUWrapper.class);
/* command is not the last command of a chain */
public static final byte CLA_CHAINING_FLAG = 0x10;
@@ -52,7 +52,8 @@ public RequestAPDUWrapper(int pCla, int pIns, int pP1, int pP2, byte[] pData) {
this(pCla, pIns, pP1, pP2, pData, LE_NONE);
}
- public RequestAPDUWrapper(int pCla, int pIns, int pP1, int pP2, byte[] pData, int pLe) {
+ public RequestAPDUWrapper(int pCla, int pIns, int pP1, int pP2, byte[] pData,
+ int pLe) {
cla = pCla;
ins = pIns;
p1 = pP1;
@@ -248,7 +249,8 @@ public boolean isChainedRequest() {
}
public boolean isExtendedApdu() {
- return extendedApdu || (data != null && data.length > 255) || (le > 255);
+ return extendedApdu || (data != null && data.length > 255)
+ || (le > 255);
}
public boolean isSensitiveContent() {
@@ -274,7 +276,8 @@ public void setSensitiveContent(boolean sensitiveContent) {
@Override
public String toString() {
return Hex.encodeHexString(getBytes()).replaceAll("..(?=.)", "$0 ");
- // return HexTools.bytesToHexString(getBytes());
+ //return HexTools.bytesToHexString(getBytes());
}
}
+
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/ResponseAPDUWrapper.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ResponseAPDUWrapper.java
similarity index 82%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/ResponseAPDUWrapper.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ResponseAPDUWrapper.java
index 75a90173..5f03d24d 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/ResponseAPDUWrapper.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/ResponseAPDUWrapper.java
@@ -1,4 +1,4 @@
-package gov.gsa.pivconformance.card.client;
+package gov.gsa.pivconformance.cardlib.card.client;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
@@ -7,16 +7,18 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
// derived from the intarsys ResponseAPDU class
public class ResponseAPDUWrapper {
- private static final Logger s_logger = LoggerFactory.getLogger(ResponseAPDUWrapper.class);
+ private static final Logger s_logger = LoggerFactory.getLogger(ResponseAPDUWrapper.class);
private final byte[] bytes;
public ResponseAPDUWrapper(byte[] response) throws CardClientException {
assert (response != null);
if (response.length < 2) {
- throw new CardClientException("Invalid response received from card reader");
+ throw new CardClientException(
+ "Invalid response received from card reader");
}
this.bytes = response;
}
@@ -65,7 +67,8 @@ public InputStream getInputStream() {
}
public int getSw() {
- return ((bytes[bytes.length - 2] & 0xFF) << 8) + (bytes[bytes.length - 1] & 0xFF);
+ return ((bytes[bytes.length - 2] & 0xFF) << 8)
+ + (bytes[bytes.length - 1] & 0xFF);
}
public int getSw1() {
@@ -77,7 +80,8 @@ public int getSw2() {
}
public String getSwString() {
- return "0x" + Integer.toHexString(getSw1()) + "" + Integer.toHexString(getSw2());
+ return "0x" + Integer.toHexString(getSw1()) + ""
+ + Integer.toHexString(getSw2());
}
public boolean hasData() {
@@ -94,3 +98,4 @@ public String toString() {
}
}
+
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SecureMessagingCertificateSigner.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SecureMessagingCertificateSigner.java
new file mode 100644
index 00000000..a471cfaa
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SecureMessagingCertificateSigner.java
@@ -0,0 +1,176 @@
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import gov.gsa.pivconformance.cardlib.tlv.*;
+import org.apache.commons.codec.binary.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.zip.GZIPInputStream;
+
+/**
+ *
+ * Encapsulates a Card Holder Unique Identifier data object as defined by SP800-73-4 Part 2 Appendix A Table 42
+ *
+ */
+public class SecureMessagingCertificateSigner extends PIVDataObject { // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(SecureMessagingCertificateSigner.class);
+
+ private X509Certificate m_pivAuthCert;
+ private byte[] m_intermediateCVC;
+ private boolean m_error_Detection_Code;
+
+ /**
+ * SecureMessagingCertificateSigner class constructor, initializes all the class fields.
+ */
+ public SecureMessagingCertificateSigner() {
+
+ m_pivAuthCert = null;
+ m_intermediateCVC = null;
+ m_error_Detection_Code = false;
+ m_content = new HashMap();
+ }
+
+ /**
+ *
+ * Returns True if error Error Detection Code is present, false otherwise
+ *
+ * @return True if error Error Detection Code is present, false otherwise
+ */
+ @Override
+ public boolean getErrorDetectionCode() {
+
+ return m_error_Detection_Code;
+ }
+
+ /**
+ *
+ * Returns X509Certificate object containing X.509 Certificate for Content Signing
+ *
+ * @return X509Certificate object containing X.509 Certificate for Content Signing
+ */
+ public X509Certificate getCertificate() {
+ return m_pivAuthCert;
+ }
+
+ /**
+ *
+ * Returns byte array with Intermediate CVC value
+ *
+ * @return Byte array containing Intermediate CVC value
+ */
+ public byte[] getIntermediateCVC() {
+ return m_intermediateCVC;
+ }
+
+ /**
+ *
+ * Sets the Intermediate CVC value
+ *
+ * @param intermediateCVC Byte array containing Intermediate CVC value
+ */
+ public void setIntermediateCVC(byte[] intermediateCVC) {
+ m_intermediateCVC = intermediateCVC;
+ }
+
+ /**
+ *
+ * Decode function that decodes Secure Messaging Certificate Signer object retrieved from the card and populates various class fields.
+ *
+ * @return True if decode was successful, false otherwise
+ */
+ @Override
+ public boolean decode() {
+
+ if(m_pivAuthCert == null){
+
+ try{
+ byte [] raw = super.getBytes();
+
+ BerTlvParser tp = new BerTlvParser(new CCTTlvLogger(this.getClass()));
+ BerTlvs outer = tp.parse(raw);
+
+ if(outer == null){
+ s_logger.error("Error parsing {}, unable to parse TLV value.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ List values = outer.getList();
+ for(BerTlv tlv : values) {
+ if(tlv.isPrimitive()) {
+ s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv.getTag().bytes), Hex.encodeHexString(tlv.getBytesValue()));
+
+ BerTlvs outer2 = tp.parse(tlv.getBytesValue());
+
+ if(outer2 == null){
+ s_logger.error("Error parsing {}, unable to parse TLV value.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
+ List values2 = outer2.getList();
+ byte[] rawCertBuf = null;
+ byte[] certInfoBuf = null;
+ for(BerTlv tlv2 : values2) {
+ if(tlv2.isPrimitive()) {
+ s_logger.trace("Tag {}: {}", Hex.encodeHexString(tlv2.getTag().bytes), Hex.encodeHexString(tlv2.getBytesValue()));
+ } else {
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.CERTIFICATE_TAG)) {
+ if (tlv2.hasRawValue()) {
+ rawCertBuf = tlv2.getBytesValue();
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ }
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.ERROR_DETECTION_CODE_TAG)) {
+ if (tlv2.hasRawValue()) {
+ m_error_Detection_Code = true;
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ }
+ }
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.CERTINFO_TAG)) {
+ certInfoBuf = tlv2.getBytesValue();
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ }
+
+ if(Arrays.equals(tlv2.getTag().bytes, TagConstants.INTERMEDIATE_CVC_TAG)) {
+ m_intermediateCVC = tlv2.getBytesValue();
+ m_content.put(tlv2.getTag(), tlv2.getBytesValue());
+ }
+ }
+ }
+
+ InputStream certIS = null;
+ //Check if the certificate buffer is compressed
+ if(certInfoBuf != null && Arrays.equals(certInfoBuf, TagConstants.COMPRESSED_TAG)) {
+ certIS = new GZIPInputStream(new ByteArrayInputStream(rawCertBuf));
+ } else {
+ certIS = new ByteArrayInputStream(rawCertBuf);
+ }
+
+ CertificateFactory cf = CertificateFactory.getInstance("X509");
+ m_pivAuthCert = (X509Certificate)cf.generateCertificate(certIS);
+ s_logger.info(m_pivAuthCert.getSubjectDN().toString());
+ } else {
+ s_logger.info("Object: {}", Hex.encodeHexString(tlv.getTag().bytes));
+ }
+ }
+ }catch (Exception ex) {
+
+ s_logger.error("Error parsing {}: {}", APDUConstants.oidNameMap.get(super.getOID()), ex.getMessage());
+ return false;
+ }
+
+ if (m_pivAuthCert == null)
+ return false;
+ }
+
+ dump(this.getClass())
+;
+ return true;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/SecurityObject.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SecurityObject.java
similarity index 95%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/SecurityObject.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SecurityObject.java
index 6290b500..e7a83de6 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/SecurityObject.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SecurityObject.java
@@ -1,6 +1,6 @@
-package gov.gsa.pivconformance.card.client;
+package gov.gsa.pivconformance.cardlib.card.client;
-import gov.gsa.pivconformance.tlv.*;
+import gov.gsa.pivconformance.cardlib.tlv.*;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.icao.DataGroupHash;
@@ -12,6 +12,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
@@ -170,12 +171,11 @@ public boolean decode() {
BerTlvParser tlvp = new BerTlvParser(new CCTTlvLogger(this.getClass()));
BerTlvs outer = tlvp.parse(rawBytes);
- if (outer == null) {
- s_logger.error("Error parsing {}, unable to parse TLV value.",
- APDUConstants.oidNameMap.get(super.getOID()));
- return false;
- }
-
+ if(outer == null){
+ s_logger.error("Error parsing {}, unable to parse TLV value.", APDUConstants.oidNameMap.get(super.getOID()));
+ return false;
+ }
+
List outerTlvs = outer.getList();
if (outerTlvs.size() == 1 && outerTlvs.get(0).isTag(new BerTag(0x53))) {
byte[] tlvBuf = outerTlvs.get(0).getBytesValue();
@@ -222,9 +222,9 @@ public boolean decode() {
APDUConstants.oidNameMap.get(super.getOID()));
return false;
}
-
+
m_content.put(tlv.getTag(), m_so);
-
+
// Decode the ContentInfo and get SignedData object.
ByteArrayInputStream bIn = new ByteArrayInputStream(m_so);
ASN1InputStream aIn = new ASN1InputStream(bIn);
@@ -275,10 +275,10 @@ public boolean decode() {
// throw their own
// exception
setComputedDigest(signer, m_so);
- } else if (Arrays.equals(tag, TagConstants.ERROR_DETECTION_CODE_TAG)) {
- BerTag tag2 = tlv.getTag();
- m_content.put(tag2, tlv.getBytesValue());
- m_errorDetectionCode = true;
+ } else if (Arrays.equals(tag, TagConstants.ERROR_DETECTION_CODE_TAG)) {
+ BerTag tag2 = tlv.getTag();
+ m_content.put(tag2, tlv.getBytesValue());
+ m_errorDetectionCode = true;
} else if (tlv.getBytesValue().length != 0) {
s_logger.warn("Unexpected tag: {} with value: {}", Hex.encodeHexString(tlv.getTag().bytes),
Hex.encodeHexString(tlv.getBytesValue()));
@@ -338,8 +338,7 @@ public boolean verifyHashes() {
for (Map.Entry entry : m_dghList.entrySet()) {
String oid = m_containerIDList.get(entry.getKey());
- s_logger.debug("Checking digest for {} (0x{})", APDUConstants.containerOidToNameMap.get(oid),
- Integer.toHexString(entry.getKey()));
+ s_logger.debug("Checking digest for {} (0x{})", APDUConstants.containerOidToNameMap.get(oid), Integer.toHexString(entry.getKey()));
if (oid != null) {
byte[] content = m_mapOfDataElements.get(oid);
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/SignedPIVDataObject.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SignedPIVDataObject.java
similarity index 79%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/SignedPIVDataObject.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SignedPIVDataObject.java
index bd729fdc..c879019a 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/SignedPIVDataObject.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SignedPIVDataObject.java
@@ -1,555 +1,551 @@
-/**
- *
- */
-package gov.gsa.pivconformance.card.client;
-
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.security.Security;
-
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Set;
-
-import org.apache.commons.codec.binary.Hex;
-import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.cms.Attribute;
-import org.bouncycastle.asn1.cms.AttributeTable;
-import org.bouncycastle.asn1.cms.CMSAttributes;
-import org.bouncycastle.asn1.cms.ContentInfo;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.cms.CMSProcessable;
-import org.bouncycastle.cms.CMSProcessableByteArray;
-import org.bouncycastle.cms.CMSSignedData;
-import org.bouncycastle.cms.SignerInformation;
-import org.bouncycastle.cms.SignerInformationStore;
-import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
-import org.bouncycastle.jcajce.util.MessageDigestUtils;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.util.Store;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Subclass to handle signed data objects
- */
-public class SignedPIVDataObject extends PIVDataObject {
- // slf4j will thunk this through to an appropriately configured logging library
- private static final Logger s_logger = LoggerFactory.getLogger(SignedPIVDataObject.class);
- private ContentInfo m_contentInfo;
- private CMSSignedData m_asymmetricSignature;
- private Set m_dalgList;
- // The raw data over which the message digest shall be computed
- private byte[] m_signedContent;
- // The effective signing cert.
- private X509Certificate m_signerCert;
- // This will be zero or one, and reflects the number of certs in this object
- private int m_signerCertCount;
- // This will be true *only* when this object has its own cert
- private boolean m_hasOwnSignerCert;
- // Prefetch
- private byte[] m_signedAttrsDigest;
- private byte[] m_computedDigest;
- private String m_signatureAlgorithmName;
- private String m_digestAlgorithmName;
- private String m_encryptionAlgorithmName;
-
- static {
- Security.addProvider(new BouncyCastleProvider());
- }
-
- public SignedPIVDataObject() {
- super();
- m_dalgList = null;
- m_signerCert = null;
- m_signerCertCount = 0;
- m_hasOwnSignerCert = false;
- m_signedAttrsDigest = null;
- m_computedDigest = null;
- m_digestAlgorithmName = null;
- }
-
- /**
- *
- * Returns byte array with signed content
- *
- * @return Byte array with signed content buffer
- */
- public byte[] getSignedContent() {
- return m_signedContent;
- }
-
- /**
- *
- * Sets the signed content value
- *
- * @param signedContent Byte array with signed content buffer
- */
- public void setSignedContent(byte[] signedContent) {
- m_signedContent = signedContent;
- }
-
- /**
- *
- * Returns list of supported digest algorithms
- *
- * @return List of supported algorithm identifiers
- */
- public Set getDigestAlgorithms() {
- return m_dalgList;
- }
-
- /**
- *
- * Sets the signed content value
- *
- * @param signedContent Byte array with signed content buffer
- */
- public void setDigestAlgorithms(Set set) {
- m_dalgList = set;
- }
-
- /**
- *
- * Returns the signing certificate in X509Certificate object
- *
- * @return X509Certificate object containing the signing certificate
- */
- public X509Certificate getSignerCert() {
- return (m_signerCert != null) ? m_signerCert : getChuidSignerCert();
- }
-
- /**
- *
- * Sets the signing certificate
- *
- * @param signingCertificate X509Certificate object containing the signing
- * certificate
- */
- public void setSignerCert(X509Certificate signerCert) {
- m_signerCert = signerCert;
- }
-
- /**
- *
- * Returns the CHUID signer certificate of the card for this signed object
- *
- * @return X509Certificate object containing the CHUID signer cert for this card
- */
- public X509Certificate getChuidSignerCert() {
- // Cache if not already cached
- if (DataModelSingleton.getInstance().getChuidSignerCert() == null) {
- CardHolderUniqueIdentifier o = (CardHolderUniqueIdentifier) new PIVDataObject(
- APDUConstants.CARD_HOLDER_UNIQUE_IDENTIFIER_OID);
- o.decode(); // Caches it
- }
- return DataModelSingleton.getInstance().getChuidSignerCert();
- }
-
- /**
- *
- * Sets the CHUID signing certificate for this object in the event it doesn't
- * have its own signing cert (which is probably almost always).
- *
- * @param cert X509Certificate object containing the CHUID signing certificate
- */
- public void setChuidSignerCert(X509Certificate cert) {
- DataModelSingleton.getInstance().setChuidSignerCert(cert);
- }
-
- /**
- *
- * Returns ContentInfo object
- *
- * @return ContentInfo object
- */
- public ContentInfo getContentInfo() {
- return m_contentInfo;
- }
-
- /**
- *
- * Sets the ContentInfo object
- *
- * @param contentInfo ContentInfo object
- */
- public void setContentInfo(ContentInfo contentInfo) {
- m_contentInfo = contentInfo;
- }
-
- /**
- *
- * Returns the number of certs found in this object
- *
- * @return number of certs found in this object
- */
- public int getCertCount() {
- return m_signerCertCount;
- }
-
- /**
- * Gets the signed attributes message digest extracted from SignerInfo
- *
- * @return bytes in the digest
- */
-
- public byte[] getSignedAttrsDigest() {
- return m_signedAttrsDigest;
- }
-
- /**
- * Sets the extracted message digest in the signed attributes
- *
- * @param the bytes of the digest
- *
- */
- public void setSignedAttrsDigest(byte[] digest) {
- m_signedAttrsDigest = digest;
- }
-
- /**
- * Gets the computed message digest of the signed objects's content
- *
- * @return the computed message digest of the object's content
- */
-
- public byte[] getComputedDigest() {
- return m_computedDigest;
- }
-
- /**
- * Sets the computed digest of the object
- *
- * @param the bytes of the digest
- *
- * @returns the bytes of the digest
- */
-
- private void setComputedDigest(byte[] digest) {
- m_computedDigest = digest;
- }
-
- /**
- * @return the signer signature algorithm name
- */
- public String getSignatureAlgorithmName() {
- return m_signatureAlgorithmName;
- }
-
- /**
- * @param m_signatureAlgorithmName the m_signatureAlgorithmName to set
- */
- public void setSignatureAlgorithmName(String m_signatureAlgorithmName) {
- this.m_signatureAlgorithmName = m_signatureAlgorithmName;
- }
-
- /**
- *
- * Gets the message digest algorithm name extracted from the signer information
- *
- * @return the message digest algorithm name extracted from the signer
- * information
- */
- public String getDigestAlgorithmName() {
- return m_digestAlgorithmName;
- }
-
- /**
- *
- * Sets the message digest algorithm name extracted from the signer information
- * of the associated CMS.
- *
- * @param errorDetectionCode True if error Error Detection Code is present,
- * false otherwise
- */
- public void setDigestAlgorithmName(String name) {
- m_digestAlgorithmName = name;
- }
-
- /**
- *
- * Sets the message Encryption algorithm name extracted from the signer
- * information of the associated CMS.
- *
- * @param errorDetectionCode True if error Error Detection Code is present,
- * false otherwise
- */
- public void setEncryptionAlgorithmName(String name) {
- m_encryptionAlgorithmName = name;
- }
-
- /**
- *
- * Gets the message Encryption algorithm name extracted from the signer
- * information
- *
- * @return the message Encryption algorithm name extracted from the signer
- * information
- */
- public String getEncryptionAlgorithmName() {
- return m_encryptionAlgorithmName;
- }
-
- /**
- *
- * Returns CMSSignedData object containing Asymmetric Signature value
- *
- * @return CMSSignedData object containing Asymmetric Signature value
- */
- public CMSSignedData getAsymmetricSignature() {
- return m_asymmetricSignature;
- }
-
- /**
- *
- * Sets the CMSSignedData object containing Asymmetric Signature value
- *
- * @param asymmetricSignature CMSSignedData object containing Asymmetric
- * Signature value
- */
- public void setAsymmetricSignature(CMSSignedData asymmetricSignature) {
- m_asymmetricSignature = asymmetricSignature;
- }
-
- /**
- * Extracts and sets the message digest in the signed attributes
- *
- * @param the SignerInformationStore in the CMS
- *
- */
- public void setSignedAttrsDigest(SignerInformationStore signers) {
-
- if (signers != null) {
- AttributeTable at;
- // Temporarily nest these until unit test passes
- for (Iterator i = signers.getSigners().iterator(); i.hasNext();) {
- SignerInformation signer = i.next();
- at = signer.getSignedAttributes();
- if (at != null) {
- Attribute a = at.get(CMSAttributes.messageDigest); // messageDigest
- if (a != null) {
- DEROctetString dos = (DEROctetString) a.getAttrValues().getObjectAt(0);
- if (dos != null) {
- byte[] digest = dos.getOctets();
- if (digest != null) {
- m_signedAttrsDigest = digest;
- s_logger.info("Reference digest: " + Hex.encodeHexString(digest));
- } else {
- s_logger.error("Failed to extract digest");
- }
- } else {
- s_logger.error("Failed to decode octets");
- }
- } else {
- s_logger.error("Null messageDigest attribute");
- }
- } else {
- s_logger.error("Null signed attribute set");
- }
- } // End for
- } else {
- s_logger.error("Null SignerInfos");
- }
- }
-
- /**
- * Computes a digest of the received content in this object and stores it
- *
- * @param the SignerInfo of the content signer
- * @param the content to compute the digest over t
- */
-
- public void setComputedDigest(SignerInformation signer, byte[] content) {
- if (content != null) {
- try {
- AttributeTable at = signer.getSignedAttributes();
- if (at != null) {
- Attribute a = at.get(CMSAttributes.messageDigest);
- if (a != null) {
- byte[] signedContentBytes = this.getSignedContent();
-
- if (signedContentBytes != null) {
- s_logger.info("Signed content bytes: " + Hex.encodeHexString(signedContentBytes));
- String aName = MessageDigestUtils
- .getDigestName(new ASN1ObjectIdentifier(signer.getDigestAlgOID()));
- MessageDigest md = MessageDigest.getInstance(aName, "BC");
- md.update(signedContentBytes);
- byte[] digest = md.digest();
- if (digest != null) {
- setComputedDigest(digest);
- s_logger.info("Computed digest: {} ", Hex.encodeHexString(digest));
- } else {
- s_logger.error("Failed to digest content");
- }
- } else {
- String msg = "Null contentBytes";
- s_logger.error(msg);
- throw new CardClientException(msg);
- }
- } else {
- String msg = "Null messageDigest attribute";
- s_logger.error(msg);
- throw new CardClientException(msg);
- }
- } else {
- String msg = "Null signed attribute set";
- s_logger.error(msg);
- throw new CardClientException(msg);
- }
- } catch (Exception e) {
- String msg = e.getMessage();
- s_logger.error(msg);
- e.printStackTrace();
- }
- }
- }
-
- /**
- * Sets a flag indicating that this object has an embedded content signer cert.
- *
- * @param hasOwnSignerCert boolean value indicating if this object has its own
- * embedded signer cert
- */
-
- public void setHasOwnSignerCert(boolean hasOwnSignerCert) {
- m_hasOwnSignerCert = hasOwnSignerCert;
- }
-
- /**
- * Returns boolean value indicating if this object has its own embedded signer
- * cert
- *
- * @return Boolean value indicating if this object has its own embedded signer
- * cert
- */
- public boolean getHasOwnSignerCert() {
- return m_hasOwnSignerCert;
- }
-
- /**
- * Indicates whether this object has an embedded content signer cert
- *
- */
-
- public boolean hasOwnSignerCert() {
- return m_signerCertCount > 0;
- }
-
- /**
- *
- * Verifies the signature on the object.
- *
- * @return True if signature successfully verified, false otherwise
- */
- public boolean verifySignature() {
- boolean rv_result = false;
-
- CMSSignedData s;
- try {
- s = new CMSSignedData(m_contentInfo);
-
- if (m_asymmetricSignature.isDetachedSignature()) {
- CMSProcessable procesableContentBytes = new CMSProcessableByteArray(m_signedContent);
- s = new CMSSignedData(procesableContentBytes, m_contentInfo);
- }
-
- SignerInformationStore signers = s.getSignerInfos();
- if (signers.size() != 1) {
- s_logger.error("There were {} signers", signers.size());
- return rv_result;
- }
- Store certs = s.getCertificates();
- Set digAlgSet = s.getDigestAlgorithmIDs();
- Iterator dai = digAlgSet.iterator();
-
- ArrayList allowedDigestAlgOids = new ArrayList();
- allowedDigestAlgOids.add("2.16.840.1.101.3.4.2.1");
- allowedDigestAlgOids.add("2.16.840.1.101.3.4.2.2");
- String daOid = null;
-
- while (dai.hasNext()) {
- // Check against allowed signing algorithms
- AlgorithmIdentifier ai = dai.next();
- daOid = ai.getAlgorithm().getId();
- if (!allowedDigestAlgOids.contains(daOid)) {
- s_logger.error("Unsupported digest algorithm for PIV/PIV-I: {}", daOid);
- return rv_result;
- }
- break; // TODO: Should we handle multiple?
- }
-
- for (Iterator i = signers.getSigners().iterator(); i.hasNext();) {
- SignerInformation signer = i.next();
- signer = new _SignerInformation(signer);
-
- AttributeTable at = signer.getSignedAttributes();
-
- s_logger.info("There are {} signed attributes", at.size());
- // Message digest
- if (at.get(new ASN1ObjectIdentifier("1.2.840.113549.1.9.4")) == null) {
- s_logger.error("Required messageDigest attribute is missing");
- return rv_result;
- }
-
- // Content type
- if (at.get(new ASN1ObjectIdentifier("1.2.840.113549.1.9.3")) == null) {
- s_logger.error("Required contentType attribute is missing");
- return rv_result;
- }
-
- // Ensure there is a content signer certificate
- X509Certificate signerCert = getChuidSignerCert();
-
- if (signerCert == null) {
- s_logger.error("Unable to find CHUID signer certificate for {}",
- APDUConstants.oidNameMap.get(super.getOID()));
- return rv_result;
- }
-
- @SuppressWarnings("unchecked")
- Collection certCollection = certs.getMatches(signer.getSID());
- Iterator certIt = certCollection.iterator();
- if (certIt.hasNext()) {
- X509CertificateHolder certHolder = certIt.next();
- signerCert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
- // Housekeeping
- if (signerCert != null)
- setSignerCert(signerCert);
- }
-
- rv_result = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(signerCert));
- }
- } catch (CertificateException e) {
- s_logger.error("Error verifying signature on {}: {}", APDUConstants.oidNameMap.get(super.getOID()),
- e.getMessage());
- } catch (CMSException e) {
- s_logger.error("CMS exception while verifying signature on {}: {}",
- APDUConstants.oidNameMap.get(super.getOID()), e.getMessage());
- } catch (OperatorCreationException e) {
- s_logger.error("Operator exception while verifying signature on {}: {}",
- APDUConstants.oidNameMap.get(super.getOID()), e.getMessage());
- }
-
- return rv_result;
- }
-
- private class _SignerInformation extends SignerInformation {
- protected _SignerInformation(SignerInformation baseSignerInfo) {
- super(baseSignerInfo);
- }
-
- @Override
- public byte[] getEncodedSignedAttributes() throws IOException {
- return signedAttributeSet.getEncoded(ASN1Encoding.DL);
- }
- }
-}
+/**
+ *
+ */
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.Security;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.commons.codec.binary.Hex;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.cms.CMSAttributes;
+import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessable;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.jcajce.util.MessageDigestUtils;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.Store;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Subclass to handle signed data objects
+ */
+public class SignedPIVDataObject extends PIVDataObject {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(SignedPIVDataObject.class);
+ private ContentInfo m_contentInfo;
+ private CMSSignedData m_asymmetricSignature;
+ private Set m_dalgList;
+ // The raw data over which the message digest shall be computed
+ private byte[] m_signedContent;
+ // The effective signing cert.
+ private X509Certificate m_signerCert;
+ // This will be zero or one, and reflects the number of certs in this object
+ private int m_signerCertCount;
+ // This will be true *only* when this object has its own cert
+ private boolean m_hasOwnSignerCert;
+ // Prefetch
+ private byte[] m_signedAttrsDigest;
+ private byte[] m_computedDigest;
+ private String m_signatureAlgorithmName;
+ private String m_digestAlgorithmName;
+ private String m_encryptionAlgorithmName;
+
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ public SignedPIVDataObject() {
+ super();
+ m_dalgList = null;
+ m_signerCert = null;
+ m_signerCertCount = 0;
+ m_hasOwnSignerCert = false;
+ m_signedAttrsDigest = null;
+ m_computedDigest = null;
+ m_digestAlgorithmName = null;
+ }
+
+ /**
+ *
+ * Returns byte array with signed content
+ *
+ * @return Byte array with signed content buffer
+ */
+ public byte[] getSignedContent() {
+ return m_signedContent;
+ }
+
+ /**
+ *
+ * Sets the signed content value
+ *
+ * @param signedContent Byte array with signed content buffer
+ */
+ public void setSignedContent(byte[] signedContent) {
+ m_signedContent = signedContent;
+ }
+
+ /**
+ *
+ * Returns list of supported digest algorithms
+ *
+ * @return List of supported algorithm identifiers
+ */
+ public Set getDigestAlgorithms() {
+ return m_dalgList;
+ }
+
+ /**
+ *
+ * Sets the signed content value
+ *
+ * @param signedContent Byte array with signed content buffer
+ */
+ public void setDigestAlgorithms(Set set) {
+ m_dalgList = set;
+ }
+
+ /**
+ *
+ * Returns the signing certificate in X509Certificate object
+ *
+ * @return X509Certificate object containing the signing certificate
+ */
+ public X509Certificate getSignerCert() {
+ return (m_signerCert != null) ? m_signerCert : getChuidSignerCert();
+ }
+
+ /**
+ *
+ * Sets the signing certificate
+ *
+ * @param signingCertificate X509Certificate object containing the signing
+ * certificate
+ */
+ public void setSignerCert(X509Certificate signerCert) {
+ m_signerCert = signerCert;
+ }
+
+ /**
+ *
+ * Returns the CHUID signer certificate of the card for this signed object
+ *
+ * @return X509Certificate object containing the CHUID signer cert for this card
+ */
+ public X509Certificate getChuidSignerCert() {
+ // Cache if not already cached
+ if (DataModelSingleton.getInstance().getChuidSignerCert() == null) {
+ CardHolderUniqueIdentifier o = (CardHolderUniqueIdentifier) new PIVDataObject(
+ APDUConstants.CARD_HOLDER_UNIQUE_IDENTIFIER_OID);
+ o.decode(); // Caches it
+ }
+ return DataModelSingleton.getInstance().getChuidSignerCert();
+ }
+
+ /**
+ *
+ * Sets the CHUID signing certificate for this object in the event it doesn't
+ * have its own signing cert (which is probably almost always).
+ *
+ * @param cert X509Certificate object containing the CHUID signing certificate
+ */
+ public void setChuidSignerCert(X509Certificate cert) {
+ DataModelSingleton.getInstance().setChuidSignerCert(cert);
+ }
+
+ /**
+ *
+ * Returns ContentInfo object
+ *
+ * @return ContentInfo object
+ */
+ public ContentInfo getContentInfo() {
+ return m_contentInfo;
+ }
+
+ /**
+ *
+ * Sets the ContentInfo object
+ *
+ * @param contentInfo ContentInfo object
+ */
+ public void setContentInfo(ContentInfo contentInfo) {
+ m_contentInfo = contentInfo;
+ }
+
+ /**
+ *
+ * Returns the number of certs found in this object
+ *
+ * @return number of certs found in this object
+ */
+ public int getCertCount() {
+ return m_signerCertCount;
+ }
+
+ /**
+ * Gets the signed attributes message digest extracted from SignerInfo
+ *
+ * @return bytes in the digest
+ */
+
+ public byte[] getSignedAttrsDigest() {
+ return m_signedAttrsDigest;
+ }
+
+ /**
+ * Sets the extracted message digest in the signed attributes
+ *
+ * @param the bytes of the digest
+ *
+ */
+ public void setSignedAttrsDigest(byte[] digest) {
+ m_signedAttrsDigest = digest;
+ }
+
+ /**
+ * Gets the computed message digest of the signed objects's content
+ *
+ * @return the computed message digest of the object's content
+ */
+
+ public byte[] getComputedDigest() {
+ return m_computedDigest;
+ }
+
+ /**
+ * Sets the computed digest of the object
+ *
+ * @param the bytes of the digest
+ *
+ * @returns the bytes of the digest
+ */
+
+ private void setComputedDigest(byte[] digest) {
+ m_computedDigest = digest;
+ }
+
+ /**
+ * @return the signer signature algorithm name
+ */
+ public String getSignatureAlgorithmName() {
+ return m_signatureAlgorithmName;
+ }
+
+ /**
+ * @param m_signatureAlgorithmName the m_signatureAlgorithmName to set
+ */
+ public void setSignatureAlgorithmName(String m_signatureAlgorithmName) {
+ this.m_signatureAlgorithmName = m_signatureAlgorithmName;
+ }
+
+ /**
+ *
+ * Gets the message digest algorithm name extracted from the signer information
+ *
+ * @return the message digest algorithm name extracted from the signer
+ * information
+ */
+ public String getDigestAlgorithmName() {
+ return m_digestAlgorithmName;
+ }
+
+ /**
+ *
+ * Sets the message digest algorithm name extracted from the signer information
+ * of the associated CMS.
+ *
+ * @param errorDetectionCode True if error Error Detection Code is present,
+ * false otherwise
+ */
+ public void setDigestAlgorithmName(String name) {
+ m_digestAlgorithmName = name;
+ }
+
+ /**
+ *
+ * Sets the message Encryption algorithm name extracted from the signer
+ * information of the associated CMS.
+ *
+ * @param errorDetectionCode True if error Error Detection Code is present,
+ * false otherwise
+ */
+ public void setEncryptionAlgorithmName(String name) {
+ m_encryptionAlgorithmName = name;
+ }
+
+ /**
+ *
+ * Gets the message Encryption algorithm name extracted from the signer
+ * information
+ *
+ * @return the message Encryption algorithm name extracted from the signer
+ * information
+ */
+ public String getEncryptionAlgorithmName() {
+ return m_encryptionAlgorithmName;
+ }
+
+ /**
+ *
+ * Returns CMSSignedData object containing Asymmetric Signature value
+ *
+ * @return CMSSignedData object containing Asymmetric Signature value
+ */
+ public CMSSignedData getAsymmetricSignature() {
+ return m_asymmetricSignature;
+ }
+
+ /**
+ *
+ * Sets the CMSSignedData object containing Asymmetric Signature value
+ *
+ * @param asymmetricSignature CMSSignedData object containing Asymmetric
+ * Signature value
+ */
+ public void setAsymmetricSignature(CMSSignedData asymmetricSignature) {
+ m_asymmetricSignature = asymmetricSignature;
+ }
+
+ /**
+ * Extracts and sets the message digest in the signed attributes
+ *
+ * @param the SignerInformationStore in the CMS
+ *
+ */
+ public void setSignedAttrsDigest(SignerInformationStore signers) {
+
+ if (signers != null) {
+ AttributeTable at;
+ // Temporarily nest these until unit test passes
+ for (Iterator i = signers.getSigners().iterator(); i.hasNext();) {
+ SignerInformation signer = i.next();
+ at = signer.getSignedAttributes();
+ if (at != null) {
+ Attribute a = at.get(CMSAttributes.messageDigest); // messageDigest
+ if (a != null) {
+ DEROctetString dos = (DEROctetString) a.getAttrValues().getObjectAt(0);
+ if (dos != null) {
+ byte[] digest = dos.getOctets();
+ if (digest != null) {
+ m_signedAttrsDigest = digest;
+ s_logger.info("Reference digest: " + Hex.encodeHexString(digest));
+ } else {
+ s_logger.error("Failed to extract digest");
+ }
+ } else {
+ s_logger.error("Failed to decode octets");
+ }
+ } else {
+ s_logger.error("Null messageDigest attribute");
+ }
+ } else {
+ s_logger.error("Null signed attribute set");
+ }
+ } // End for
+ } else {
+ s_logger.error("Null SignerInfos");
+ }
+ }
+
+ /**
+ * Computes a digest of the received content in this object and stores it
+ *
+ * @param the SignerInfo of the content signer
+ * @param the content to compute the digest over t
+ */
+
+ public void setComputedDigest(SignerInformation signer, byte[] content) {
+ if (content != null) {
+ try {
+ AttributeTable at = signer.getSignedAttributes();
+ if (at != null) {
+ Attribute a = at.get(CMSAttributes.messageDigest);
+ if (a != null) {
+ byte[] signedContentBytes = this.getSignedContent();
+
+ if (signedContentBytes != null) {
+ s_logger.info("Signed content bytes: " + Hex.encodeHexString(signedContentBytes));
+ String aName = MessageDigestUtils
+ .getDigestName(new ASN1ObjectIdentifier(signer.getDigestAlgOID()));
+ MessageDigest md = MessageDigest.getInstance(aName, "BC");
+ md.update(signedContentBytes);
+ byte[] digest = md.digest();
+ if (digest != null) {
+ setComputedDigest(digest);
+ s_logger.info("Computed digest: {} ", Hex.encodeHexString(digest));
+ } else {
+ s_logger.error("Failed to digest content");
+ }
+ } else {
+ String msg = "Null contentBytes";
+ s_logger.error(msg);
+ throw new CardClientException(msg);
+ }
+ } else {
+ String msg = "Null messageDigest attribute";
+ s_logger.error(msg);
+ throw new CardClientException(msg);
+ }
+ } else {
+ String msg = "Null signed attribute set";
+ s_logger.error(msg);
+ throw new CardClientException(msg);
+ }
+ } catch (Exception e) {
+ String msg = e.getMessage();
+ s_logger.error(msg);
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Sets a flag indicating that this object has an embedded content signer cert.
+ *
+ * @param hasOwnSignerCert boolean value indicating if this object has its own
+ * embedded signer cert
+ */
+
+ public void setHasOwnSignerCert(boolean hasOwnSignerCert) {
+ m_hasOwnSignerCert = hasOwnSignerCert;
+ }
+
+ /**
+ * Returns boolean value indicating if this object has its own embedded signer
+ * cert
+ *
+ * @return Boolean value indicating if this object has its own embedded signer
+ * cert
+ */
+ public boolean getHasOwnSignerCert() {
+ return m_hasOwnSignerCert;
+ }
+
+ /**
+ * Indicates whether this object has an embedded content signer cert
+ *
+ */
+
+ public boolean hasOwnSignerCert() {
+ return m_signerCertCount > 0;
+ }
+
+ /**
+ *
+ * Verifies the signature on the object.
+ *
+ * @return True if signature successfully verified, false otherwise
+ */
+ public boolean verifySignature() {
+ boolean rv_result = false;
+
+ CMSSignedData s;
+ try {
+ s = new CMSSignedData(m_contentInfo);
+
+ if (m_asymmetricSignature.isDetachedSignature()) {
+ CMSProcessable procesableContentBytes = new CMSProcessableByteArray(m_signedContent);
+ s = new CMSSignedData(procesableContentBytes, m_contentInfo);
+ }
+
+ SignerInformationStore signers = s.getSignerInfos();
+ if (signers.size() != 1) {
+ s_logger.error("There were {} signers", signers.size());
+ return rv_result;
+ }
+ Store certs = s.getCertificates();
+ Set digAlgSet = s.getDigestAlgorithmIDs();
+ Iterator dai = digAlgSet.iterator();
+
+ ArrayList allowedDigestAlgOids = new ArrayList();
+ allowedDigestAlgOids.add("2.16.840.1.101.3.4.2.1");
+ allowedDigestAlgOids.add("2.16.840.1.101.3.4.2.2");
+ String daOid = null;
+
+ while (dai.hasNext()) {
+ // Check against allowed signing algorithms
+ AlgorithmIdentifier ai = dai.next();
+ daOid = ai.getAlgorithm().getId();
+ if (!allowedDigestAlgOids.contains(daOid)) {
+ s_logger.error("Unsupported digest algorithm for PIV/PIV-I: {}", daOid);
+ return rv_result;
+ }
+ break; // TODO: Should we handle multiple?
+ }
+
+ for (Iterator i = signers.getSigners().iterator(); i.hasNext();) {
+ SignerInformation signer = i.next();
+ signer = new _SignerInformation(signer);
+
+ AttributeTable at = signer.getSignedAttributes();
+
+ s_logger.info("There are {} signed attributes", at.size());
+ // Message digest
+ if (at.get(new ASN1ObjectIdentifier("1.2.840.113549.1.9.4")) == null) {
+ s_logger.error("Required messageDigest attribute is missing");
+ return rv_result;
+ }
+
+ // Content type
+ if (at.get(new ASN1ObjectIdentifier("1.2.840.113549.1.9.3")) == null) {
+ s_logger.error("Required contentType attribute is missing");
+ return rv_result;
+ }
+
+ // Ensure there is a content signer certificate
+ X509Certificate signerCert = getChuidSignerCert();
+
+ if (signerCert == null) {
+ s_logger.error("Unable to find CHUID signer certificate for {}", APDUConstants.oidNameMap.get(super.getOID()));
+ return rv_result;
+ }
+
+ @SuppressWarnings("unchecked")
+ Collection certCollection = certs.getMatches(signer.getSID());
+ Iterator certIt = certCollection.iterator();
+ if (certIt.hasNext()) {
+ X509CertificateHolder certHolder = certIt.next();
+ signerCert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
+ // Housekeeping
+ if (signerCert != null)
+ setSignerCert(signerCert);
+ }
+
+ rv_result = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(signerCert));
+ }
+ } catch (CertificateException e) {
+ s_logger.error("Error verifying signature on {}: {}", APDUConstants.oidNameMap.get(super.getOID()), e.getMessage());
+ } catch (CMSException e) {
+ s_logger.error("CMS exception while verifying signature on {}: {}", APDUConstants.oidNameMap.get(super.getOID()), e.getMessage());
+ } catch (OperatorCreationException e) {
+ s_logger.error("Operator exception while verifying signature on {}: {}", APDUConstants.oidNameMap.get(super.getOID()), e.getMessage());
+ }
+
+ return rv_result;
+ }
+
+ private class _SignerInformation extends SignerInformation {
+ protected _SignerInformation(SignerInformation baseSignerInfo) {
+ super(baseSignerInfo);
+ }
+
+ @Override
+ public byte[] getEncodedSignedAttributes() throws IOException {
+ return signedAttributeSet.getEncoded(ASN1Encoding.DL);
+ }
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/SoftTagBoundaryException.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SoftTagBoundaryException.java
similarity index 84%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/SoftTagBoundaryException.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SoftTagBoundaryException.java
index 0d4c2f69..4884cc30 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/SoftTagBoundaryException.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/SoftTagBoundaryException.java
@@ -1,58 +1,58 @@
-package gov.gsa.pivconformance.card.client;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A class for card-related exceptions
- */
-public class SoftTagBoundaryException extends Exception {
- private static final Logger s_logger = LoggerFactory.getLogger(SoftTagBoundaryException.class);
-
- /**
- *
- */
- private static final long serialVersionUID = 1L;
-
- /**
- *
- * Default constructor for CardClientException class
- *
- */
- public SoftTagBoundaryException() {
- super();
- }
-
- /**
- *
- * Constructor for CardClientException class that takes a string with exception
- * message
- *
- * @param message String with the exception message
- */
- public SoftTagBoundaryException(String message) {
- super(message);
- }
-
- /**
- *
- * Constructor for CardClientException class that takes a string with exception
- * message and a Throwable cause
- *
- * @param message String with the exception message
- * @param cause Throwable cause
- */
- public SoftTagBoundaryException(String message, Throwable cause) {
- super(message, cause);
- }
-
- /**
- *
- * Constructor for CardClientException class that takes a Throwable cause
- *
- * @param cause Throwable cause
- */
- public SoftTagBoundaryException(Throwable cause) {
- super(cause);
- }
-}
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class for card-related exceptions
+ */
+public class SoftTagBoundaryException extends Exception {
+ private static final Logger s_logger = LoggerFactory.getLogger(SoftTagBoundaryException.class);
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ * Default constructor for CardClientException class
+ *
+ */
+ public SoftTagBoundaryException() {
+ super();
+ }
+
+ /**
+ *
+ * Constructor for CardClientException class that takes a string with exception
+ * message
+ *
+ * @param message String with the exception message
+ */
+ public SoftTagBoundaryException(String message) {
+ super(message);
+ }
+
+ /**
+ *
+ * Constructor for CardClientException class that takes a string with exception
+ * message and a Throwable cause
+ *
+ * @param message String with the exception message
+ * @param cause Throwable cause
+ */
+ public SoftTagBoundaryException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ *
+ * Constructor for CardClientException class that takes a Throwable cause
+ *
+ * @param cause Throwable cause
+ */
+ public SoftTagBoundaryException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/TagBoundaryException.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/TagBoundaryException.java
similarity index 84%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/TagBoundaryException.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/TagBoundaryException.java
index c82646a1..a43608b3 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/TagBoundaryException.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/TagBoundaryException.java
@@ -1,57 +1,57 @@
-package gov.gsa.pivconformance.card.client;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A class for tag-related exceptions
- */
-public class TagBoundaryException extends Exception {
- private static final Logger s_logger = LoggerFactory.getLogger(TagBoundaryException.class);
- /**
- *
- */
- private static final long serialVersionUID = 1L;
-
- /**
- *
- * Default constructor for TagBoundaryException class
- *
- */
- public TagBoundaryException() {
- super();
- }
-
- /**
- *
- * Constructor for TagBoundaryException class that takes a string with exception
- * message
- *
- * @param message String with the exception message
- */
- public TagBoundaryException(String message) {
- super(message);
- }
-
- /**
- *
- * Constructor for TagBoundaryException class that takes a string with exception
- * message and a Throwable cause
- *
- * @param message String with the exception message
- * @param cause Throwable cause
- */
- public TagBoundaryException(String message, Throwable cause) {
- super(message, cause);
- }
-
- /**
- *
- * Constructor for TagBoundaryException class that takes a Throwable cause
- *
- * @param cause Throwable cause
- */
- public TagBoundaryException(Throwable cause) {
- super(cause);
- }
-}
+package gov.gsa.pivconformance.cardlib.card.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A class for tag-related exceptions
+ */
+public class TagBoundaryException extends Exception {
+ private static final Logger s_logger = LoggerFactory.getLogger(TagBoundaryException.class);
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ * Default constructor for TagBoundaryException class
+ *
+ */
+ public TagBoundaryException() {
+ super();
+ }
+
+ /**
+ *
+ * Constructor for TagBoundaryException class that takes a string with exception
+ * message
+ *
+ * @param message String with the exception message
+ */
+ public TagBoundaryException(String message) {
+ super(message);
+ }
+
+ /**
+ *
+ * Constructor for TagBoundaryException class that takes a string with exception
+ * message and a Throwable cause
+ *
+ * @param message String with the exception message
+ * @param cause Throwable cause
+ */
+ public TagBoundaryException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ *
+ * Constructor for TagBoundaryException class that takes a Throwable cause
+ *
+ * @param cause Throwable cause
+ */
+ public TagBoundaryException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/TestFieldResolution.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/TestFieldResolution.java
similarity index 90%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/TestFieldResolution.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/TestFieldResolution.java
index cd14e6ea..fc7fec02 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/TestFieldResolution.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/TestFieldResolution.java
@@ -1,4 +1,4 @@
-package gov.gsa.pivconformance.card.client;
+package gov.gsa.pivconformance.cardlib.card.client;
import java.lang.reflect.Field;
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/X509CertificateDataObject.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/X509CertificateDataObject.java
similarity index 89%
rename from cardlib/src/main/java/gov/gsa/pivconformance/card/client/X509CertificateDataObject.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/X509CertificateDataObject.java
index 8bff0ce0..5ab0cf94 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/card/client/X509CertificateDataObject.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/card/client/X509CertificateDataObject.java
@@ -1,24 +1,18 @@
-package gov.gsa.pivconformance.card.client;
+package gov.gsa.pivconformance.cardlib.card.client;
-import gov.gsa.pivconformance.tlv.*;
import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import gov.gsa.pivconformance.cardlib.tlv.*;
+
import java.io.InputStream;
-import java.nio.file.Paths;
-import java.nio.file.Path;
-import java.nio.file.Files;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.util.zip.GZIPInputStream;
/**
@@ -118,17 +112,17 @@ public boolean decode() {
String oid = getOID();
if (oid.compareTo(APDUConstants.X509_CERTIFICATE_FOR_PIV_AUTHENTICATION_OID) == 0)
- setContainerName("X509 Certificate for PivAuthentication");
+ setContainerName("X.509_Certificate_for_PIV_Authentication");
else if (oid
.compareTo(APDUConstants.X509_CERTIFICATE_FOR_CARD_AUTHENTICATION_OID) == 0)
- setContainerName("X509 Certificate for Card Authentication");
+ setContainerName("X.509_Certificate_for_Card_Authentication");
else if (oid
.compareTo(APDUConstants.X509_CERTIFICATE_FOR_DIGITAL_SIGNATURE_OID) == 0)
- setContainerName("X509 Certificate for Digital Signature");
+ setContainerName("X.509_Certificate_for_Digital_Signature");
else if (oid.compareTo(APDUConstants.X509_CERTIFICATE_FOR_KEY_MANAGEMENT_OID) == 0)
- setContainerName("X509 Certificate for Key Management");
+ setContainerName("X.509 Certificate_for_Key_Management");
- m_x509ArtifactCache.saveObject(APDUConstants.getFileNameForOid(oid)+ ".cer", rawCertBuf);
+ m_x509ArtifactCache.saveObject("x509-artifacts", APDUConstants.getFileNameForOid(oid)+ ".cer", rawCertBuf);
}
if (Arrays.equals(tlv2.getTag().bytes, TagConstants.ERROR_DETECTION_CODE_TAG)) {
setErrorDetectionCode(true);
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/BerTag.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/BerTag.java
new file mode 100644
index 00000000..48eb389e
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/BerTag.java
@@ -0,0 +1,61 @@
+package gov.gsa.pivconformance.cardlib.tlv;
+
+import java.util.Arrays;
+
+public class BerTag {
+ public final byte[] bytes;
+
+ /**
+ * Creates a new tag from given byte array. Similar {@link BerTag#BerTag(byte[], int, int)} but using
+ * the full array.
+ *
+ * @param aBuf to create the tag
+ */
+ public BerTag(byte[] aBuf) {
+ this(aBuf, 0, aBuf.length);
+ }
+
+ public BerTag(byte[] aBuf, int aOffset, int aLength) {
+ byte[] temp = new byte[aLength];
+ System.arraycopy(aBuf, aOffset, temp, 0, aLength);
+ bytes = temp;
+ }
+
+ public BerTag(int aFirstByte, int aSecondByte) {
+ bytes = new byte[]{(byte) (aFirstByte), (byte) aSecondByte};
+ }
+
+ public BerTag(int aFirstByte, int aSecondByte, int aFirth) {
+ bytes = new byte[]{(byte) (aFirstByte), (byte) aSecondByte, (byte) aFirth};
+ }
+
+ public BerTag(int aFirstByte) {
+ bytes = new byte[]{(byte) aFirstByte};
+ }
+
+ public boolean isConstructed() {
+ return (bytes[0] & 0x20) != 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BerTag berTag = (BerTag) o;
+
+ return Arrays.equals(bytes, berTag.bytes);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(bytes);
+ }
+
+ @Override
+ public String toString() {
+ return (isConstructed() ? "+ " : "- ") + HexUtil.toHexString(bytes, 0, bytes.length);
+ }
+}
+
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/BerTlv.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/BerTlv.java
new file mode 100644
index 00000000..fda31804
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/BerTlv.java
@@ -0,0 +1,186 @@
+package gov.gsa.pivconformance.cardlib.tlv;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ *
+ */
+public class BerTlv {
+
+ private final static Charset ASCII = Charset.forName("US-ASCII");
+
+ private final BerTag theTag;
+ private final byte[] theValue;
+ protected final List theList;
+
+ /**
+ * Creates constructed TLV
+ *
+ * @param aTag tag
+ * @param aList set of nested TLVs
+ */
+ public BerTlv(BerTag aTag, List aList) {
+ theTag = aTag;
+ theList = aList;
+ theValue = null;
+ }
+
+ public BerTlv(BerTag aTag, List aList, byte[] aValue) {
+ theTag = aTag;
+ theList = aList;
+ theValue = aValue;
+ }
+
+ /**
+ * Creates primitive TLV
+ *
+ * @param aTag tag
+ * @param aValue value as byte[]
+ */
+ public BerTlv(BerTag aTag, byte[] aValue) {
+ theTag = aTag;
+ theValue = aValue;
+ theList = null;
+ }
+
+ //
+ //
+ //
+
+ public BerTag getTag() {
+ return theTag;
+ }
+
+ public boolean isPrimitive() {
+ return !theTag.isConstructed();
+ }
+
+ public boolean hasRawValue() {
+ return theValue != null;
+ }
+
+ public boolean isConstructed() {
+ return theTag.isConstructed();
+ }
+
+ public boolean isTag(BerTag aTag) {
+ return theTag.equals(aTag);
+ }
+
+ //
+ // find
+ //
+
+ public BerTlv find(BerTag aTag) {
+ if(aTag.equals(getTag())) {
+ return this;
+ }
+
+ if(isConstructed()) {
+ if(theList == null) return null;
+ for (BerTlv tlv : theList) {
+ BerTlv ret = tlv.find(aTag);
+ if(ret!=null) {
+ return ret;
+ }
+ }
+ return null;
+ }
+ return null;
+ }
+
+ public List findAll(BerTag aTag) {
+ List list = new ArrayList();
+ if(aTag.equals(getTag())) {
+ list.add(this);
+ return list;
+ } else if(isConstructed()) {
+ for (BerTlv tlv : theList) {
+ list.addAll(tlv.findAll(aTag));
+ }
+ }
+ return list;
+ }
+
+ //
+ // getters
+ //
+
+ public String getHexValue() {
+ if(isConstructed() && theValue == null) throw new IllegalStateException("Tag is CONSTRUCTED "+ HexUtil.toHexString(theTag.bytes));
+ return HexUtil.toHexString(theValue);
+ }
+
+ /**
+ * Text value with US-ASCII charset
+ * @return text
+ */
+ public String getTextValue() {
+ return getTextValue(ASCII);
+ }
+
+ public String getTextValue(Charset aCharset) {
+ if(isConstructed()) {
+ throw new IllegalStateException("TLV is constructed");
+ }
+ return new String(theValue, aCharset);
+ }
+
+ public byte[] getBytesValue() {
+ if(isConstructed() && theValue == null) {
+ throw new IllegalStateException("TLV ["+theTag+"]is constructed");
+ }
+ return theValue;
+ }
+
+ public int getIntValue() {
+ int i=0;
+ int j=0;
+ int number = 0;
+
+ for (i = 0; i < theValue.length; i++) {
+ j=theValue[i];
+ number = number * 256 + ( j<0 ? j+=256 : j);
+ }
+ return number;
+ }
+
+ public List getValues() {
+ if(isPrimitive()) throw new IllegalStateException("Tag is PRIMITIVE");
+ return theList;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BerTlv berTlv = (BerTlv) o;
+
+ if (theTag != null ? !theTag.equals(berTlv.theTag) : berTlv.theTag != null) return false;
+ if (!Arrays.equals(theValue, berTlv.theValue)) return false;
+ return theList != null ? theList.equals(berTlv.theList) : berTlv.theList == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = theTag != null ? theTag.hashCode() : 0;
+ result = 31 * result + Arrays.hashCode(theValue);
+ result = 31 * result + (theList != null ? theList.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+
+ return "BerTlv{" +
+ "theTag=" + theTag +
+ ", theValue=" + Arrays.toString(theValue) +
+ ", theList=" + theList +
+ '}';
+ }
+
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/BerTlvBuilder.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/BerTlvBuilder.java
new file mode 100644
index 00000000..c3087329
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/BerTlvBuilder.java
@@ -0,0 +1,254 @@
+package gov.gsa.pivconformance.cardlib.tlv;
+
+
+import java.math.BigDecimal;
+import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ *
+ */
+public class BerTlvBuilder {
+
+ private static final Charset ASCII = Charset.forName("US-ASCII");
+ private static final BigDecimal HUNDRED = new BigDecimal(100);
+ private static final int DEFAULT_SIZE = 24 * 1024;
+
+ public BerTlvBuilder() {
+ this((BerTag)null);
+ }
+
+ public BerTlvBuilder(BerTag aTemplate) {
+ this(aTemplate, new byte[DEFAULT_SIZE], 0, DEFAULT_SIZE);
+ }
+
+
+ public BerTlvBuilder(BerTlvs tlvs) {
+ this((BerTag)null);
+ for (BerTlv tlv : tlvs.getList()) {
+ addBerTlv(tlv);
+ }
+ }
+
+ public BerTlvBuilder(BerTag aTemplate, byte[] aBuffer, int aOffset, int aLength) {
+ theTemplate = aTemplate;
+ theBuffer = aBuffer;
+ thePos = aOffset;
+ theBufferOffset = aOffset;
+ }
+
+ public static BerTlvBuilder from(BerTlv aTlv) {
+ if(aTlv.isConstructed()) {
+ BerTlvBuilder builder = template(aTlv.getTag());
+ for (BerTlv tlv : aTlv.theList) {
+ builder.addBerTlv(tlv);
+ }
+ return builder;
+ } else {
+ return new BerTlvBuilder().addBerTlv(aTlv);
+ }
+ }
+
+ public static BerTlvBuilder template(BerTag aTemplate) {
+ return new BerTlvBuilder(aTemplate);
+ }
+
+ public BerTlvBuilder addEmpty(BerTag aObject) {
+ return addBytes(aObject, new byte[]{}, 0, 0);
+ }
+
+ public BerTlvBuilder addByte(BerTag aObject, byte aByte) {
+ // type
+ int len = aObject.bytes.length;
+ System.arraycopy(aObject.bytes, 0, theBuffer, thePos, len);
+ thePos+=len;
+
+ // len
+ theBuffer[thePos++] = 1;
+
+ // value
+ theBuffer[thePos++] = aByte;
+ return this;
+ }
+
+ public BerTlvBuilder addAmount(BerTag aObject, BigDecimal aAmount) {
+ BigDecimal numeric = aAmount.multiply(HUNDRED);
+ StringBuilder sb = new StringBuilder(12);
+ sb.append(numeric.longValue());
+ while(sb.length() < 12) {
+ sb.insert(0, '0');
+ }
+ return addHex(aObject, sb.toString());
+ }
+
+ public BerTlvBuilder addDate(BerTag aObject, Date aDate) {
+ SimpleDateFormat format = new SimpleDateFormat("yyMMdd");
+ return addHex(aObject, format.format(aDate));
+ }
+
+ public BerTlvBuilder addTime(BerTag aObject, Date aDate) {
+ SimpleDateFormat format = new SimpleDateFormat("HHmmss");
+ return addHex(aObject, format.format(aDate));
+ }
+
+ public int build() {
+
+ if (theTemplate != null) {
+
+ int tagLen = theTemplate.bytes.length;
+ int lengthBytesCount = calculateBytesCountForLength(thePos);
+
+ // shifts array
+ System.arraycopy(theBuffer, theBufferOffset, theBuffer, tagLen + lengthBytesCount, thePos);
+
+ // copies tag
+ System.arraycopy(theTemplate.bytes, 0, theBuffer, theBufferOffset, theTemplate.bytes.length);
+
+ fillLength(theBuffer, tagLen, thePos);
+
+ thePos += tagLen + lengthBytesCount;
+ }
+ return thePos;
+ }
+
+ private void fillLength(byte[] aBuffer, int aOffset, int aLength) {
+
+ if(aLength < 0x80) {
+ aBuffer[aOffset] = (byte) aLength;
+
+ } else if (aLength <0x100) {
+ aBuffer[aOffset] = (byte) 0x81;
+ aBuffer[aOffset+1] = (byte) aLength;
+
+ } else if( aLength < 0x10000) {
+
+ aBuffer[aOffset] = (byte) 0x82;
+ aBuffer[aOffset+1] = (byte) (aLength / 0x100);
+ aBuffer[aOffset+2] = (byte) (aLength % 0x100);
+
+ } else if( aLength < 0x1000000 ) {
+ aBuffer[aOffset] = (byte) 0x83;
+ aBuffer[aOffset+1] = (byte) (aLength / 0x10000);
+ aBuffer[aOffset+2] = (byte) (aLength / 0x100);
+ aBuffer[aOffset+3] = (byte) (aLength % 0x100);
+ } else {
+ throw new IllegalStateException("length ["+aLength+"] out of range (0x1000000)");
+ }
+ }
+
+ private int calculateBytesCountForLength(int aLength) {
+ int ret;
+ if(aLength < 0x80) {
+ ret = 1;
+ } else if (aLength <0x100) {
+ ret = 2;
+ } else if( aLength < 0x10000) {
+ ret = 3;
+ } else if( aLength < 0x1000000 ) {
+ ret = 4;
+ } else {
+ throw new IllegalStateException("length ["+aLength+"] out of range (0x1000000)");
+ }
+ return ret;
+ }
+
+ public BerTlvBuilder addHex(BerTag aObject, String aHex) {
+ byte[] buffer = HexUtil.parseHex(aHex);
+ return addBytes(aObject, buffer, 0, buffer.length);
+ }
+
+ public BerTlvBuilder addBytes(BerTag aObject, byte[] aBytes) {
+ return addBytes(aObject, aBytes, 0, aBytes.length);
+ }
+
+ public BerTlvBuilder addBytes(BerTag aTag, byte[] aBytes, int aFrom, int aLength) {
+ int tagLength = aTag.bytes.length;
+ int lengthBytesCount = calculateBytesCountForLength(aLength);
+
+ // TAG
+ System.arraycopy(aTag.bytes, 0, theBuffer, thePos, tagLength);
+ thePos+=tagLength;
+
+ // LENGTH
+ fillLength(theBuffer, thePos, aLength);
+ thePos += lengthBytesCount;
+
+ // VALUE
+ System.arraycopy(aBytes, aFrom, theBuffer, thePos, aLength);
+ thePos+=aLength;
+
+ return this;
+ }
+
+ public BerTlvBuilder add(BerTlvBuilder aBuilder) {
+ byte[] array = aBuilder.buildArray();
+ System.arraycopy(array, 0, theBuffer, thePos, array.length);
+ thePos+=array.length;
+ return this;
+ }
+
+
+ public BerTlvBuilder addBerTlv(BerTlv aTlv) {
+ if(aTlv.isConstructed()) {
+ return add(from(aTlv));
+ } else {
+ return addBytes(aTlv.getTag(), aTlv.getBytesValue());
+ }
+ }
+
+ /**
+ * Add ASCII text
+ *
+ * @param aTag tag
+ * @param aText text
+ * @return builder
+ */
+ public BerTlvBuilder addText(BerTag aTag, String aText) {
+ return addText(aTag, aText, ASCII);
+ }
+
+ /**
+ * Add ASCII text
+ *
+ * @param aTag tag
+ * @param aText text
+ * @return builder
+ */
+ public BerTlvBuilder addText(BerTag aTag, String aText, Charset aCharset) {
+ byte[] buffer = aText.getBytes(aCharset);
+ return addBytes(aTag, buffer, 0, buffer.length);
+ }
+
+ public BerTlvBuilder addIntAsHex(BerTag aObject, int aCode, int aLength) {
+ StringBuilder sb = new StringBuilder(aLength*2);
+ sb.append(aCode);
+ while(sb.length() tlvs = new ArrayList();
+ if(aLen==0) return new BerTlvs(tlvs);
+
+ int offset = aOffset;
+ for(int i=0; i<100; i++) {
+ ParseResult result = parseWithResult(0, aBuf, offset, aLen-offset, false);
+ tlvs.add(result.tlv);
+
+ if(result.offset>=aOffset+aLen) {
+ break;
+ }
+
+ offset = result.offset;
+
+ }
+
+ return new BerTlvs(tlvs);
+ }
+
+ private ParseResult parseWithResult(int aLevel, byte[] aBuf, int aOffset, int aLen) {
+ return parseWithResult(aLevel, aBuf, aOffset, aLen, true);
+ }
+ private ParseResult parseWithResult(int aLevel, byte[] aBuf, int aOffset, int aLen, boolean recurse) {
+ String levelPadding = createLevelPadding(aLevel);
+ if(aOffset+aLen > aBuf.length) {
+ throw new IllegalStateException("Length is out of the range [offset="+aOffset+", len="+aLen+", array.length="+aBuf.length+", level="+aLevel+"]");
+ }
+ if(log.isDebugEnabled()) {
+ log.debug("{}parseWithResult(level={}, offset={}, len={}, buf={})", levelPadding, aLevel, aOffset, aLen, HexUtil.toFormattedHexString(aBuf, aOffset, aLen));
+ }
+
+ // tag
+ int tagBytesCount = getTagBytesCount(aBuf, aOffset);
+ BerTag tag = createTag(levelPadding, aBuf, aOffset, tagBytesCount);
+ if(log.isDebugEnabled()) {
+ log.debug("{}tag = {}, tagBytesCount={}, tagBuf={}", levelPadding, tag, tagBytesCount, HexUtil.toFormattedHexString(aBuf, aOffset, tagBytesCount));
+ }
+
+ // length
+ int lengthBytesCount = getLengthBytesCount(aBuf, aOffset + tagBytesCount);
+ int valueLength = getDataLength(aBuf, aOffset + tagBytesCount);
+
+ if(log.isDebugEnabled()) {
+ log.debug("{}lenBytesCount = {}, len = {}, lenBuf = {}"
+ , levelPadding, lengthBytesCount, valueLength, HexUtil.toFormattedHexString(aBuf, aOffset + tagBytesCount, lengthBytesCount));
+ }
+
+ // value
+ if(tag.isConstructed() && recurse) {
+
+ ArrayList list = new ArrayList();
+ addChildren(aLevel, aBuf, aOffset, levelPadding, tagBytesCount, lengthBytesCount, valueLength, list);
+
+ int resultOffset = aOffset + tagBytesCount + lengthBytesCount + valueLength;
+ if(log.isDebugEnabled()) {
+ log.debug("{}returning constructed offset = {}", levelPadding, resultOffset);
+ }
+ byte[] value = new byte[valueLength];
+ System.arraycopy(aBuf, aOffset+tagBytesCount+lengthBytesCount, value, 0, valueLength);
+ return new ParseResult(new BerTlv(tag, list, value), resultOffset);
+ } else {
+ // value
+ byte[] value = new byte[valueLength];
+ log.debug("src.length={}, srcPos={}, value.length={}, valueLength={}",
+ aBuf.length, aOffset+tagBytesCount+lengthBytesCount, value.length, valueLength);
+ System.arraycopy(aBuf, aOffset+tagBytesCount+lengthBytesCount, value, 0, valueLength);
+ int resultOffset = aOffset + tagBytesCount + lengthBytesCount + valueLength;
+ if(log.isDebugEnabled()) {
+ log.debug("{}value = {}", levelPadding, HexUtil.toFormattedHexString(value));
+ log.debug("{}returning primitive offset = {}", levelPadding, resultOffset);
+ }
+ return new ParseResult(new BerTlv(tag, value), resultOffset);
+ }
+
+ }
+
+ /**
+ *
+ * @param aLevel level for debug
+ * @param aBuf buffer
+ * @param aOffset offset (first byte)
+ * @param levelPadding level padding (for debug)
+ * @param aTagBytesCount tag bytes count
+ * @param aDataBytesCount data bytes count
+ * @param valueLength length
+ * @param list list to add
+ */
+ private void addChildren(int aLevel, byte[] aBuf, int aOffset, String levelPadding, int aTagBytesCount, int aDataBytesCount, int valueLength, ArrayList list) {
+ int startPosition = aOffset + aTagBytesCount + aDataBytesCount;
+ int len = valueLength;
+ while (startPosition <= aOffset + valueLength) {
+ ParseResult result = parseWithResult(aLevel+1, aBuf, startPosition, len);
+ list.add(result.tlv);
+
+ startPosition = result.offset;
+ len = valueLength - startPosition;
+
+ if(log.isDebugEnabled()) {
+ log.debug("{}level {}: adding {} with offset {}, startPosition={}, aDataBytesCount={}, valueLength={}"
+ , levelPadding, aLevel, result.tlv.getTag(), result.offset, startPosition, aDataBytesCount, valueLength);
+ }
+ }
+ }
+
+ private String createLevelPadding(int aLevel) {
+ if(!log.isDebugEnabled()) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for(int i=0; i3) {
+ throw new IllegalStateException(String.format("At position %d the len is more then 3 [%d]", aOffset, numberOfBytes));
+ }
+
+ length = 0;
+ for(int i=aOffset+1; i aTlvs) {
+ tlvs = aTlvs;
+ }
+
+ public BerTlv find(BerTag aTag) {
+ for (BerTlv tlv : tlvs) {
+ BerTlv found = tlv.find(aTag);
+ if(found!=null) {
+ return found;
+ }
+ }
+ return null;
+ }
+
+ public List findAll(BerTag aTag) {
+ List list = new ArrayList();
+ for (BerTlv tlv : tlvs) {
+ list.addAll(tlv.findAll(aTag));
+ }
+ return list;
+ }
+
+
+ public List getList() {
+ return tlvs;
+ }
+
+ private final List tlvs;
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BerTlvs berTlvs = (BerTlvs) o;
+
+ return tlvs != null ? tlvs.equals(berTlvs.tlvs) : berTlvs.tlvs == null;
+ }
+
+ @Override
+ public int hashCode() {
+ return tlvs != null ? tlvs.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "BerTlvs{" +
+ "tlvs=" + tlvs +
+ '}';
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/CCTTlvLogger.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/CCTTlvLogger.java
new file mode 100644
index 00000000..81ddfce5
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/CCTTlvLogger.java
@@ -0,0 +1,23 @@
+package gov.gsa.pivconformance.cardlib.tlv;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CCTTlvLogger implements IBerTlvLogger {
+
+ private Logger m_logger = null;
+ public CCTTlvLogger(Class> clazz) {
+ m_logger = LoggerFactory.getLogger(clazz.toString() + ".TLVParser");
+ }
+
+ @Override
+ public boolean isDebugEnabled() {
+ return m_logger != null && m_logger.isDebugEnabled();
+ }
+
+ @Override
+ public void debug(String aFormat, Object... args) {
+ if (m_logger == null) return;
+ //m_logger.debug(aFormat, args);
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/ContainerRuleset.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/ContainerRuleset.java
similarity index 79%
rename from cardlib/src/main/java/gov/gsa/pivconformance/tlv/ContainerRuleset.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/ContainerRuleset.java
index cf9e0569..f42a119a 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/ContainerRuleset.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/ContainerRuleset.java
@@ -1,50 +1,45 @@
-package gov.gsa.pivconformance.tlv;
-
-import java.util.HashMap;
-
-import gov.gsa.pivconformance.tlv.TagLengthRule;
-
-/**
- * Each ContainerRuleset consists of a container name and HashMap of
- * TagLengthRules for each tag in the container
- *
- */
-public class ContainerRuleset {
- private String m_containerName = null;
- private HashMap m_tagRuleset = new HashMap();
-
- public ContainerRuleset(String containerName) {
- this.m_containerName = containerName;
- this.m_tagRuleset = new HashMap();
- }
-
- /**
- * Adds a container length rule to this container's ruleset
- *
- * @param tag container tag
- * @param RULE length rule to apply
- */
- public void add(BerTag tag, TagLengthRule RULE) {
- m_tagRuleset.put(tag, RULE);
- }
-
- /**
- * Gets the list of tags and rules for this container
- *
- * @return
- */
-
- public String getContainerName() {
- return m_containerName;
- }
-
- /**
- * Gets the list of tags and rules for this container
- *
- * @return
- */
-
- public HashMap getTagRuleset() {
- return m_tagRuleset;
- }
-}
+package gov.gsa.pivconformance.cardlib.tlv;
+
+import java.util.HashMap;
+
+import gov.gsa.pivconformance.cardlib.tlv.TagLengthRule;
+
+/**
+ * Each ContainerRuleset consists of a container name and HashMap of TagLengthRules for each tag in the container
+
+ */
+public class ContainerRuleset {
+ private String m_containerName = null;
+ private HashMap m_tagRuleset = new HashMap();
+
+ public ContainerRuleset(String containerName) {
+ this.m_containerName = containerName;
+ this.m_tagRuleset = new HashMap();
+ }
+
+ /**
+ * Adds a container length rule to this container's ruleset
+ * @param tag container tag
+ * @param RULE length rule to apply
+ */
+ public void add(BerTag tag, TagLengthRule RULE) {
+ m_tagRuleset.put(tag, RULE);
+ }
+
+ /**
+ * Gets the list of tags and rules for this container
+ * @return
+ */
+
+ public String getContainerName() {
+ return m_containerName;
+ }
+ /**
+ * Gets the list of tags and rules for this container
+ * @return
+ */
+
+ public HashMap getTagRuleset() {
+ return m_tagRuleset;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/HexUtil.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/HexUtil.java
new file mode 100644
index 00000000..42062dcd
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/HexUtil.java
@@ -0,0 +1,74 @@
+package gov.gsa.pivconformance.cardlib.tlv;
+
+public class HexUtil {
+
+ private static final char[] CHARS_TABLES = "0123456789ABCDEF".toCharArray();
+ static final byte[] BYTES = new byte[128];
+
+ static {
+ for (int i = 0; i < 10; i++) {
+ BYTES['0' + i] = (byte) i;
+ BYTES['A' + i] = (byte) (10 + i);
+ BYTES['a' + i] = (byte) (10 + i);
+ }
+ }
+
+ public static String toHexString(byte[] aBytes) {
+ return toHexString(aBytes, 0, aBytes.length);
+ }
+
+ public static String toFormattedHexString(byte[] aBytes) {
+ return toFormattedHexString(aBytes, 0, aBytes.length);
+ }
+
+ public static String toHexString(byte[] aBytes, int aLength) {
+ return toHexString(aBytes, 0, aLength);
+ }
+
+ public static byte[] parseHex(String aHexString) {
+ char[] src = aHexString.replace("\n", "").replace(" ", "").toUpperCase().toCharArray();
+ byte[] dst = new byte[src.length / 2];
+
+ for (int si = 0, di = 0; di < dst.length; di++) {
+ byte high = BYTES[src[si++] & 0x7f];
+ byte low = BYTES[src[si++] & 0x7f];
+ dst[di] = (byte) ((high << 4) + low);
+ }
+
+ return dst;
+ }
+
+ public static String toFormattedHexString(byte[] aBytes, int aOffset, int aLength) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ sb.append(aLength);
+ sb.append("] :");
+ for (int si = aOffset, di = 0; si < aOffset+aLength; si++, di++) {
+ byte b = aBytes[si];
+ if (di % 4 == 0) {
+ sb.append(" ");
+ } else {
+ sb.append(' ');
+ }
+ sb.append( CHARS_TABLES[(b & 0xf0) >>> 4] );
+ sb.append( CHARS_TABLES[(b & 0x0f)] );
+
+ }
+
+ return sb.toString();
+
+ }
+
+ public static String toHexString(byte[] aBytes, int aOffset, int aLength) {
+ char[] dst = new char[aLength * 2];
+
+ for (int si = aOffset, di = 0; si < aOffset+aLength; si++) {
+ byte b = aBytes[si];
+ dst[di++] = CHARS_TABLES[(b & 0xf0) >>> 4];
+ dst[di++] = CHARS_TABLES[(b & 0x0f)];
+ }
+
+ return new String(dst);
+ }
+
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/IBerTlvLogger.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/IBerTlvLogger.java
new file mode 100644
index 00000000..c4ddb1df
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/IBerTlvLogger.java
@@ -0,0 +1,8 @@
+package gov.gsa.pivconformance.cardlib.tlv;
+
+public interface IBerTlvLogger {
+
+ boolean isDebugEnabled();
+
+ void debug(String aFormat, Object ...args);
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/TagBoundaryManager.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/TagBoundaryManager.java
similarity index 84%
rename from cardlib/src/main/java/gov/gsa/pivconformance/tlv/TagBoundaryManager.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/TagBoundaryManager.java
index 0deda974..df1eb659 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/TagBoundaryManager.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/TagBoundaryManager.java
@@ -1,289 +1,273 @@
-package gov.gsa.pivconformance.tlv;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-import org.bouncycastle.util.encoders.Hex;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import gov.gsa.pivconformance.tlv.TagLengthRule;
-import gov.gsa.pivconformance.tlv.TagLengthRule.CONSTRAINT;
-import gov.gsa.pivconformance.tlv.TagConstants;
-import gov.gsa.pivconformance.card.client.APDUConstants;
-import gov.gsa.pivconformance.card.client.CardClientException;
-import gov.gsa.pivconformance.card.client.SoftTagBoundaryException;
-import gov.gsa.pivconformance.tlv.ContainerRuleset;
-
-/**
- * This class is intended to be used side-by-side SP 800-73 for quick
- * comparison/updates to lengths.
- *
- * TODO: This class statically marks a tag as eligible per SP 800-73-4 table,
- * but until the tag is obtained, we only know that it is allowed in-contecxt if
- * the container also contains a separate signing cert. That gets determined
- * when the container is loaded and we've determined that there is or is not a
- * cert. Whether the tool looks for that needs to be flushed out.
- *
- */
-public class TagBoundaryManager {
- private static final Logger s_logger = LoggerFactory.getLogger(TagBoundaryManager.class);
- private static final HashMap m_maxLenMap = new HashMap();
-
- private void initCache() {
- /*
- * This cache gets hung from the
- * gov.gsa.pivconformance.card.client.DataModelSingleton object with a public
- * accessor getLengthRules() method.
- */
- // Handle cert containers from Table 10, 15, 16, 17, 20-39, 42 of SP 800-73-4
- ArrayList certNames = new ArrayList();
- certNames.add(APDUConstants.X509_CERTIFICATE_FOR_PIV_AUTHENTICATION_NAME);
- certNames.add(APDUConstants.X509_CERTIFICATE_FOR_CARD_AUTHENTICATION_NAME);
- certNames.add(APDUConstants.X509_CERTIFICATE_FOR_DIGITAL_SIGNATURE_NAME);
- certNames.add(APDUConstants.X509_CERTIFICATE_FOR_KEY_MANAGEMENT_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_1_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_2_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_3_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_4_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_5_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_6_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_7_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_8_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_9_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_10_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_11_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_12_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_13_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_14_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_15_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_16_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_17_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_18_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_19_NAME);
- certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_20_NAME);
- certNames.add(APDUConstants.SECURE_MESSAGING_CERTIFICATE_SIGNER_NAME);
-
- // SP800-73-4 Part 1, Table 8. Card Capability Container tags
- ContainerRuleset crs = new ContainerRuleset(APDUConstants.CARD_CAPABILITY_CONTAINER_NAME);
- crs.add(new BerTag(TagConstants.CARD_IDENTIFIER_TAG), new TagLengthRule(CONSTRAINT.OR, 0, 21));
- crs.add(new BerTag(TagConstants.CAPABILITY_CONTAINER_VERSION_NUMBER_TAG),
- new TagLengthRule(CONSTRAINT.OR, 0, 1));
- crs.add(new BerTag(TagConstants.CAPABILITY_GRAMMAR_VERSION_NUMBER_TAG), new TagLengthRule(CONSTRAINT.OR, 0, 1));
- crs.add(new BerTag(TagConstants.APPLICATIONS_CARDURL_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 128));
- crs.add(new BerTag(TagConstants.PKCS15_TAG), new TagLengthRule(CONSTRAINT.OR, 0, 1));
- crs.add(new BerTag(TagConstants.REGISTERED_DATA_MODEL_NUMBER_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
- crs.add(new BerTag(TagConstants.ACCESS_CONTROL_RULE_TABLE_TAG), new TagLengthRule(CONSTRAINT.OR, 0, 17));
- crs.add(new BerTag(TagConstants.CARD_APDUS_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- crs.add(new BerTag(TagConstants.REDIRECTION_TAG_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- crs.add(new BerTag(TagConstants.CAPABILITY_TUPLES_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- crs.add(new BerTag(TagConstants.STATUS_TUPLES_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- crs.add(new BerTag(TagConstants.NEXT_CCC_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- crs.add(new BerTag(TagConstants.EXTENDED_APPLICATION_CARDURL_TAG), new TagLengthRule(CONSTRAINT.FIXED, 48, 48));
- crs.add(new BerTag(TagConstants.SECURITY_OBJECT_BUFFER_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 48));
- crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- m_maxLenMap.put(crs.getContainerName(), crs);
-
- // SP800-73-4 Part 1, Table 9. Card Holder Unique Identifier tags
- crs = new ContainerRuleset(APDUConstants.CARD_HOLDER_UNIQUE_IDENTIFIER_NAME);
- crs.add(new BerTag(TagConstants.BUFFER_LENGTH_TAG), new TagLengthRule(CONSTRAINT.FIXED, 2, 2));
- crs.add(new BerTag(TagConstants.FASC_N_TAG), new TagLengthRule(CONSTRAINT.FIXED, 25, 25));
- crs.add(new BerTag(TagConstants.ORGANIZATIONAL_IDENTIFIER_TAG), new TagLengthRule(CONSTRAINT.FIXED, 4, 4));
- crs.add(new BerTag(TagConstants.DUNS_TAG), new TagLengthRule(CONSTRAINT.FIXED, 9, 9));
- crs.add(new BerTag(TagConstants.GUID_TAG), new TagLengthRule(CONSTRAINT.FIXED, 16, 16));
- crs.add(new BerTag(TagConstants.CHUID_EXPIRATION_DATE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 8, 8));
- crs.add(new BerTag(TagConstants.CARDHOLDER_UUID_TAG), new TagLengthRule(CONSTRAINT.FIXED, 16, 16));
- crs.add(new BerTag(TagConstants.ISSUER_ASYMMETRIC_SIGNATURE_TAG),
- new TagLengthRule(CONSTRAINT.VARIABLE, 0, 3200, true));
- crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- m_maxLenMap.put(crs.getContainerName(), crs);
-
- // Handle cert containers from Table 10, 15, 16, 17, 20-39, 42 of SP 800-73-4
-
- for (String cn : certNames) {
- crs = new ContainerRuleset(cn);
- crs.add(new BerTag(TagConstants.CERTIFICATE_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 1858, true));
- crs.add(new BerTag(TagConstants.CERTINFO_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
- crs.add(new BerTag(TagConstants.MSCUID_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 38));
- crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- m_maxLenMap.put(crs.getContainerName(), crs);
- }
-
- // SP 800-73-4 Part 1, Table 11. Cardholder Fingerprints
- crs = new ContainerRuleset(APDUConstants.CARDHOLDER_FINGERPRINTS_NAME);
- crs.add(new BerTag(TagConstants.FINGERPRINT_I_AND_II_TAG),
- new TagLengthRule(CONSTRAINT.VARIABLE, 88, 4000, true));
- crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- m_maxLenMap.put(crs.getContainerName(), crs);
-
- // SP 800-73-4 Part 1, Table 12. Security Object
- crs = new ContainerRuleset(APDUConstants.SECURITY_OBJECT_NAME);
- crs.add(new BerTag(TagConstants.MAPPING_OF_DG_TO_CONTAINER_ID_TAG),
- new TagLengthRule(CONSTRAINT.VARIABLE, 0, 30));
- crs.add(new BerTag(TagConstants.SECURITY_OBJECT_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 1298));
- crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- m_maxLenMap.put(crs.getContainerName(), crs);
-
- // SP 800-73-4 Part 1, Table 13. Cardholder Facial Image
- crs = new ContainerRuleset(APDUConstants.CARDHOLDER_FACIAL_IMAGE_NAME);
- crs.add(new BerTag(TagConstants.IMAGE_FOR_VISUAL_VERIFICATION_TAG),
- new TagLengthRule(CONSTRAINT.VARIABLE, 0, 12704, true));
- crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- m_maxLenMap.put(crs.getContainerName(), crs);
-
- // SP 800-73-4 Part 1, Table 14. Printed Information tags
- crs = new ContainerRuleset(APDUConstants.PRINTED_INFORMATION_NAME);
- crs.add(new BerTag(TagConstants.NAME_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 125));
- crs.add(new BerTag(TagConstants.EMPLOYEE_AFFILIATION_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 20));
- crs.add(new BerTag(TagConstants.PRINTED_INFORMATION_EXPIRATION_DATE_TAG),
- new TagLengthRule(CONSTRAINT.FIXED, 9, 9));
- crs.add(new BerTag(TagConstants.AGENCY_CARD_SERIAL_NUMBER_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 20));
- crs.add(new BerTag(TagConstants.ISSUER_IDENTIFICATION_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 15));
- crs.add(new BerTag(TagConstants.ORGANIZATIONAL_AFFILIATION_L1_TAG),
- new TagLengthRule(CONSTRAINT.VARIABLE, 0, 20));
- crs.add(new BerTag(TagConstants.ORGANIZATIONAL_AFFILIATION_L2_TAG),
- new TagLengthRule(CONSTRAINT.VARIABLE, 0, 20));
- crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- m_maxLenMap.put(crs.getContainerName(), crs);
-
- // SP 800-73-4 Part 1, Table 18. Discovery Object
- crs = new ContainerRuleset(APDUConstants.DISCOVERY_OBJECT_NAME);
- crs.add(new BerTag(TagConstants.PIN_USAGE_POLICY_TAG), new TagLengthRule(CONSTRAINT.FIXED, 2, 2));
- crs.add(new BerTag(TagConstants.PIV_CARD_APPLICATION_AID_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 10, 12));
- m_maxLenMap.put(crs.getContainerName(), crs);
-
- // SP 800-73-4 Part 1, Table 19. Key History
- crs = new ContainerRuleset(APDUConstants.KEY_HISTORY_OBJECT_NAME);
- crs.add(new BerTag(TagConstants.KEYS_WITH_ON_CARD_CERTS_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
- crs.add(new BerTag(TagConstants.KEYS_WITH_OFF_CARD_CERTS_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
- // TODO: Handle conditional hmmm...
- crs.add(new BerTag(TagConstants.OFF_CARD_CERT_URL_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 118));
- crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- m_maxLenMap.put(crs.getContainerName(), crs);
-
- // SP 800-73-4 Part 1, Table 40. Cardholder Iris Images
- crs = new ContainerRuleset(APDUConstants.CARDHOLDER_IRIS_IMAGES_NAME);
- crs.add(new BerTag(TagConstants.IMAGES_FOR_IRIS_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 7100, true));
- crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- m_maxLenMap.put(crs.getContainerName(), crs);
-
- // SP 800-73-4 Part 1, Table 41. Biometric Information Templates Group Template
- crs = new ContainerRuleset(APDUConstants.BIOMETRIC_INFORMATION_TEMPLATES_GROUP_TEMPLATE_NAME);
- crs.add(new BerTag(TagConstants.NUMBER_OF_FINGERS_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
- crs.add(new BerTag(TagConstants.BIT_FOR_FIRST_FINGER_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 28));
- crs.add(new BerTag(TagConstants.BIT_FOR_SECOND_FINGER_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 28));
- crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- m_maxLenMap.put(crs.getContainerName(), crs);
-
- // SP 800-73-4 Part 1, Table 43. Pairing Code Reference Data
- crs = new ContainerRuleset(APDUConstants.PAIRING_CODE_REFERENCE_DATA_CONTAINER_NAME);
- crs.add(new BerTag(TagConstants.PAIRING_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
- crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
- m_maxLenMap.put(crs.getContainerName(), crs);
- }
-
- /*
- * Public constructor
- */
-
- public TagBoundaryManager() {
- initCache();
- }
-
- /**
- * Gets the tag length rules
- *
- * @param name container name
- * @return HashMap of TagLengthRule objects, one for each tag
- */
- private HashMap getTagLengthRules(String name) {
- ContainerRuleset cr = m_maxLenMap.get(name);
- HashMap tlr = null;
- tlr = cr.getTagRuleset();
- return tlr;
- }
-
- /**
- * Determines whether the length of the byte array corresponding to the tag
- * falls within the length boundaries for that container
- *
- * @param tag the element's tag
- * @param byteLength computed by adding value lengths from the value length
- * under test
- * @return the difference between the prescribed lengths and the value length,
- * hopefully all bits clear
- * @throws NullPointerException
- * @throws CardClientException
- */
- public int lengthDelta(String containerName, BerTag tag, int bytesLength)
- throws NullPointerException, CardClientException, SoftTagBoundaryException {
- int rv = -1;
- HashMap tlRules = getTagLengthRules(containerName);
- if (tlRules == null) {
- String errStr = (String.format("Rules for container %s, tag 0x%02x is null", containerName,
- Hex.toHexString(tag.bytes)));
- s_logger.error(errStr);
- NullPointerException e = new NullPointerException(errStr);
- throw (e);
- }
- TagLengthRule tlr = tlRules.get(tag);
- int hi = tlr.getHighVal();
- int lo = tlr.getLowVal();
- CONSTRAINT rule = tlr.getRule();
- if ((rule = tlr.getRule()) != null) {
- switch (rule) {
- case VARIABLE:
- // When there's a range, negative indicates below floor,
- // positive indicates above ceiling, zero indicates in range.
- if (bytesLength >= lo && bytesLength <= hi) {
- rv = 0; // Pass
- } else if (bytesLength < lo) {
- rv = lo - bytesLength;
- } else {
- rv = bytesLength - hi;
- ;
- }
- break;
-
- case OR:
- // Here, we want the return value to indicate what didn't match
- if (bytesLength == lo || bytesLength == hi) {
- rv = 0; // Pass
- } else {
- rv = ((bytesLength != lo) ? 1 : 0) << 1;
- rv |= (bytesLength != hi) ? 1 : 0;
- }
- break;
- case FIXED:
- if (bytesLength == lo && bytesLength == hi) { // Check for typos in maxLenMap i suppose
- rv = 0; // Pass
- }
- break;
- default: // Let's fail the programmer
- String errStr = String.format("Rule for %s, container %s has unknown rule", Hex.toHexString(tag.bytes));
- s_logger.error(errStr);
- break;
- }
- }
- if (rv != 0) {
- if (tlr.hasSoftUpperBound()) {
- String errStr = String.format("Container %s, Tag %s varies from SP 800-73-4 table by %d", containerName,
- Hex.toHexString(tag.bytes), rv);
- try {
- rv = 0; // TODO: Here, we should *really* be checking a boolean m_signerCertEmbedded
- // flag.
- errStr += " (ignored due to tag container rule)";
- throw new SoftTagBoundaryException(errStr);
- } catch (SoftTagBoundaryException e) {
- s_logger.error(errStr);
- }
- } else {
- String errStr = String.format("Container %s, Tag %s varies from SP 800-73-4 table by %d", containerName,
- Hex.toHexString(tag.bytes), rv);
- s_logger.error(errStr);
- throw new CardClientException(errStr);
- }
- }
- return rv;
- }
-}
+package gov.gsa.pivconformance.cardlib.tlv;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.bouncycastle.util.encoders.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import gov.gsa.pivconformance.cardlib.tlv.TagLengthRule;
+import gov.gsa.pivconformance.cardlib.tlv.TagLengthRule.CONSTRAINT;
+import gov.gsa.pivconformance.cardlib.tlv.TagConstants;
+import gov.gsa.pivconformance.cardlib.card.client.APDUConstants;
+import gov.gsa.pivconformance.cardlib.card.client.CardClientException;
+import gov.gsa.pivconformance.cardlib.card.client.SoftTagBoundaryException;
+import gov.gsa.pivconformance.cardlib.tlv.ContainerRuleset;
+/**
+ * This class is intended to be used side-by-side SP 800-73 for quick comparison/updates to
+ * lengths.
+ *
+ * TODO: This class statically marks a tag as eligible per SP 800-73-4 table, but until the tag is obtained,
+ * we only know that it is allowed in-contecxt if the container also contains a separate signing cert. That gets
+ * determined when the container is loaded and we've determined that there is or is not a cert. Whether the tool looks
+ * for that needs to be flushed out.
+ *
+ */
+public class TagBoundaryManager {
+ private static final Logger s_logger = LoggerFactory.getLogger(TagBoundaryManager.class);
+ private static final HashMap m_maxLenMap = new HashMap();
+
+ private void initCache() {
+ /*
+ * This cache gets hung from the gov.gsa.pivconformance.card.client.DataModelSingleton object
+ * with a public accessor getLengthRules() method.
+ */
+ // Handle cert containers from Table 10, 15, 16, 17, 20-39, 42 of SP 800-73-4
+ ArrayList certNames = new ArrayList();
+ certNames.add(APDUConstants.X509_CERTIFICATE_FOR_PIV_AUTHENTICATION_NAME);
+ certNames.add(APDUConstants.X509_CERTIFICATE_FOR_CARD_AUTHENTICATION_NAME);
+ certNames.add(APDUConstants.X509_CERTIFICATE_FOR_DIGITAL_SIGNATURE_NAME);
+ certNames.add(APDUConstants.X509_CERTIFICATE_FOR_KEY_MANAGEMENT_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_1_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_2_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_3_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_4_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_5_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_6_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_7_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_8_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_9_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_10_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_11_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_12_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_13_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_14_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_15_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_16_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_17_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_18_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_19_NAME);
+ certNames.add(APDUConstants.RETIRED_X_509_CERTIFICATE_FOR_KEY_MANAGEMENT_20_NAME);
+ certNames.add(APDUConstants.SECURE_MESSAGING_CERTIFICATE_SIGNER_NAME);
+
+ // SP800-73-4 Part 1, Table 8. Card Capability Container tags
+ ContainerRuleset crs = new ContainerRuleset(APDUConstants.CARD_CAPABILITY_CONTAINER_NAME);
+ crs.add(new BerTag(TagConstants.CARD_IDENTIFIER_TAG), new TagLengthRule(CONSTRAINT.OR, 0, 21));
+ crs.add(new BerTag(TagConstants.CAPABILITY_CONTAINER_VERSION_NUMBER_TAG), new TagLengthRule(CONSTRAINT.OR, 0, 1));
+ crs.add(new BerTag(TagConstants.CAPABILITY_GRAMMAR_VERSION_NUMBER_TAG), new TagLengthRule(CONSTRAINT.OR, 0, 1));
+ crs.add(new BerTag(TagConstants.APPLICATIONS_CARDURL_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 128));
+ crs.add(new BerTag(TagConstants.PKCS15_TAG), new TagLengthRule(CONSTRAINT.OR, 0, 1));
+ crs.add(new BerTag(TagConstants.REGISTERED_DATA_MODEL_NUMBER_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
+ crs.add(new BerTag(TagConstants.ACCESS_CONTROL_RULE_TABLE_TAG), new TagLengthRule(CONSTRAINT.OR, 0, 17));
+ crs.add(new BerTag(TagConstants.CARD_APDUS_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ crs.add(new BerTag(TagConstants.REDIRECTION_TAG_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ crs.add(new BerTag(TagConstants.CAPABILITY_TUPLES_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ crs.add(new BerTag(TagConstants.STATUS_TUPLES_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ crs.add(new BerTag(TagConstants.NEXT_CCC_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ crs.add(new BerTag(TagConstants.EXTENDED_APPLICATION_CARDURL_TAG), new TagLengthRule(CONSTRAINT.FIXED, 48, 48));
+ crs.add(new BerTag(TagConstants.SECURITY_OBJECT_BUFFER_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 48));
+ crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+
+ // SP800-73-4 Part 1, Table 9. Card Holder Unique Identifier tags
+ crs = new ContainerRuleset(APDUConstants.CARD_HOLDER_UNIQUE_IDENTIFIER_NAME);
+ crs.add(new BerTag(TagConstants.BUFFER_LENGTH_TAG), new TagLengthRule(CONSTRAINT.FIXED, 2, 2));
+ crs.add(new BerTag(TagConstants.FASC_N_TAG), new TagLengthRule(CONSTRAINT.FIXED, 25, 25));
+ crs.add(new BerTag(TagConstants.ORGANIZATIONAL_IDENTIFIER_TAG), new TagLengthRule(CONSTRAINT.FIXED, 4, 4));
+ crs.add(new BerTag(TagConstants.DUNS_TAG), new TagLengthRule(CONSTRAINT.FIXED, 9, 9));
+ crs.add(new BerTag(TagConstants.GUID_TAG), new TagLengthRule(CONSTRAINT.FIXED, 16, 16));
+ crs.add(new BerTag(TagConstants.CHUID_EXPIRATION_DATE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 8, 8));
+ crs.add(new BerTag(TagConstants.CARDHOLDER_UUID_TAG), new TagLengthRule(CONSTRAINT.FIXED, 16, 16));
+ crs.add(new BerTag(TagConstants.ISSUER_ASYMMETRIC_SIGNATURE_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 3200, true));
+ crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+
+ // Handle cert containers from Table 10, 15, 16, 17, 20-39, 42 of SP 800-73-4
+
+ for (String cn : certNames) {
+ crs = new ContainerRuleset(cn);
+ crs.add(new BerTag(TagConstants.CERTIFICATE_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 1858, true));
+ crs.add(new BerTag(TagConstants.CERTINFO_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
+ crs.add(new BerTag(TagConstants.MSCUID_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 38));
+ crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+ }
+
+ // SP 800-73-4 Part 1, Table 11. Cardholder Fingerprints
+ crs = new ContainerRuleset(APDUConstants.CARDHOLDER_FINGERPRINTS_NAME);
+ crs.add(new BerTag(TagConstants.FINGERPRINT_I_AND_II_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 88, 4000, true));
+ crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+
+ // SP 800-73-4 Part 1, Table 12. Security Object
+ crs = new ContainerRuleset(APDUConstants.SECURITY_OBJECT_NAME);
+ crs.add(new BerTag(TagConstants.MAPPING_OF_DG_TO_CONTAINER_ID_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 30));
+ crs.add(new BerTag(TagConstants.SECURITY_OBJECT_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 1298));
+ crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+
+ // SP 800-73-4 Part 1, Table 13. Cardholder Facial Image
+ crs = new ContainerRuleset(APDUConstants.CARDHOLDER_FACIAL_IMAGE_NAME);
+ crs.add(new BerTag(TagConstants.IMAGE_FOR_VISUAL_VERIFICATION_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 12704, true));
+ crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+
+ // SP 800-73-4 Part 1, Table 14. Printed Information tags
+ crs = new ContainerRuleset(APDUConstants.PRINTED_INFORMATION_NAME);
+ crs.add(new BerTag(TagConstants.NAME_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 125));
+ crs.add(new BerTag(TagConstants.EMPLOYEE_AFFILIATION_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 20));
+ crs.add(new BerTag(TagConstants.PRINTED_INFORMATION_EXPIRATION_DATE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 9, 9));
+ crs.add(new BerTag(TagConstants.AGENCY_CARD_SERIAL_NUMBER_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 20));
+ crs.add(new BerTag(TagConstants.ISSUER_IDENTIFICATION_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 15));
+ crs.add(new BerTag(TagConstants.ORGANIZATIONAL_AFFILIATION_L1_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 20));
+ crs.add(new BerTag(TagConstants.ORGANIZATIONAL_AFFILIATION_L2_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 20));
+ crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+
+ // SP 800-73-4 Part 1, Table 18. Discovery Object
+ crs = new ContainerRuleset(APDUConstants.DISCOVERY_OBJECT_NAME);
+ crs.add(new BerTag(TagConstants.PIN_USAGE_POLICY_TAG), new TagLengthRule(CONSTRAINT.FIXED, 2, 2));
+ crs.add(new BerTag(TagConstants.PIV_CARD_APPLICATION_AID_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 10, 12));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+
+ // SP 800-73-4 Part 1, Table 19. Key History
+ crs = new ContainerRuleset(APDUConstants.KEY_HISTORY_OBJECT_NAME);
+ crs.add(new BerTag(TagConstants.KEYS_WITH_ON_CARD_CERTS_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
+ crs.add(new BerTag(TagConstants.KEYS_WITH_OFF_CARD_CERTS_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
+ // TODO: Handle conditional hmmm...
+ crs.add(new BerTag(TagConstants.OFF_CARD_CERT_URL_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 118));
+ crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+
+ // SP 800-73-4 Part 1, Table 40. Cardholder Iris Images
+ crs = new ContainerRuleset(APDUConstants.CARDHOLDER_IRIS_IMAGES_NAME);
+ crs.add(new BerTag(TagConstants.IMAGES_FOR_IRIS_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 7100, true));
+ crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+
+ // SP 800-73-4 Part 1, Table 41. Biometric Information Templates Group Template
+ crs = new ContainerRuleset(APDUConstants.BIOMETRIC_INFORMATION_TEMPLATES_GROUP_TEMPLATE_NAME);
+ crs.add(new BerTag(TagConstants.NUMBER_OF_FINGERS_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
+ crs.add(new BerTag(TagConstants.BIT_FOR_FIRST_FINGER_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 28));
+ crs.add(new BerTag(TagConstants.BIT_FOR_SECOND_FINGER_TAG), new TagLengthRule(CONSTRAINT.VARIABLE, 0, 28));
+ crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+
+ // SP 800-73-4 Part 1, Table 43. Pairing Code Reference Data
+ crs = new ContainerRuleset(APDUConstants.PAIRING_CODE_REFERENCE_DATA_CONTAINER_NAME);
+ crs.add(new BerTag(TagConstants.PAIRING_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 1, 1));
+ crs.add(new BerTag(TagConstants.ERROR_DETECTION_CODE_TAG), new TagLengthRule(CONSTRAINT.FIXED, 0, 0));
+ m_maxLenMap.put(crs.getContainerName(), crs);
+ }
+
+ /*
+ * Public constructor
+ */
+
+ public TagBoundaryManager() {
+ initCache();
+ }
+
+ /**
+ * Gets the tag length rules
+ *
+ * @param name container name
+ * @return HashMap of TagLengthRule objects, one for each tag
+ */
+ private HashMap getTagLengthRules(String name) {
+ ContainerRuleset cr = m_maxLenMap.get(name);
+ HashMap tlr = null;
+ tlr = cr.getTagRuleset();
+ return tlr;
+ }
+
+ /**
+ * Determines whether the length of the byte array corresponding to the tag
+ * falls within the length boundaries for that container
+ *
+ * @param tag the element's tag
+ * @param byteLength computed by adding value lengths from the value length
+ * under test
+ * @return the difference between the prescribed lengths and the value length,
+ * hopefully all bits clear
+ * @throws NullPointerException
+ * @throws CardClientException
+ */
+ public int lengthDelta(String containerName, BerTag tag, int bytesLength) throws NullPointerException, CardClientException, SoftTagBoundaryException {
+ int rv = -1;
+ HashMap tlRules = getTagLengthRules(containerName);
+ if (tlRules == null) {
+ String errStr = (String.format("Rules for container %s, tag 0x%02x is null", containerName, Hex.toHexString(tag.bytes)));
+ s_logger.error(errStr);
+ NullPointerException e = new NullPointerException(errStr);
+ throw (e);
+ }
+ TagLengthRule tlr = tlRules.get(tag);
+ int hi = tlr.getHighVal();
+ int lo = tlr.getLowVal();
+ CONSTRAINT rule = tlr.getRule();
+ if((rule = tlr.getRule()) != null) {
+ switch (rule) {
+ case VARIABLE:
+ // When there's a range, negative indicates below floor,
+ // positive indicates above ceiling, zero indicates in range.
+ if (bytesLength >= lo && bytesLength <= hi) {
+ rv = 0; // Pass
+ } else if (bytesLength < lo) {
+ rv = lo - bytesLength;
+ } else {
+ rv = bytesLength - hi;;
+ }
+ break;
+
+ case OR:
+ // Here, we want the return value to indicate what didn't match
+ if (bytesLength == lo || bytesLength == hi) {
+ rv = 0; // Pass
+ } else {
+ rv = ((bytesLength != lo) ? 1 : 0) << 1;
+ rv |= (bytesLength != hi) ? 1 : 0;
+ }
+ break;
+ case FIXED:
+ if (bytesLength == lo && bytesLength == hi) { // Check for typos in maxLenMap i suppose
+ rv = 0; // Pass
+ }
+ break;
+ default: // Let's fail the programmer
+ String errStr = String.format("Rule for %s, container %s has unknown rule", Hex.toHexString(tag.bytes));
+ s_logger.error(errStr);
+ break;
+ }
+ }
+ if (rv != 0) {
+ if (tlr.hasSoftUpperBound()) {
+ String errStr = String.format("Container %s, Tag %s varies from SP 800-73-4 table by %d",
+ containerName, Hex.toHexString(tag.bytes), rv);
+ try {
+ rv = 0; // TODO: Here, we should *really* be checking a boolean m_signerCertEmbedded flag.
+ errStr += " (ignored due to tag container rule)";
+ throw new SoftTagBoundaryException(errStr);
+ } catch (SoftTagBoundaryException e) {
+ s_logger.error(errStr);
+ }
+ } else {
+ String errStr = String.format("Container %s, Tag %s varies from SP 800-73-4 table by %d",
+ containerName, Hex.toHexString(tag.bytes), rv); s_logger.error(errStr);
+ throw new CardClientException(errStr);
+ }
+ }
+ return rv;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/TagConstants.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/TagConstants.java
similarity index 98%
rename from cardlib/src/main/java/gov/gsa/pivconformance/tlv/TagConstants.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/TagConstants.java
index c1fc9cd3..a8202487 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/TagConstants.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/TagConstants.java
@@ -1,4 +1,4 @@
-package gov.gsa.pivconformance.tlv;
+package gov.gsa.pivconformance.cardlib.tlv;
import java.util.ArrayList;
import java.util.HashMap;
@@ -93,9 +93,9 @@ public static final List AllCCCTags() {
public static final byte[] ORGANIZATIONAL_IDENTIFIER_TAG = { 0x32 };
public static final byte[] DUNS_TAG = { 0x33 };
public static final byte[] GUID_TAG = { 0x34 };
- public static final byte[] CHUID_EXPIRATION_DATE_TAG = { 0x35 };
+ public static final byte[] CHUID_EXPIRATION_DATE_TAG = { 0x35 };
public static final byte[] CARDHOLDER_UUID_TAG = { 0x36 };
- public static final byte[] DEPRECATED_AUTHENTICATION_KEY_MAP = { 0x3D };
+ public static final byte[] DEPRECATED_AUTHENTICATION_KEY_MAP ={ 0x3D };
public static final byte[] ISSUER_ASYMMETRIC_SIGNATURE_TAG = { 0x3E };
/**
@@ -127,7 +127,7 @@ public static final List AllCHUIDTags() {
public static final byte[] SECURITY_OBJECT_TAG = { (byte) 0xBB };
// SP800-73-4 Part 1, Table 13. Card Holder Facial Image Tags
- public static final byte[] IMAGE_FOR_VISUAL_VERIFICATION_TAG = { (byte) 0xBC };
+ public static final byte[] IMAGE_FOR_VISUAL_VERIFICATION_TAG = { (byte) 0xBC };
// SP800-73-4 Part 1, Table 14. Printed Information Tags
public static final byte[] NAME_TAG = { 0x01 };
@@ -245,3 +245,4 @@ public static final List AllPrintedInfoTags() {
}
};
}
+
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/TagLengthRule.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/TagLengthRule.java
similarity index 85%
rename from cardlib/src/main/java/gov/gsa/pivconformance/tlv/TagLengthRule.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/TagLengthRule.java
index b5ff849a..656e7514 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/TagLengthRule.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tlv/TagLengthRule.java
@@ -1,79 +1,79 @@
-/**
- *
- */
-package gov.gsa.pivconformance.tlv;
-
-/**
- * One of three classes to encapsulate the rather fuzzy max lengths per Tables
- * 8-43 in SP 800-73-4 TODO: Add logic to account for embedded content signing
- * certs in biometrics.
- */
-public class TagLengthRule {
- private CONSTRAINT m_rule;
- private int m_lowVal;
- private int m_highVal;
- private boolean m_softUpperBound;
-
- // Private constructor
- public TagLengthRule(CONSTRAINT rule, int lowVal, int highVal, boolean softUpperBound) {
- m_rule = rule;
- m_lowVal = lowVal;
- m_highVal = highVal;
- m_softUpperBound = softUpperBound;
- }
-
- // Additional constructor
- public TagLengthRule(CONSTRAINT rule, int lowVal, int highVal) {
- m_rule = rule;
- m_lowVal = lowVal;
- m_highVal = highVal;
- m_softUpperBound = false;
- }
-
- /**
- * Eunumeration used to compute lengths of TLV values
- *
- */
- public static enum CONSTRAINT {
- FIXED, OR, VARIABLE
- };
-
- /*
- * Provides the RULE (for lack of a better word at the time) of the rule
- *
- * @return the RULE
- */
-
- public CONSTRAINT getRule() {
- return m_rule;
- }
-
- /*
- * Provides the low value in the rule
- *
- * @return the low value in the rule
- */
- public int getLowVal() {
- return m_lowVal;
- }
-
- /*
- * Provides the high value in the rule
- *
- * @return the high value in the rule
- */
-
- public int getHighVal() {
- return m_highVal;
- }
-
- /*
- * Indicates if a soft boundary exists (due to an embedded CMS or signing cert)
- *
- * @return whether this value is eligible to have a soft upper bound
- */
-
- public boolean hasSoftUpperBound() {
- return m_softUpperBound;
- }
-}
+/**
+ *
+ */
+package gov.gsa.pivconformance.cardlib.tlv;
+
+/**
+ * One of three classes to encapsulate the rather fuzzy max lengths per Tables 8-43 in SP
+ * 800-73-4 TODO: Add logic to account for embedded content signing certs in
+ * biometrics.
+ */
+public class TagLengthRule {
+ private CONSTRAINT m_rule;
+ private int m_lowVal;
+ private int m_highVal;
+ private boolean m_softUpperBound;
+
+ // Private constructor
+ public TagLengthRule(CONSTRAINT rule, int lowVal, int highVal, boolean softUpperBound) {
+ m_rule = rule;
+ m_lowVal = lowVal;
+ m_highVal = highVal;
+ m_softUpperBound = softUpperBound;
+ }
+
+ // Additional constructor
+ public TagLengthRule(CONSTRAINT rule, int lowVal, int highVal) {
+ m_rule = rule;
+ m_lowVal = lowVal;
+ m_highVal = highVal;
+ m_softUpperBound = false;
+ }
+ /**
+ * Eunumeration used to compute lengths of TLV values
+ *
+ */
+ public static enum CONSTRAINT {
+ FIXED, OR, VARIABLE
+ };
+
+ /*
+ * Provides the RULE (for lack of a better word at the time) of the rule
+ *
+ * @return the RULE
+ */
+
+ public CONSTRAINT getRule() {
+ return m_rule;
+ }
+
+ /*
+ * Provides the low value in the rule
+ *
+ * @return the low value in the rule
+ */
+ public int getLowVal() {
+ return m_lowVal;
+ }
+
+ /*
+ * Provides the high value in the rule
+ *
+ * @return the high value in the rule
+ */
+
+ public int getHighVal() {
+ return m_highVal;
+ }
+
+ /*
+ * Indicates if a soft boundary exists (due to an embedded CMS or signing cert)
+ *
+ * @return whether this value is eligible to have a soft upper bound
+ */
+
+ public boolean hasSoftUpperBound() {
+ return m_softUpperBound;
+ }
+}
+
\ No newline at end of file
diff --git a/tools/85b-swing-gui/src/main/java/gov/gsa/pivconformancegui/AuthTest.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/AuthTest.java
similarity index 93%
rename from tools/85b-swing-gui/src/main/java/gov/gsa/pivconformancegui/AuthTest.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/AuthTest.java
index b07b2e4f..19891aca 100644
--- a/tools/85b-swing-gui/src/main/java/gov/gsa/pivconformancegui/AuthTest.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/AuthTest.java
@@ -1,4 +1,4 @@
-package gov.gsa.pivconformancegui;
+package gov.gsa.pivconformance.cardlib.tools;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
@@ -11,8 +11,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import gov.gsa.pivconformance.utils.PCSCUtils;
-import gov.gsa.pivconformance.utils.PCSCWrapper;
+import gov.gsa.pivconformance.cardlib.utils.PCSCUtils;
+import gov.gsa.pivconformance.cardlib.utils.PCSCWrapper;
public class AuthTest {
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/ConformanceTestRunner.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/ConformanceTestRunner.java
new file mode 100644
index 00000000..56f5ea50
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/ConformanceTestRunner.java
@@ -0,0 +1,120 @@
+package gov.gsa.pivconformance.cardlib.tools;
+
+import gov.gsa.pivconformance.cardlib.utils.PCSCUtils;
+import gov.gsa.pivconformance.cardlib.utils.VersionUtils;
+import org.apache.commons.cli.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ConformanceTestRunner {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(ConformanceTestRunner.class);
+ private static final Options s_options = new Options();
+ static {
+ s_options.addOption("h", "help", false, "Print this help and exit");
+ s_options.addOption( null, "listReaders", false, "Print a list of connected readers and exit");
+ // Specify a specific reader, as given by --listReaders. If none is specified the first reader will be used
+ s_options.addOption("r", "reader", true, "Use the specified reader for conformance tests");
+ s_options.addOption("t", "testConfig", true, "Use the specified test configuration file");
+ s_options.addOption(null, "runSelected", true, "Run only the specified test cases (comma separated)");
+ s_options.addOption(null, "runTagged", true, "Run all test cases with the specified tags (comma separated)");
+ s_options.addOption(null, "dryRun", true, "Collect and configure test cases, and log which ones would be run, but do not execute");
+ s_options.addOption("l", "logConfig", true, "Use the specified log configuration file");
+ s_options.addOption("o", "outDir", true, "Specify the output directory for reports");
+ s_options.addOption("n", false, "Number of times to repeat the run");
+ }
+
+ private static void PrintHelpAndExit(int exitCode) {
+ new HelpFormatter().printHelp("ConformanceTestRunner ", s_options);
+ System.exit(exitCode);
+ }
+
+ private static List CheckIncompatibleOptions(String option, String incompatibleOptions, CommandLine cmd) {
+ List incompatibleOptionList = Arrays.asList(incompatibleOptions.split("\\s*,\\s*"));
+ // if the split didn't find anything, just treat the whole string as an option to test
+ if(incompatibleOptionList.isEmpty()) {
+ incompatibleOptionList.add(incompatibleOptions);
+ }
+ ArrayList messages = new ArrayList<>();
+ for(String opt: incompatibleOptionList) {
+ if(cmd.hasOption(opt)) {
+ messages.add(option + " cannot be combined with " + opt + ".");
+ }
+ }
+ return messages;
+ }
+
+ private static List CheckRequiredOptions(String option, String requiredOptions, CommandLine cmd) {
+ List requiredOptionList = Arrays.asList(requiredOptions.split("\\s*,\\s*"));
+ // if the split didn't find anything, just treat the whole string as an option to test
+ if(requiredOptionList.isEmpty()) {
+ requiredOptionList.add(requiredOptions);
+ }
+ ArrayList messages = new ArrayList<>();
+ for(String opt: requiredOptionList) {
+ if(!cmd.hasOption(opt)) {
+ messages.add(option + " requires that " + opt + " also be specified.");
+ }
+ }
+ return messages;
+ }
+
+ private static void LogErrorsIfNonEmptyAndExit(String msg, List messages, int exitCode) {
+ if(!messages.isEmpty()) {
+ if(msg != null && !msg.isEmpty()) s_logger.error(msg);
+ for(String message: messages) {
+ s_logger.error(message);
+ }
+ System.exit(exitCode);
+ }
+ }
+
+ public static void main(String[] args) {
+ s_logger.info("main class: {}", MethodHandles.lookup().lookupClass().getSimpleName());
+ s_logger.info("package version: {}", VersionUtils.GetPackageVersionString());
+ s_logger.info("build time: {}", VersionUtils.GetPackageBuildTime());
+
+ CommandLineParser p = new DefaultParser();
+ CommandLine cmd = null;
+ try {
+ cmd = p.parse(s_options, args);
+ } catch (ParseException e) {
+ s_logger.error("Failed to parse command line arguments", e);
+ PrintHelpAndExit(1);
+ }
+
+ if(cmd.hasOption("help")) {
+ PrintHelpAndExit(0);
+ }
+
+ if(cmd.hasOption("listReaders")) {
+ List messages = CheckIncompatibleOptions("listReaders", "reader,testConfig,runSingle,runTagged,outDir,n", cmd);
+ LogErrorsIfNonEmptyAndExit("Incompatible command line options found", messages, 1);
+ List readers = PCSCUtils.GetConnectedReaders();
+ if(!readers.isEmpty()) {
+ s_logger.info("Currently connected readers:");
+ int currReader = 0;
+ for (String reader : readers) {
+ currReader++;
+ s_logger.info("{}: {}", currReader, reader);
+ }
+ } else {
+ s_logger.info("No readers are connected.");
+ }
+ System.exit(0);
+ }
+
+ if(cmd.hasOption("testConfig")) {
+ List messages = CheckIncompatibleOptions("testConfig", "listReaders", cmd);
+ LogErrorsIfNonEmptyAndExit("Incompatible command line options found", messages, 1);
+ messages = CheckRequiredOptions("testConfig", "outDir", cmd);
+ LogErrorsIfNonEmptyAndExit("Required command line options missing", messages, 1);
+ }
+
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/PIVRunner.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/PIVRunner.java
new file mode 100644
index 00000000..4f47c68d
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/PIVRunner.java
@@ -0,0 +1,610 @@
+package gov.gsa.pivconformance.cardlib.tools;
+
+import gov.gsa.pivconformance.cardlib.card.client.*;
+import gov.gsa.pivconformance.cardlib.tlv.*;
+import gov.gsa.pivconformance.cardlib.utils.PCSCUtils;
+import gov.gsa.pivconformance.cardlib.utils.VersionUtils;
+import org.apache.commons.cli.*;
+import org.apache.commons.codec.binary.Hex;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.SignerId;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.text.SimpleDateFormat;
+
+import javax.smartcardio.*;
+import java.lang.invoke.MethodHandles;
+import java.security.cert.X509Certificate;
+import java.util.*;
+
+public class PIVRunner {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(PIVRunner.class);
+ private static final Options s_options = new Options();
+
+ static {
+ s_options.addOption("a", "all", false, "Scan all readers");
+ s_options.addOption("h", "help", false, "Print this help and exit");
+ }
+
+ private static void PrintHelpAndExit(int exitCode) {
+ new HelpFormatter().printHelp("PIVRunner ", s_options);
+ System.exit(exitCode);
+ }
+
+ public static boolean TestCard(CardHandle c) {
+ if(c.isValid()) {
+ CardTerminal t = c.getConnectionDescription().getTerminal();
+ try {
+ if(t.isCardPresent()) {
+ s_logger.info("Card found in reader {}", t.getName());
+ } else {
+ s_logger.error("No card was present in reader {}", t.getName());
+ return false;
+ }
+ } catch (CardException e) {
+ s_logger.error("Card communication error", e);
+ }
+ Card conn = c.getCard();
+ s_logger.info("Card connected.");
+ s_logger.info("Card protocol: {}", conn.getProtocol());
+ s_logger.info("Card ATR: {}", Hex.encodeHexString(conn.getATR().getBytes()));
+ ApplicationProperties cardAppProperties = new ApplicationProperties();
+ DefaultPIVApplication piv = new DefaultPIVApplication();
+ ApplicationAID aid = new ApplicationAID();
+ s_logger.info("Attempting to select default PIV application");
+ MiddlewareStatus result = piv.pivSelectCardApplication(c, aid, cardAppProperties);
+ s_logger.info("pivSelectCardApplication() returned {}", result);
+ if(result == MiddlewareStatus.PIV_OK) {
+ byte[] pcap = cardAppProperties.getBytes();
+
+ byte [] appID = cardAppProperties.getAppID();
+ String appLabel = cardAppProperties.getAppLabel();
+ String url = cardAppProperties.getURL();
+ List cryptoAlgs = cardAppProperties.getCryptoAlgs();
+ byte [] coexistentTagAllocationAuthority = cardAppProperties.getCoexistentTagAllocationAuthority();
+
+ if(appID != null)
+ s_logger.info("Application identifier of application: {}", Hex.encodeHexString(appID));
+
+ if(coexistentTagAllocationAuthority != null)
+ s_logger.info("Coexistent tag allocation authority: {}", Hex.encodeHexString(coexistentTagAllocationAuthority));
+
+ if(appLabel != "")
+ s_logger.info("Application label: {}", appLabel);
+
+ if(url != "")
+ s_logger.info("Uniform resource locator: {}", url);
+
+ if(cryptoAlgs != null) {
+ for(byte[] b : cryptoAlgs) {
+
+ s_logger.info("Cryptographic algorithms supported:");
+ s_logger.info("Algorithm ID: {} Algorithm Description: {}", Hex.encodeHexString(b), TagConstants.algMAP.get(b));
+ }
+ }
+
+
+ s_logger.info("PCAP: {}", Hex.encodeHexString(pcap));
+ BerTlvParser tp = new BerTlvParser(new CCTTlvLogger(PIVRunner.class));
+ BerTlv outer = tp.parseConstructed(pcap);
+ List values = outer.getValues();
+ for(BerTlv tlv : values) {
+ if(tlv.isPrimitive()) {
+ s_logger.info("PCAP Tag {}: {}", Hex.encodeHexString(tlv.getTag().bytes), Hex.encodeHexString(tlv.getBytesValue()));
+ } else {
+ s_logger.info("PCAP object: {}", Hex.encodeHexString(tlv.getTag().bytes));
+ }
+ }
+
+ result = MiddlewareStatus.PIV_AUTHENTICATION_FAILURE;
+
+ if(result != MiddlewareStatus.PIV_OK)
+ s_logger.error("Error authenticating to the smartcard: {}", result.toString());
+
+ X509Certificate signingCertificate = null;
+
+ HashMap soDataElements = new HashMap();
+ PIVDataObject securityObject = null;
+
+ for(String containerOID : APDUConstants.MandatoryContainers()) {
+ PIVDataObject dataObject = PIVDataObjectFactory.createDataObjectForOid(containerOID);
+ s_logger.info("Attempting to read data object for OID {} ({})", containerOID, APDUConstants.oidNameMap.get(containerOID));
+ result = piv.pivGetData(c, containerOID, dataObject);
+ if(result != MiddlewareStatus.PIV_OK) continue;
+ boolean decoded = dataObject.decode();
+ s_logger.info("{} {}", dataObject.getFriendlyName(), decoded ? "decoded successfully" : "failed to decode");
+ s_logger.info("pivGetData returned {}", result);
+ s_logger.info(dataObject.toString());
+
+ if(containerOID.equals(APDUConstants.CARD_CAPABILITY_CONTAINER_OID)) {
+
+ s_logger.info("Card Identifier: {}", Hex.encodeHexString(((CardCapabilityContainer) dataObject).getCardIdentifier()));
+ s_logger.info("Capability Container Version Number: {}", Hex.encodeHexString(((CardCapabilityContainer) dataObject).getCapabilityContainerVersionNumber()));
+ s_logger.info("Capability Grammar Version Number: {}", Hex.encodeHexString(((CardCapabilityContainer) dataObject).getCapabilityGrammarVersionNumber()));
+
+ List appCardURLList = ((CardCapabilityContainer) dataObject).getAppCardURL();
+
+ if (appCardURLList.size() > 0) {
+ s_logger.info("Applications CardURL List");
+ for (byte[] u : appCardURLList) {
+ s_logger.info("{}", Hex.encodeHexString(u));
+ }
+ }
+
+ s_logger.info("Registered Data Model number: {}", Hex.encodeHexString(((CardCapabilityContainer) dataObject).getRegisteredDataModelNumber()));
+ s_logger.info("Access Control Rule Table: {}", Hex.encodeHexString(((CardCapabilityContainer) dataObject).getAccessControlRuleTable()));
+
+
+ s_logger.info("Card APDUs Tag Present: {}", ((CardCapabilityContainer) dataObject).getCardAPDUs());
+ s_logger.info("RedirectionTag Tag Present: {}", ((CardCapabilityContainer) dataObject).getRedirectionTag());
+ s_logger.info("Capability Tuples Tag Present: {}", ((CardCapabilityContainer) dataObject).getCapabilityTuples());
+ s_logger.info("Status Tuples Tag Present: {}", ((CardCapabilityContainer) dataObject).getStatusTuples());
+ s_logger.info("Next CCC Tag Present: {}", ((CardCapabilityContainer) dataObject).getNextCCC());
+
+ if (((CardCapabilityContainer) dataObject).getExtendedApplicationCardURL() != null) {
+
+ List extendedAppCardURLList = ((CardCapabilityContainer) dataObject).getExtendedApplicationCardURL();
+
+ if (extendedAppCardURLList.size() > 0) {
+ s_logger.info("Extended Application CardURL List:");
+ for (byte[] u2 : extendedAppCardURLList) {
+ s_logger.info(" {}", Hex.encodeHexString(u2));
+ }
+ }
+ }
+
+ if (((CardCapabilityContainer) dataObject).getSecurityObjectBuffer() != null)
+ s_logger.info("Security Object Buffer: {}", Hex.encodeHexString(((CardCapabilityContainer) dataObject).getSecurityObjectBuffer()));
+
+
+ s_logger.info("Error Detection Code Tag Present: {}", ((CardCapabilityContainer) dataObject).getErrorDetectionCode());
+
+ soDataElements.put(APDUConstants.CARD_CAPABILITY_CONTAINER_OID, ((CardCapabilityContainer) dataObject).getSignedContent());
+ }
+
+ if (containerOID.equals(APDUConstants.CARD_HOLDER_UNIQUE_IDENTIFIER_OID)) {
+ if (((CardHolderUniqueIdentifier) dataObject).getBufferLength() != null) {
+ s_logger.info("Buffer Length: {}", Hex.encodeHexString(((CardHolderUniqueIdentifier) dataObject).getBufferLength()));
+ }
+ s_logger.info("FASC-N: {}", Hex.encodeHexString(((CardHolderUniqueIdentifier) dataObject).getfASCN()));
+ if (((CardHolderUniqueIdentifier) dataObject).getOrganizationalIdentifier() != null) {
+ s_logger.info("Organizational Identifier: {}", Hex.encodeHexString(((CardHolderUniqueIdentifier) dataObject).getOrganizationalIdentifier()));
+ }
+ if (((CardHolderUniqueIdentifier) dataObject).getdUNS() != null) {
+ s_logger.info("DUNS: {}", Hex.encodeHexString(((CardHolderUniqueIdentifier) dataObject).getdUNS()));
+ }
+ s_logger.info("GUID: {}", Hex.encodeHexString(((CardHolderUniqueIdentifier) dataObject).getgUID()));
+
+ SimpleDateFormat sdfmt = new SimpleDateFormat("MM/dd/yyyy");
+ s_logger.info("Expiration Date: {}", sdfmt.format(((CardHolderUniqueIdentifier) dataObject).getExpirationDate()));
+
+ s_logger.info("Cardholder UUID: {}", Hex.encodeHexString(((CardHolderUniqueIdentifier) dataObject).getCardholderUUID()));
+ s_logger.info("Issuer Asymmetric Signature Info:");
+
+ CMSSignedData sd = ((CardHolderUniqueIdentifier) dataObject).getAsymmetricSignature();
+ SignerInformationStore signers = sd.getSignerInfos();
+ Collection collection = signers.getSigners();
+ Iterator it = collection.iterator();
+
+ while (it.hasNext())
+ {
+ SignerInformation signer = it.next();
+ SignerId sid = signer.getSID();
+ String issuer = sid.getIssuer().toString();
+ String serial = Hex.encodeHexString(sid.getSerialNumber().toByteArray());
+ String skid = "";
+ if( sid.getSubjectKeyIdentifier() != null)
+ skid = Hex.encodeHexString(sid.getSubjectKeyIdentifier());
+
+ if(sid.getSubjectKeyIdentifier() != null)
+ s_logger.info("Signer skid: {} ", skid);
+ else
+ s_logger.info("Signer Issuer: {}, Serial Number: {} ", issuer, serial);
+
+ }
+ s_logger.info("Signature valid: {}", ((CardHolderUniqueIdentifier) dataObject).verifySignature());
+ signingCertificate = ((CardHolderUniqueIdentifier) dataObject).getSignerCert();
+
+ s_logger.info("Error Detection Code Tag Present: {}", ((CardHolderUniqueIdentifier) dataObject).getErrorDetectionCode());
+
+ soDataElements.put(APDUConstants.CARD_HOLDER_UNIQUE_IDENTIFIER_OID, ((CardHolderUniqueIdentifier) dataObject).getChuidContainer());
+ }
+
+ if (containerOID.equals(APDUConstants.X509_CERTIFICATE_FOR_PIV_AUTHENTICATION_OID)) {
+ X509Certificate pibAuthCert = ((X509CertificateDataObject) dataObject).getCertificate();
+
+ s_logger.info("PIV Auth Cert SubjectName: {}", pibAuthCert.getSubjectDN().getName());
+ s_logger.info("PIV Auth Cert SerialNumber: {}", Hex.encodeHexString(pibAuthCert.getSerialNumber().toByteArray()));
+ s_logger.info("PIV Auth Cert IssuerName: {}", pibAuthCert.getSubjectDN().getName());
+ }
+
+ if (containerOID.equals(APDUConstants.CARDHOLDER_FINGERPRINTS_OID)) {
+
+ s_logger.info("Fingerprint I & II: {}", Hex.encodeHexString(((CardHolderBiometricData) dataObject).getBiometricData()));
+
+
+ s_logger.info("Biometric Creation Date: {}", ((CardHolderBiometricData) dataObject).getBiometricCreationDate());
+ s_logger.info("Validity Period From: {}", ((CardHolderBiometricData) dataObject).getValidityPeriodFrom());
+ s_logger.info("Validity Period To: {}",((CardHolderBiometricData) dataObject).getValidityPeriodTo());
+
+ CMSSignedData sd = ((SignedPIVDataObject) dataObject).getAsymmetricSignature();
+ SignerInformationStore signers = sd.getSignerInfos();
+ Collection collection = signers.getSigners();
+ Iterator it = collection.iterator();
+
+ while (it.hasNext())
+ {
+ SignerInformation signer = it.next();
+ SignerId sid = signer.getSID();
+ String issuer = sid.getIssuer().toString();
+ String serial = Hex.encodeHexString(sid.getSerialNumber().toByteArray());
+ String skid = "";
+ if( sid.getSubjectKeyIdentifier() != null)
+ skid = Hex.encodeHexString(sid.getSubjectKeyIdentifier());
+
+ if(sid.getSubjectKeyIdentifier() != null)
+ s_logger.info("Signer skid: {} ", skid);
+ else
+ s_logger.info("Signer Issuer: {}, Serial Number: {} ", issuer, serial);
+
+ }
+ if(signingCertificate != null)
+ s_logger.info("Is signatue valid: {}",((SignedPIVDataObject) dataObject).verifySignature());
+ else
+ s_logger.info("Missing signing certificate to verify signature.");
+
+
+ s_logger.info("Error Detection Code Tag Present: {}", ((CardHolderBiometricData) dataObject).getErrorDetectionCode());
+
+ soDataElements.put(APDUConstants.CARDHOLDER_FINGERPRINTS_OID, ((CardHolderBiometricData) dataObject).getCbeffContainer());
+ }
+
+ if (containerOID.equals(APDUConstants.SECURITY_OBJECT_OID)) {
+
+ s_logger.info("RAW Mapping of DG to ContainerID value: {}", Hex.encodeHexString(((SecurityObject) dataObject).getMapping()));
+
+ HashMap idMap = ((SecurityObject) dataObject).getContainerIDList();
+
+ s_logger.info("List of containers included in the Security Object:");
+ for (HashMap.Entry entry : idMap.entrySet()) {
+ s_logger.info("Container ID: {}, Container Name: {}, Container OID: {}",entry.getKey(), entry.getValue(), APDUConstants.oidNameMap.get(entry.getValue()));
+ }
+
+ CMSSignedData sd = ((SignedPIVDataObject) dataObject).getAsymmetricSignature();
+ SignerInformationStore signers = sd.getSignerInfos();
+ Collection collection = signers.getSigners();
+ Iterator it = collection.iterator();
+
+ while (it.hasNext())
+ {
+ SignerInformation signer = it.next();
+ SignerId sid = signer.getSID();
+ String issuer = sid.getIssuer().toString();
+ String serial = Hex.encodeHexString(sid.getSerialNumber().toByteArray());
+ String skid = "";
+ if( sid.getSubjectKeyIdentifier() != null)
+ skid = Hex.encodeHexString(sid.getSubjectKeyIdentifier());
+
+ if(sid.getSubjectKeyIdentifier() != null)
+ s_logger.info("Signer skid: {} ", skid);
+ else
+ s_logger.info("Signer Issuer: {}, Serial Number: {} ", issuer, serial);
+
+ }
+ //s_logger.info("Error Detection Code Tag Present: {}", ((SecurityObject) dataObject).getErrorDetectionCode());
+
+ s_logger.info("SecurityObject signatue valid: {}", ((SignedPIVDataObject) dataObject).verifySignature());
+
+ securityObject = dataObject;
+ }
+
+ if (containerOID.equals(APDUConstants.CARDHOLDER_FACIAL_IMAGE_OID)) {
+ s_logger.info("Image for Visual Verification: {}", Hex.encodeHexString(((CardHolderBiometricData) dataObject).getBiometricData()));
+
+ s_logger.info("Biometric Creation Date: {}", ((CardHolderBiometricData) dataObject).getBiometricCreationDate());
+ s_logger.info("Validity Period From: {}", ((CardHolderBiometricData) dataObject).getValidityPeriodFrom());
+ s_logger.info("Validity Period To: {}", ((CardHolderBiometricData) dataObject).getValidityPeriodTo());
+
+
+ CMSSignedData sd = ((SignedPIVDataObject) dataObject).getAsymmetricSignature();
+ SignerInformationStore signers = sd.getSignerInfos();
+ Collection collection = signers.getSigners();
+ Iterator it = collection.iterator();
+
+ while (it.hasNext())
+ {
+ SignerInformation signer = it.next();
+ SignerId sid = signer.getSID();
+ String issuer = sid.getIssuer().toString();
+ String serial = Hex.encodeHexString(sid.getSerialNumber().toByteArray());
+ String skid = "";
+ if( sid.getSubjectKeyIdentifier() != null)
+ skid = Hex.encodeHexString(sid.getSubjectKeyIdentifier());
+
+ if(sid.getSubjectKeyIdentifier() != null)
+ s_logger.info("Signer skid: {} ", skid);
+ else
+ s_logger.info("Signer Issuer: {}, Serial Number: {} ", issuer, serial);
+ }
+
+ if(signingCertificate != null)
+ s_logger.info("Is signatue valid: {}",((SignedPIVDataObject) dataObject).verifySignature());
+ else
+ s_logger.info("Missing signing certificate to verify signature.");
+
+ s_logger.info("Error Detection Code Tag Present: {}", ((CardHolderBiometricData) dataObject).getErrorDetectionCode());
+
+ soDataElements.put(APDUConstants.CARDHOLDER_FACIAL_IMAGE_OID, ((CardHolderBiometricData) dataObject).getCbeffContainer());
+ }
+
+ if(containerOID.equals(APDUConstants.X509_CERTIFICATE_FOR_KEY_MANAGEMENT_OID)){
+ X509Certificate pibAuthCert = ((X509CertificateDataObject) dataObject).getCertificate();
+
+ s_logger.info("Key Managment Cert SubjectName: {}", pibAuthCert.getSubjectDN().getName());
+ s_logger.info("Key Managment Cert SerialNumber: {}", Hex.encodeHexString(pibAuthCert.getSerialNumber().toByteArray()));
+ s_logger.info("Key Managment Cert IssuerName: {}", pibAuthCert.getSubjectDN().getName());
+ }
+
+ if(containerOID.equals(APDUConstants.X509_CERTIFICATE_FOR_DIGITAL_SIGNATURE_OID)){
+ X509Certificate pibAuthCert = ((X509CertificateDataObject) dataObject).getCertificate();
+
+ s_logger.info("Digital Signature Cert SubjectName: {}", pibAuthCert.getSubjectDN().getName());
+ s_logger.info("Digital Signature SerialNumber: {}", Hex.encodeHexString(pibAuthCert.getSerialNumber().toByteArray()));
+ s_logger.info("Digital Signature IssuerName: {}", pibAuthCert.getSubjectDN().getName());
+ }
+
+ if(containerOID.equals(APDUConstants.X509_CERTIFICATE_FOR_CARD_AUTHENTICATION_OID)){
+ X509Certificate pibAuthCert = ((X509CertificateDataObject) dataObject).getCertificate();
+
+ s_logger.info("Card Auth Cert SubjectName: {}", pibAuthCert.getSubjectDN().getName());
+ s_logger.info("Card Auth Cert SerialNumber: {}", Hex.encodeHexString(pibAuthCert.getSerialNumber().toByteArray()));
+ s_logger.info("Card Auth Cert IssuerName: {}", pibAuthCert.getSubjectDN().getName());
+ }
+
+ }
+
+ PIVDataObject printedInformation = PIVDataObjectFactory.createDataObjectForOid(APDUConstants.PRINTED_INFORMATION_OID);
+ result = piv.pivGetData(c, APDUConstants.PRINTED_INFORMATION_OID, printedInformation);
+
+ if(result == MiddlewareStatus.PIV_OK) {
+ s_logger.info("Attempted to read {} object: {}", APDUConstants.oidNameMap.get(APDUConstants.PRINTED_INFORMATION_OID), result);
+ boolean decoded = printedInformation.decode();
+ s_logger.info("{} {}", printedInformation.getFriendlyName(), decoded ? "decoded successfully" : "failed to decode");
+
+ if (decoded) {
+ s_logger.info("Name: {}", ((PrintedInformation) printedInformation).getName());
+ s_logger.info("Employee Affiliation: {}", ((PrintedInformation) printedInformation).getEmployeeAffiliation());
+ s_logger.info("Expiration date: {}", ((PrintedInformation) printedInformation).getExpirationDate());
+ s_logger.info("Agency Card Serial Number: {}", ((PrintedInformation) printedInformation).getAgencyCardSerialNumber());
+ s_logger.info("Issuer Identification: {}", ((PrintedInformation) printedInformation).getIssuerIdentification());
+ if (((PrintedInformation) printedInformation).getOrganizationAffiliation1() != "")
+ s_logger.info("Name: {}", ((PrintedInformation) printedInformation).getOrganizationAffiliation1());
+ if (((PrintedInformation) printedInformation).getOrganizationAffiliation2() != "")
+ s_logger.info("Name: {}", ((PrintedInformation) printedInformation).getOrganizationAffiliation2());
+ s_logger.info("Error Detection Code Tag Present: {}", ((PrintedInformation) printedInformation).getErrorDetectionCode());
+
+ }
+
+
+ soDataElements.put(APDUConstants.PRINTED_INFORMATION_OID, ((PrintedInformation) printedInformation).getSignedContent());
+ }
+
+ boolean decoded = false;
+ PIVDataObject discoveryObject = PIVDataObjectFactory.createDataObjectForOid(APDUConstants.DISCOVERY_OBJECT_OID);
+ result = piv.pivGetData(c, APDUConstants.DISCOVERY_OBJECT_OID, discoveryObject);
+
+ if(result == MiddlewareStatus.PIV_OK) {
+ s_logger.info("Attempted to read discovery object: {}", result);
+ decoded = discoveryObject.decode();
+ s_logger.info("{} {}", discoveryObject.getFriendlyName(), decoded ? "decoded successfully" : "failed to decode");
+ }
+
+ soDataElements.put(APDUConstants.DISCOVERY_OBJECT_OID, ((DiscoveryObject) discoveryObject).getSignedContent());
+
+ PIVDataObject cardholderIrisImages = PIVDataObjectFactory.createDataObjectForOid(APDUConstants.CARDHOLDER_IRIS_IMAGES_OID);
+ result = piv.pivGetData(c, APDUConstants.CARDHOLDER_IRIS_IMAGES_OID, cardholderIrisImages);
+
+ if(result == MiddlewareStatus.PIV_OK) {
+ s_logger.info("Attempted to read {} object: {}", APDUConstants.oidNameMap.get(APDUConstants.CARDHOLDER_IRIS_IMAGES_OID), result);
+ decoded = cardholderIrisImages.decode();
+ s_logger.info("{} {}", cardholderIrisImages.getFriendlyName(), decoded ? "decoded successfully" : "failed to decode");
+
+ if (decoded) {
+ if (((CardHolderBiometricData) cardholderIrisImages).getBiometricData() != null) {
+ s_logger.info("Images for Iris: {}", Hex.encodeHexString(((CardHolderBiometricData) cardholderIrisImages).getBiometricData()));
+
+ s_logger.info("Biometric Creation Date: {}", ((CardHolderBiometricData) cardholderIrisImages).getBiometricCreationDate());
+ s_logger.info("Validity Period From: {}", ((CardHolderBiometricData) cardholderIrisImages).getValidityPeriodFrom());
+ s_logger.info("Validity Period To: {}", ((CardHolderBiometricData) cardholderIrisImages).getValidityPeriodTo());
+
+
+ CMSSignedData sd = ((SignedPIVDataObject) cardholderIrisImages).getAsymmetricSignature();
+ SignerInformationStore signers = sd.getSignerInfos();
+ Collection collection = signers.getSigners();
+ Iterator it = collection.iterator();
+
+ while (it.hasNext())
+ {
+ SignerInformation signer = it.next();
+ SignerId sid = signer.getSID();
+ String issuer = sid.getIssuer().toString();
+ String serial = Hex.encodeHexString(sid.getSerialNumber().toByteArray());
+ String skid = "";
+ if( sid.getSubjectKeyIdentifier() != null)
+ skid = Hex.encodeHexString(sid.getSubjectKeyIdentifier());
+
+ if(sid.getSubjectKeyIdentifier() != null)
+ s_logger.info("Signer skid: {} ", skid);
+ else
+ s_logger.info("Signer Issuer: {}, Serial Number: {} ", issuer, serial);
+
+ }
+
+ if(signingCertificate != null)
+ s_logger.info("Is signatue valid: {}",((SignedPIVDataObject) cardholderIrisImages).verifySignature());
+ else
+ s_logger.info("Missing signing certificate to verify signature.");
+ }
+ s_logger.info("Error Detection Code Tag Present: {}", ((CardHolderBiometricData) cardholderIrisImages).getErrorDetectionCode());
+
+ }
+ }
+
+ PIVDataObject keyHistoryObject = PIVDataObjectFactory.createDataObjectForOid(APDUConstants.KEY_HISTORY_OBJECT_OID);
+ result = piv.pivGetData(c, APDUConstants.KEY_HISTORY_OBJECT_OID, keyHistoryObject);
+
+ if(result == MiddlewareStatus.PIV_OK) {
+ s_logger.info("Attempted to read key history object: {}", result);
+ decoded = keyHistoryObject.decode();
+ if (decoded) {
+ s_logger.info("Decoded successfully {}", keyHistoryObject.toString());
+ }
+ }
+
+ PIVDataObject biometricInformationTemplatesGroupTemplate = PIVDataObjectFactory.createDataObjectForOid(APDUConstants.BIOMETRIC_INFORMATION_TEMPLATES_GROUP_TEMPLATE_OID);
+ result = piv.pivGetData(c, APDUConstants.BIOMETRIC_INFORMATION_TEMPLATES_GROUP_TEMPLATE_OID, biometricInformationTemplatesGroupTemplate);
+
+ if(result == MiddlewareStatus.PIV_OK) {
+ s_logger.info("Attempted to read {} object: {}", APDUConstants.oidNameMap.get(APDUConstants.BIOMETRIC_INFORMATION_TEMPLATES_GROUP_TEMPLATE_OID), result);
+ decoded = biometricInformationTemplatesGroupTemplate.decode();
+ s_logger.info("{} {}", biometricInformationTemplatesGroupTemplate.getFriendlyName(), decoded ? "decoded successfully" : "failed to decode");
+
+ if (decoded) {
+
+ s_logger.info("Number of fingers: {}", ((BiometricInformationTemplatesGroupTemplate) biometricInformationTemplatesGroupTemplate).getNumberOfFingers());
+ if (((BiometricInformationTemplatesGroupTemplate) biometricInformationTemplatesGroupTemplate).getbITForFirstFinger() != null)
+ s_logger.info("BIT for first Finger: {}", Hex.encodeHexString(((BiometricInformationTemplatesGroupTemplate) biometricInformationTemplatesGroupTemplate).getbITForFirstFinger()));
+ if (((BiometricInformationTemplatesGroupTemplate) biometricInformationTemplatesGroupTemplate).getbITForSecondFinger() != null)
+ s_logger.info("BIT for second Finger: {}", Hex.encodeHexString(((BiometricInformationTemplatesGroupTemplate) biometricInformationTemplatesGroupTemplate).getbITForSecondFinger()));
+
+ }
+ }
+
+ PIVDataObject secureMessagingCertificateSigner = PIVDataObjectFactory.createDataObjectForOid(APDUConstants.SECURE_MESSAGING_CERTIFICATE_SIGNER_OID);
+ result = piv.pivGetData(c, APDUConstants.SECURE_MESSAGING_CERTIFICATE_SIGNER_OID, secureMessagingCertificateSigner);
+
+ if(result == MiddlewareStatus.PIV_OK) {
+ s_logger.info("Attempted to read {} object: {}", APDUConstants.oidNameMap.get(APDUConstants.SECURE_MESSAGING_CERTIFICATE_SIGNER_OID), result);
+ decoded = secureMessagingCertificateSigner.decode();
+ s_logger.info("{} {}", secureMessagingCertificateSigner.getFriendlyName(), decoded ? "decoded successfully" : "failed to decode");
+
+ if (decoded) {
+
+ X509Certificate contentSigningCert = ((SecureMessagingCertificateSigner) secureMessagingCertificateSigner).getCertificate();
+
+ s_logger.info("Content Signing Cert SubjectName: {}", contentSigningCert.getSubjectDN().getName());
+ s_logger.info("Content Signing Cert SerialNumber: {}", Hex.encodeHexString(contentSigningCert.getSerialNumber().toByteArray()));
+ s_logger.info("Content Signing Cert IssuerName: {}", contentSigningCert.getSubjectDN().getName());
+
+ if (((SecureMessagingCertificateSigner) secureMessagingCertificateSigner).getIntermediateCVC() != null)
+ s_logger.info("Intermediate CVC: {}", Hex.encodeHexString(((SecureMessagingCertificateSigner) secureMessagingCertificateSigner).getIntermediateCVC()));
+
+ }
+ }
+
+ PIVDataObject pairingCodeReferenceDataContainer = PIVDataObjectFactory.createDataObjectForOid(APDUConstants.PAIRING_CODE_REFERENCE_DATA_CONTAINER_OID);
+ result = piv.pivGetData(c, APDUConstants.PAIRING_CODE_REFERENCE_DATA_CONTAINER_OID, pairingCodeReferenceDataContainer);
+
+ if(result == MiddlewareStatus.PIV_OK) {
+ s_logger.info("Attempted to read {} object: {}", APDUConstants.oidNameMap.get(APDUConstants.PAIRING_CODE_REFERENCE_DATA_CONTAINER_OID), result);
+ decoded = pairingCodeReferenceDataContainer.decode();
+ s_logger.info("{} {}", pairingCodeReferenceDataContainer.getFriendlyName(), decoded ? "decoded successfully" : "failed to decode");
+
+ if (decoded) {
+ s_logger.info("Name: {}", ((PairingCodeReferenceDataContainer) pairingCodeReferenceDataContainer).getName());
+ s_logger.info("Error Detection Code Tag Present: {}", ((PairingCodeReferenceDataContainer) pairingCodeReferenceDataContainer).getErrorDetectionCode());
+
+ }
+ }
+ ((SecurityObject) securityObject).setMapOfDataElements(soDataElements);
+
+ boolean hashesVerified = ((SecurityObject) securityObject).verifyHashes();
+ s_logger.info("Security Object hashes verified: {}", hashesVerified);
+
+ boolean hashVerified = ((SecurityObject) securityObject).verifyHash(5);
+ s_logger.info("Printed Information hash verified: {}", hashVerified);
+
+ }
+ //ResponseAPDU rsp = null;
+ return true;
+ } else {
+ s_logger.error("TestCard called with invalid card handle");
+ }
+ return false;
+ }
+
+ public static void main(String[] args) {
+ s_logger.info("main class: {}", MethodHandles.lookup().lookupClass().getSimpleName());
+ s_logger.info("package version: {}", VersionUtils.GetPackageVersionString());
+ s_logger.info("build time: {}", VersionUtils.GetPackageBuildTime());
+
+ CommandLineParser p = new DefaultParser();
+ CommandLine cmd = null;
+ try {
+ cmd = p.parse(s_options, args);
+ } catch (ParseException e) {
+ s_logger.error("Failed to parse command line arguments", e);
+ PrintHelpAndExit(1);
+ }
+
+ if(cmd.hasOption("help")) {
+ PrintHelpAndExit(0);
+ }
+
+ PCSCUtils.ConfigureUserProperties();
+ PIVMiddlewareVersion mwv = new PIVMiddlewareVersion();
+ MiddlewareStatus middlewareStatus = PIVMiddleware.pivMiddlewareVersion(mwv);
+ s_logger.info("pivMiddlewareVersion returned status {} and version {}", middlewareStatus, mwv);
+
+ TerminalFactory tf = TerminalFactory.getDefault();
+ List terminals = null;
+ try {
+ terminals = tf.terminals().list();
+ } catch (CardException e) {
+ s_logger.error("Failed to list card terminals", e);
+ System.exit(1);
+ }
+ if(terminals.size() == 0) {
+ s_logger.error("No readers were found.");
+ System.exit(1);
+ }
+ int terminalCount = 0;
+ for(CardTerminal t : terminals) {
+ terminalCount++;
+ ConnectionDescription cd = ConnectionDescription.createFromTerminal(t);
+ byte[] descriptor = cd.getBytes();
+ if(descriptor != null) {
+ s_logger.info("Descriptor for terminal {}: {}", terminalCount, Hex.encodeHexString(descriptor, false));
+ }
+ // if there is only one reader or if we've been asked to only test one reader,
+ // wait for a card
+ try {
+ if(!t.isCardPresent() && (!cmd.hasOption("all") || terminals.size() == 1)) {
+ s_logger.info("Insert a card into {}", t.getName());
+ t.waitForCardPresent(0);
+ }
+ } catch (CardException e) {
+ s_logger.error("Error checking for card presence", e);
+ }
+ s_logger.info("Testing with terminal {}: {}", terminalCount, t.getName());
+ CardHandle ch = new CardHandle();
+ MiddlewareStatus result = PIVMiddleware.pivConnect(true, cd, ch);
+ s_logger.info("[{}] PIVMiddleware.pivConnect() returned {} for reader {}", terminalCount, result, t.getName());
+ boolean testResult = TestCard(ch);
+ if(testResult) {
+ s_logger.info("Card test completed successfully.");
+ } else {
+ s_logger.error("Card test failed.");
+ }
+ if(!cmd.hasOption("all")) {
+ break;
+ }
+ }
+
+ System.exit(0);
+
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/PrintEnvironmentInfo.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/PrintEnvironmentInfo.java
new file mode 100644
index 00000000..b48a1c02
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/PrintEnvironmentInfo.java
@@ -0,0 +1,44 @@
+package gov.gsa.pivconformance.cardlib.tools;
+
+import gov.gsa.pivconformance.cardlib.utils.PCSCUtils;
+import gov.gsa.pivconformance.cardlib.utils.VersionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.smartcardio.CardException;
+import javax.smartcardio.CardTerminal;
+import javax.smartcardio.TerminalFactory;
+import java.lang.invoke.MethodHandles;
+import java.security.Provider;
+import java.security.Security;
+
+public class PrintEnvironmentInfo {
+
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(PrintEnvironmentInfo.class);
+
+ /**
+ * A simple test program that dumps info about the environment we're running in.
+ */
+ public static void main(String[] args) {
+ s_logger.info("main class: {}", MethodHandles.lookup().lookupClass().getSimpleName());
+ s_logger.info("package version: {}", VersionUtils.GetPackageVersionString());
+ s_logger.info("build time: {}", VersionUtils.GetPackageBuildTime());
+ PCSCUtils.ConfigureUserProperties();
+ s_logger.info("System properties");
+ System.getProperties().forEach((key, value) -> s_logger.info("property: '{}' = '{}'", key, value));
+ for (Provider prov : Security.getProviders()) {
+ s_logger.info("Security Provider: {} version {}", prov.getName(), prov.getVersion());
+ }
+ TerminalFactory tf = TerminalFactory.getDefault();
+ s_logger.info("Attempting to list card terminals");
+ try {
+ for (CardTerminal t : tf.terminals().list()) {
+ s_logger.info("Reader: {}: {}", t.getName(), t.isCardPresent() ? "Card present":"Card not present");
+ }
+ } catch (CardException e) {
+ s_logger.error("Unable to enumerate card terminals", e);
+ }
+ }
+}
+
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/SQLiteDBGenerator.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/SQLiteDBGenerator.java
new file mode 100644
index 00000000..5f885479
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/tools/SQLiteDBGenerator.java
@@ -0,0 +1,82 @@
+package gov.gsa.pivconformance.cardlib.tools;
+
+import gov.gsa.pivconformance.cardlib.utils.VersionUtils;
+import org.apache.commons.cli.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+
+public class SQLiteDBGenerator {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(SQLiteDBGenerator.class);
+ private static final Options s_options = new Options();
+ static {
+ s_options.addOption("h", "help", false, "Print this help and exit");
+ s_options.addOption("d", "database", true, "path to database file");
+ }
+
+ private static void PrintHelpAndExit(int exitCode) {
+ new HelpFormatter().printHelp("ConformanceTestRunner ", s_options);
+ System.exit(exitCode);
+ }
+
+ public static void main(String[] args) {
+ s_logger.info("main class: {}", MethodHandles.lookup().lookupClass().getSimpleName());
+ s_logger.info("package version: {}", VersionUtils.GetPackageVersionString());
+ s_logger.info("build time: {}", VersionUtils.GetPackageBuildTime());
+
+ CommandLineParser p = new DefaultParser();
+ CommandLine cmd = null;
+ try {
+ cmd = p.parse(s_options, args);
+ } catch (ParseException e) {
+ s_logger.error("Failed to parse command line arguments", e);
+ PrintHelpAndExit(1);
+ }
+
+ if(cmd.hasOption("help")) {
+ PrintHelpAndExit(0);
+ }
+
+ if(cmd.hasOption("database")) {
+ String dbParam = cmd.getOptionValue("database");
+ File f = new File(dbParam);
+ if(f.exists()) {
+ s_logger.error("Cowardly refusing to overwrite existing file {}", dbParam);
+ System.exit(1);
+ }
+ String dbUrl = null;
+ try {
+ dbUrl = "jdbc:sqlite:" + f.getCanonicalPath();
+ } catch (IOException e) {
+ s_logger.error("Unable to calculate canonical name for database file", e);
+ System.exit(1);
+ }
+ Connection conn = null;
+ try {
+ conn = DriverManager.getConnection(dbUrl);
+ } catch (SQLException e) {
+ s_logger.error("Unable to establish JDBC connection for SQLite database", e);
+ }
+ if(conn != null) {
+ s_logger.debug("Created sql connection for {}", dbParam);
+ DatabaseMetaData metaData = null;
+ try {
+ metaData = conn.getMetaData();
+ s_logger.debug("Driver: {} version {}", metaData.getDriverName(), metaData.getDriverVersion());
+ } catch (SQLException e) {
+ s_logger.error("Unable to read driver metadata", e);
+ }
+ }
+ }
+
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/utils/ITransmitCounter.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/ITransmitCounter.java
similarity index 62%
rename from cardlib/src/main/java/gov/gsa/pivconformance/utils/ITransmitCounter.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/ITransmitCounter.java
index 4d27a47d..f8426628 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/utils/ITransmitCounter.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/ITransmitCounter.java
@@ -1,4 +1,4 @@
-package gov.gsa.pivconformance.utils;
+package gov.gsa.pivconformance.cardlib.utils;
public interface ITransmitCounter {
public void incrementTransmitCount();
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/utils/NullParameters.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/NullParameters.java
similarity index 63%
rename from cardlib/src/main/java/gov/gsa/pivconformance/utils/NullParameters.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/NullParameters.java
index ac216e57..d56626c8 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/utils/NullParameters.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/NullParameters.java
@@ -1,14 +1,15 @@
-package gov.gsa.pivconformance.utils;
+package gov.gsa.pivconformance.cardlib.utils;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Null;
import org.bouncycastle.asn1.ASN1Primitive;
-public class NullParameters implements ASN1Encodable {
+
+public class NullParameters implements ASN1Encodable{
@Override
public ASN1Primitive toASN1Primitive() {
- byte[] NULL = { 0x05, 0x00 };
+ byte[] NULL = {0x05, 0x00};
return ASN1Null.getInstance(NULL);
}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/utils/OSUtils.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/OSUtils.java
similarity index 55%
rename from cardlib/src/main/java/gov/gsa/pivconformance/utils/OSUtils.java
rename to cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/OSUtils.java
index dd84023c..db8ebd5b 100644
--- a/cardlib/src/main/java/gov/gsa/pivconformance/utils/OSUtils.java
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/OSUtils.java
@@ -1,58 +1,58 @@
-package gov.gsa.pivconformance.utils;
-
-public class OSUtils {
-
- public enum OSTYPE {
- WINDOWS(10), OSX(20), LINUX(30);
-
- private OSTYPE(int ostypeValue) {
- }
- }
-
- public OSUtils() {
-
- }
-
- /**
- * Get one of the three supported operating system types
- *
- * @return manufactured OSTYPE from environment
- *
- */
- public static OSTYPE getOSType() {
- OSTYPE rv = OSTYPE.LINUX;
- String osName = System.getProperty("os.name");
- if (osName.toLowerCase().contains("windows")) {
- rv = OSTYPE.WINDOWS;
- } else if (osName.toLowerCase().startsWith("mac")) {
- rv = OSTYPE.OSX;
- } else if (osName.toLowerCase().contains("linux")) {
- rv = OSTYPE.LINUX;
- }
- return rv;
- }
-
- /**
- * Get the location of the temp directory
- *
- * @return the location of the temp directory
- */
-
- public static String getTempDir() {
- OSTYPE os = OSUtils.getOSType();
- String rv;
-
- switch (os) {
- case WINDOWS:
- rv = System.getenv("TEMP");
- break;
- case OSX:
- case LINUX:
- rv = "/tmp";
- default:
- rv = System.getenv("TEMP");
- }
-
- return rv;
- }
-}
+package gov.gsa.pivconformance.cardlib.utils;
+
+public class OSUtils {
+
+ public enum OSTYPE {
+ WINDOWS(10),
+ OSX(20),
+ LINUX(30);
+
+ private OSTYPE (int ostypeValue) {
+ }
+ }
+
+ public OSUtils () {
+
+ }
+
+ /**
+ * Get one of the three supported operating system types
+ * @return manufactured OSTYPE from environment
+ *
+ */
+ public static OSTYPE getOSType() {
+ OSTYPE rv = OSTYPE.LINUX;
+ String osName = System.getProperty("os.name");
+ if (osName.toLowerCase().contains("windows")) {
+ rv = OSTYPE.WINDOWS;
+ } else if (osName.toLowerCase().startsWith("mac")) {
+ rv = OSTYPE.OSX;
+ } else if (osName.toLowerCase().contains("linux")) {
+ rv = OSTYPE.LINUX;
+ }
+ return rv;
+ }
+
+ /**
+ * Get the location of the temp directory
+ * @return the location of the temp directory
+ */
+
+ public static String getTempDir() {
+ OSTYPE os = OSUtils.getOSType();
+ String rv;
+
+ switch (os) {
+ case WINDOWS:
+ rv = System.getenv("TEMP");
+ break;
+ case OSX:
+ case LINUX:
+ rv = "/tmp";
+ default:
+ rv = System.getenv("TEMP");
+ }
+
+ return rv;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/PCSCUtils.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/PCSCUtils.java
new file mode 100644
index 00000000..61ae6598
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/PCSCUtils.java
@@ -0,0 +1,135 @@
+package gov.gsa.pivconformance.cardlib.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.smartcardio.CardException;
+import javax.smartcardio.CardTerminal;
+import javax.smartcardio.TerminalFactory;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+public class PCSCUtils {
+ // slf4j will thunk this through to an appropriately configured logging library
+ private static final Logger s_logger = LoggerFactory.getLogger(PCSCUtils.class);
+ public static void ConfigureUserProperties() {
+ String homeDirectoryEnv = System.getenv("HOME");
+ if(homeDirectoryEnv == null) {
+ return;
+ }
+ File homeDirectory = new File(homeDirectoryEnv);
+ if(!homeDirectory.exists()) return;
+ File configFile = new File(homeDirectory, ".pivconformance-pcsc.properties");
+ if(configFile.exists()) {
+ ConfigureUserProperties(configFile);
+ }
+ }
+ public static void ConfigureUserProperties(File fileName) {
+ Properties props = new Properties();
+ try {
+ props.load(new FileInputStream(fileName));
+ props.forEach((key, value) -> {
+ s_logger.info("Adding property: '{}' = '{}'", key, value);
+ System.setProperty((String)key, (String) value);
+ });
+ } catch (IOException e) {
+ s_logger.error("Unable to read " + fileName.getAbsolutePath(), e);
+ return;
+ }
+ }
+ public static List GetConnectedReaders() {
+ ArrayList readerList = new ArrayList<>();
+ TerminalFactory tf = TerminalFactory.getDefault();
+ List terminals = null;
+ try {
+ s_logger.debug("About to list connected readers");
+ terminals = tf.terminals().list();
+ s_logger.debug("Done listing connected readers");
+ } catch (CardException e) {
+ s_logger.error("Failed to list card terminals", e);
+ return readerList;
+ }
+ if(terminals.size() == 0) {
+ s_logger.debug("No readers were connected.");
+ return readerList;
+ }
+ int terminalCount = 0;
+ for(CardTerminal t : terminals) {
+ terminalCount++;
+ readerList.add(t.getName());
+ }
+ s_logger.debug("Found {} readers.", terminalCount);
+ return readerList;
+ }
+
+ public static String GetFirstReaderWithCardPresent() {
+ new ArrayList<>();
+ TerminalFactory tf = TerminalFactory.getDefault();
+ List terminals = null;
+ try {
+ s_logger.debug("About to list connected readers");
+ terminals = tf.terminals().list();
+ s_logger.debug("Done listing connected readers");
+ } catch (CardException e) {
+ s_logger.error("Failed to list card terminals", e);
+ return null;
+ }
+ if(terminals.size() == 0) {
+ s_logger.debug("No readers were connected.");
+ return null;
+ }
+ for(CardTerminal t : terminals) {
+ try {
+ if(t.isCardPresent()) {
+ return t.getName();
+ }
+ } catch (CardException e) {
+ s_logger.debug("isCardPresent() threw an exception for reader {}", t.getName(), e);
+ }
+ }
+ s_logger.debug("No reader found with card inserted");
+ return null;
+ }
+
+ public static CardTerminal TerminalForReaderName(String name) {
+ TerminalFactory tf = TerminalFactory.getDefault();
+ List terminals = null;
+ try {
+ terminals = tf.terminals().list();
+ } catch (CardException e) {
+ s_logger.error("TerminalForReaderName(): Unable to enumerate terminals");
+ return null;
+ }
+ if(terminals.size() == 0) {
+ s_logger.error("Unable to find any readers.");
+ return null;
+ }
+ for(CardTerminal t : terminals) {
+ if(t.getName().equals(name)) {
+ return t;
+ }
+ }
+ s_logger.error("No reader named " + name + " is attached to the system.");
+ return null;
+
+ }
+
+ public static int StatusWordsToRetries(byte[] sw)
+ {
+ if(sw == null || sw.length < 2) {
+ s_logger.error("a status word array must be at least 2 bytes.");
+ return -1;
+ }
+ byte sw1 = sw[sw.length -2];
+ byte sw2 = sw[sw.length -1];
+ if(sw1 != 0x63 || sw2 == 0x00) {
+ s_logger.error("bytes do not contain password retry count.");
+ return -1;
+ }
+ return 0x0F & sw2;
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/PCSCWrapper.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/PCSCWrapper.java
new file mode 100644
index 00000000..4cf58ded
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/PCSCWrapper.java
@@ -0,0 +1,99 @@
+package gov.gsa.pivconformance.cardlib.utils;
+
+import javax.smartcardio.Card;
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CardTerminal;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import gov.gsa.pivconformance.cardlib.card.client.CardClientException;
+import gov.gsa.pivconformance.cardlib.card.client.ChainingAPDUTransmitter;
+import gov.gsa.pivconformance.cardlib.card.client.RequestAPDUWrapper;
+import gov.gsa.pivconformance.cardlib.card.client.ResponseAPDUWrapper;
+
+public class PCSCWrapper implements ITransmitCounter{
+ private static final Logger s_logger = LoggerFactory.getLogger(PCSCWrapper.class);
+ private static PCSCWrapper INSTANCE = new PCSCWrapper();
+
+ private int m_connectCount = 0;
+ private int m_transmitCount = 0;
+
+ public Card connect(CardTerminal t) throws CardException {
+ s_logger.debug("Connecting to card in {} using the default protocol", t.getName());
+ return connect(t, "*");
+ }
+
+ public Card connect(CardTerminal t, String protocol) throws CardException {
+ s_logger.debug("Connecting to card in {} using protocol: \"{}\"", t.getName(), protocol);
+ m_connectCount++;
+ Card rv = null;
+ try {
+ rv = t.connect(protocol);
+ } catch(CardException e) {
+ s_logger.error("Caught CardException: {} while attempting to connect to card in {} using protocol \"{}\"",
+ e.getMessage(), t.getName(), protocol, e);
+ throw e;
+ }
+ s_logger.debug("Connected: {}", rv);
+ return rv;
+
+ }
+
+ public ResponseAPDU transmit(CardChannel channel, CommandAPDU cmd) throws CardException {
+ s_logger.debug("transmit() wrapper called");
+ m_transmitCount++;
+ /*
+ ResponseAPDU rsp = null;
+ s_apduLogger.info("Sending Command APDU: {}", Hex.encodeHexString(cmd.getBytes()).replaceAll("..(?=.)", "$0 "));
+ try {
+ rsp = channel.transmit(cmd);
+ s_apduLogger.debug("Received Response APDU: {}", Hex.encodeHexString(rsp.getBytes()).replaceAll("..(?=.)", "$0 "));
+ } catch (CardException e) {
+ s_logger.error("Caught CardException {} transmitting APDU.", e.getMessage(), e);
+ throw e;
+ }
+ return rsp;
+ */
+ ChainingAPDUTransmitter ct = new ChainingAPDUTransmitter(channel);
+ RequestAPDUWrapper req = new RequestAPDUWrapper(cmd.getBytes());
+ ResponseAPDUWrapper rsp = null;
+ try {
+ rsp = ct.transmit(req);
+ } catch (CardClientException e) {
+ s_logger.error("Failed to receive response APDU", e);
+ return null;
+ }
+ return new ResponseAPDU(rsp.getBytes());
+ }
+
+ private PCSCWrapper() {
+
+ }
+
+ public static PCSCWrapper getInstance() {
+ return INSTANCE;
+ }
+
+ public int getTransmitCount() {
+ return m_transmitCount;
+ }
+
+ public int getConnectCount() {
+ return m_connectCount;
+ }
+
+ public void resetCounters() {
+ m_connectCount = 0;
+ m_transmitCount = 0;
+ }
+
+ @Override
+ public void incrementTransmitCount() {
+ m_transmitCount++;
+ }
+
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/VersionUtils.java b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/VersionUtils.java
new file mode 100644
index 00000000..f1897ed1
--- /dev/null
+++ b/cardlib/src/main/java/gov/gsa/pivconformance/cardlib/utils/VersionUtils.java
@@ -0,0 +1,52 @@
+package gov.gsa.pivconformance.cardlib.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+public class VersionUtils {
+ private static final Logger s_logger = LoggerFactory.getLogger(VersionUtils.class);
+
+ static Properties s_properties;
+
+ public static final String PACKAGE_VERSION = "build.version";
+
+ public static final String PACKAGE_REVISION = "git.commit.id";
+ public static final String PACKAGE_REVISION_TIME = "git.commit.time";
+ public static final String PACKAGE_BUILD_TIME = "build.time";
+
+ static {
+ s_properties = new Properties();
+ InputStream pis = null;
+ try {
+ VersionUtils.class.getClassLoader();
+ pis = VersionUtils.class.getResourceAsStream("version.properties");
+ s_properties.load(pis);
+ } catch(Exception e) {
+ s_logger.debug("Unable to read version.properties file from classpath. This may only be available from jar packaged builds.", e);
+ s_properties.setProperty(PACKAGE_VERSION, "UNAVAILABLE");
+ s_properties.setProperty(PACKAGE_REVISION, "UNAVAILABLE");
+ s_properties.setProperty(PACKAGE_BUILD_TIME, "UNAVAILABLE");
+ s_properties.setProperty(PACKAGE_REVISION_TIME, "UNAVAILABLE");
+
+ }
+ if(!s_properties.containsKey(PACKAGE_VERSION)) {
+ s_logger.error("Version.properties was read from classpath but did not contain versioning information");
+ s_properties.setProperty(PACKAGE_VERSION, "ERROR");
+ s_properties.setProperty(PACKAGE_REVISION, "ERROR");
+ s_properties.setProperty(PACKAGE_BUILD_TIME, "ERROR");
+ s_properties.setProperty(PACKAGE_REVISION_TIME, "ERROR");
+ }
+
+ }
+
+ public static String GetPackageVersionString() {
+ return String.format("%s.%s", s_properties.getProperty(PACKAGE_VERSION), s_properties.getProperty(PACKAGE_REVISION));
+ }
+
+ public static String GetPackageBuildTime() {
+ return s_properties.getProperty(PACKAGE_BUILD_TIME);
+ }
+}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTag.java b/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTag.java
deleted file mode 100644
index 45b4eb28..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTag.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package gov.gsa.pivconformance.tlv;
-
-import java.util.Arrays;
-
-public class BerTag {
- public final byte[] bytes;
-
- /**
- * Creates a new tag from given byte array. Similar
- * {@link BerTag#BerTag(byte[], int, int)} but using the full array.
- *
- * @param aBuf to create the tag
- */
- public BerTag(byte[] aBuf) {
- this(aBuf, 0, aBuf.length);
- }
-
- public BerTag(byte[] aBuf, int aOffset, int aLength) {
- byte[] temp = new byte[aLength];
- System.arraycopy(aBuf, aOffset, temp, 0, aLength);
- bytes = temp;
- }
-
- public BerTag(int aFirstByte, int aSecondByte) {
- bytes = new byte[] { (byte) (aFirstByte), (byte) aSecondByte };
- }
-
- public BerTag(int aFirstByte, int aSecondByte, int aFirth) {
- bytes = new byte[] { (byte) (aFirstByte), (byte) aSecondByte, (byte) aFirth };
- }
-
- public BerTag(int aFirstByte) {
- bytes = new byte[] { (byte) aFirstByte };
- }
-
- public boolean isConstructed() {
- return (bytes[0] & 0x20) != 0;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- BerTag berTag = (BerTag) o;
-
- return Arrays.equals(bytes, berTag.bytes);
-
- }
-
- @Override
- public int hashCode() {
- return Arrays.hashCode(bytes);
- }
-
- @Override
- public String toString() {
- return (isConstructed() ? "+ " : "- ") + HexUtil.toHexString(bytes, 0, bytes.length);
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlv.java b/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlv.java
deleted file mode 100644
index 98a7d548..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlv.java
+++ /dev/null
@@ -1,191 +0,0 @@
-package gov.gsa.pivconformance.tlv;
-
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- *
- */
-public class BerTlv {
-
- private final static Charset ASCII = Charset.forName("US-ASCII");
-
- private final BerTag theTag;
- private final byte[] theValue;
- protected final List theList;
-
- /**
- * Creates constructed TLV
- *
- * @param aTag tag
- * @param aList set of nested TLVs
- */
- public BerTlv(BerTag aTag, List aList) {
- theTag = aTag;
- theList = aList;
- theValue = null;
- }
-
- public BerTlv(BerTag aTag, List aList, byte[] aValue) {
- theTag = aTag;
- theList = aList;
- theValue = aValue;
- }
-
- /**
- * Creates primitive TLV
- *
- * @param aTag tag
- * @param aValue value as byte[]
- */
- public BerTlv(BerTag aTag, byte[] aValue) {
- theTag = aTag;
- theValue = aValue;
- theList = null;
- }
-
- //
- //
- //
-
- public BerTag getTag() {
- return theTag;
- }
-
- public boolean isPrimitive() {
- return !theTag.isConstructed();
- }
-
- public boolean hasRawValue() {
- return theValue != null;
- }
-
- public boolean isConstructed() {
- return theTag.isConstructed();
- }
-
- public boolean isTag(BerTag aTag) {
- return theTag.equals(aTag);
- }
-
- //
- // find
- //
-
- public BerTlv find(BerTag aTag) {
- if (aTag.equals(getTag())) {
- return this;
- }
-
- if (isConstructed()) {
- if (theList == null)
- return null;
- for (BerTlv tlv : theList) {
- BerTlv ret = tlv.find(aTag);
- if (ret != null) {
- return ret;
- }
- }
- return null;
- }
- return null;
- }
-
- public List findAll(BerTag aTag) {
- List list = new ArrayList();
- if (aTag.equals(getTag())) {
- list.add(this);
- return list;
- } else if (isConstructed()) {
- for (BerTlv tlv : theList) {
- list.addAll(tlv.findAll(aTag));
- }
- }
- return list;
- }
-
- //
- // getters
- //
-
- public String getHexValue() {
- if (isConstructed() && theValue == null)
- throw new IllegalStateException("Tag is CONSTRUCTED " + HexUtil.toHexString(theTag.bytes));
- return HexUtil.toHexString(theValue);
- }
-
- /**
- * Text value with US-ASCII charset
- *
- * @return text
- */
- public String getTextValue() {
- return getTextValue(ASCII);
- }
-
- public String getTextValue(Charset aCharset) {
- if (isConstructed()) {
- throw new IllegalStateException("TLV is constructed");
- }
- return new String(theValue, aCharset);
- }
-
- public byte[] getBytesValue() {
- if (isConstructed() && theValue == null) {
- throw new IllegalStateException("TLV [" + theTag + "]is constructed");
- }
- return theValue;
- }
-
- public int getIntValue() {
- int i = 0;
- int j = 0;
- int number = 0;
-
- for (i = 0; i < theValue.length; i++) {
- j = theValue[i];
- number = number * 256 + (j < 0 ? j += 256 : j);
- }
- return number;
- }
-
- public List getValues() {
- if (isPrimitive())
- throw new IllegalStateException("Tag is PRIMITIVE");
- return theList;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- BerTlv berTlv = (BerTlv) o;
-
- if (theTag != null ? !theTag.equals(berTlv.theTag) : berTlv.theTag != null)
- return false;
- if (!Arrays.equals(theValue, berTlv.theValue))
- return false;
- return theList != null ? theList.equals(berTlv.theList) : berTlv.theList == null;
- }
-
- @Override
- public int hashCode() {
- int result = theTag != null ? theTag.hashCode() : 0;
- result = 31 * result + Arrays.hashCode(theValue);
- result = 31 * result + (theList != null ? theList.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
-
- return "BerTlv{" + "theTag=" + theTag + ", theValue=" + Arrays.toString(theValue) + ", theList=" + theList
- + '}';
- }
-
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvBuilder.java b/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvBuilder.java
deleted file mode 100644
index 6897a2e0..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvBuilder.java
+++ /dev/null
@@ -1,250 +0,0 @@
-package gov.gsa.pivconformance.tlv;
-
-import java.math.BigDecimal;
-import java.nio.charset.Charset;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-/**
- *
- */
-public class BerTlvBuilder {
-
- private static final Charset ASCII = Charset.forName("US-ASCII");
- private static final BigDecimal HUNDRED = new BigDecimal(100);
- private static final int DEFAULT_SIZE = 24 * 1024;
-
- public BerTlvBuilder() {
- this((BerTag) null);
- }
-
- public BerTlvBuilder(BerTag aTemplate) {
- this(aTemplate, new byte[DEFAULT_SIZE], 0, DEFAULT_SIZE);
- }
-
- public BerTlvBuilder(BerTlvs tlvs) {
- this((BerTag) null);
- for (BerTlv tlv : tlvs.getList()) {
- addBerTlv(tlv);
- }
- }
-
- public BerTlvBuilder(BerTag aTemplate, byte[] aBuffer, int aOffset, int aLength) {
- theTemplate = aTemplate;
- theBuffer = aBuffer;
- thePos = aOffset;
- theBufferOffset = aOffset;
- }
-
- public static BerTlvBuilder from(BerTlv aTlv) {
- if (aTlv.isConstructed()) {
- BerTlvBuilder builder = template(aTlv.getTag());
- for (BerTlv tlv : aTlv.theList) {
- builder.addBerTlv(tlv);
- }
- return builder;
- } else {
- return new BerTlvBuilder().addBerTlv(aTlv);
- }
- }
-
- public static BerTlvBuilder template(BerTag aTemplate) {
- return new BerTlvBuilder(aTemplate);
- }
-
- public BerTlvBuilder addEmpty(BerTag aObject) {
- return addBytes(aObject, new byte[] {}, 0, 0);
- }
-
- public BerTlvBuilder addByte(BerTag aObject, byte aByte) {
- // type
- int len = aObject.bytes.length;
- System.arraycopy(aObject.bytes, 0, theBuffer, thePos, len);
- thePos += len;
-
- // len
- theBuffer[thePos++] = 1;
-
- // value
- theBuffer[thePos++] = aByte;
- return this;
- }
-
- public BerTlvBuilder addAmount(BerTag aObject, BigDecimal aAmount) {
- BigDecimal numeric = aAmount.multiply(HUNDRED);
- StringBuilder sb = new StringBuilder(12);
- sb.append(numeric.longValue());
- while (sb.length() < 12) {
- sb.insert(0, '0');
- }
- return addHex(aObject, sb.toString());
- }
-
- public BerTlvBuilder addDate(BerTag aObject, Date aDate) {
- SimpleDateFormat format = new SimpleDateFormat("yyMMdd");
- return addHex(aObject, format.format(aDate));
- }
-
- public BerTlvBuilder addTime(BerTag aObject, Date aDate) {
- SimpleDateFormat format = new SimpleDateFormat("HHmmss");
- return addHex(aObject, format.format(aDate));
- }
-
- public int build() {
-
- if (theTemplate != null) {
-
- int tagLen = theTemplate.bytes.length;
- int lengthBytesCount = calculateBytesCountForLength(thePos);
-
- // shifts array
- System.arraycopy(theBuffer, theBufferOffset, theBuffer, tagLen + lengthBytesCount, thePos);
-
- // copies tag
- System.arraycopy(theTemplate.bytes, 0, theBuffer, theBufferOffset, theTemplate.bytes.length);
-
- fillLength(theBuffer, tagLen, thePos);
-
- thePos += tagLen + lengthBytesCount;
- }
- return thePos;
- }
-
- private void fillLength(byte[] aBuffer, int aOffset, int aLength) {
-
- if (aLength < 0x80) {
- aBuffer[aOffset] = (byte) aLength;
-
- } else if (aLength < 0x100) {
- aBuffer[aOffset] = (byte) 0x81;
- aBuffer[aOffset + 1] = (byte) aLength;
-
- } else if (aLength < 0x10000) {
-
- aBuffer[aOffset] = (byte) 0x82;
- aBuffer[aOffset + 1] = (byte) (aLength / 0x100);
- aBuffer[aOffset + 2] = (byte) (aLength % 0x100);
-
- } else if (aLength < 0x1000000) {
- aBuffer[aOffset] = (byte) 0x83;
- aBuffer[aOffset + 1] = (byte) (aLength / 0x10000);
- aBuffer[aOffset + 2] = (byte) (aLength / 0x100);
- aBuffer[aOffset + 3] = (byte) (aLength % 0x100);
- } else {
- throw new IllegalStateException("length [" + aLength + "] out of range (0x1000000)");
- }
- }
-
- private int calculateBytesCountForLength(int aLength) {
- int ret;
- if (aLength < 0x80) {
- ret = 1;
- } else if (aLength < 0x100) {
- ret = 2;
- } else if (aLength < 0x10000) {
- ret = 3;
- } else if (aLength < 0x1000000) {
- ret = 4;
- } else {
- throw new IllegalStateException("length [" + aLength + "] out of range (0x1000000)");
- }
- return ret;
- }
-
- public BerTlvBuilder addHex(BerTag aObject, String aHex) {
- byte[] buffer = HexUtil.parseHex(aHex);
- return addBytes(aObject, buffer, 0, buffer.length);
- }
-
- public BerTlvBuilder addBytes(BerTag aObject, byte[] aBytes) {
- return addBytes(aObject, aBytes, 0, aBytes.length);
- }
-
- public BerTlvBuilder addBytes(BerTag aTag, byte[] aBytes, int aFrom, int aLength) {
- int tagLength = aTag.bytes.length;
- int lengthBytesCount = calculateBytesCountForLength(aLength);
-
- // TAG
- System.arraycopy(aTag.bytes, 0, theBuffer, thePos, tagLength);
- thePos += tagLength;
-
- // LENGTH
- fillLength(theBuffer, thePos, aLength);
- thePos += lengthBytesCount;
-
- // VALUE
- System.arraycopy(aBytes, aFrom, theBuffer, thePos, aLength);
- thePos += aLength;
-
- return this;
- }
-
- public BerTlvBuilder add(BerTlvBuilder aBuilder) {
- byte[] array = aBuilder.buildArray();
- System.arraycopy(array, 0, theBuffer, thePos, array.length);
- thePos += array.length;
- return this;
- }
-
- public BerTlvBuilder addBerTlv(BerTlv aTlv) {
- if (aTlv.isConstructed()) {
- return add(from(aTlv));
- } else {
- return addBytes(aTlv.getTag(), aTlv.getBytesValue());
- }
- }
-
- /**
- * Add ASCII text
- *
- * @param aTag tag
- * @param aText text
- * @return builder
- */
- public BerTlvBuilder addText(BerTag aTag, String aText) {
- return addText(aTag, aText, ASCII);
- }
-
- /**
- * Add ASCII text
- *
- * @param aTag tag
- * @param aText text
- * @return builder
- */
- public BerTlvBuilder addText(BerTag aTag, String aText, Charset aCharset) {
- byte[] buffer = aText.getBytes(aCharset);
- return addBytes(aTag, buffer, 0, buffer.length);
- }
-
- public BerTlvBuilder addIntAsHex(BerTag aObject, int aCode, int aLength) {
- StringBuilder sb = new StringBuilder(aLength * 2);
- sb.append(aCode);
- while (sb.length() < aLength * 2) {
- sb.insert(0, '0');
- }
- return addHex(aObject, sb.toString());
- }
-
- public byte[] buildArray() {
- int count = build();
- byte[] buf = new byte[count];
- System.arraycopy(theBuffer, 0, buf, 0, count);
- return buf;
- }
-
- public BerTlv buildTlv() {
- int count = build();
- return new BerTlvParser().parseConstructed(theBuffer, theBufferOffset, count);
- }
-
- public BerTlvs buildTlvs() {
- int count = build();
- return new BerTlvParser().parse(theBuffer, theBufferOffset, count);
- }
-
- private final int theBufferOffset;
- private int thePos;
- private final byte[] theBuffer;
- private final BerTag theTemplate;
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvLogger.java b/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvLogger.java
deleted file mode 100644
index c82ed67f..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvLogger.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package gov.gsa.pivconformance.tlv;
-
-public class BerTlvLogger {
-
- public static void log(String aPadding, BerTlvs aTlv, IBerTlvLogger aLogger) {
-
- for (BerTlv tlv : aTlv.getList()) {
- log(aPadding, tlv, aLogger);
- }
- }
-
- public static void log(String aPadding, BerTlv aTlv, IBerTlvLogger aLogger) {
- if (aTlv == null) {
- aLogger.debug("{} is null", aPadding);
- return;
- }
-
- if (aTlv.isConstructed()) {
- aLogger.debug("{} [{}]", aPadding, HexUtil.toHexString(aTlv.getTag().bytes));
- for (BerTlv child : aTlv.getValues()) {
- log(aPadding + " ", child, aLogger);
- }
- } else {
- aLogger.debug("{} [{}] {}", aPadding, HexUtil.toHexString(aTlv.getTag().bytes), aTlv.getHexValue());
- }
-
- }
-
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvParser.java b/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvParser.java
deleted file mode 100644
index d6e417a0..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvParser.java
+++ /dev/null
@@ -1,236 +0,0 @@
-package gov.gsa.pivconformance.tlv;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- *
- */
-public class BerTlvParser {
-
- private final IBerTlvLogger log;
-
- public BerTlvParser() {
- this(EMPTY_LOGGER);
- }
-
- public BerTlvParser(IBerTlvLogger aLogger) {
- log = aLogger;
- }
-
- public BerTlv parseConstructed(byte[] aBuf) {
- return parseConstructed(aBuf, 0, aBuf.length);
- }
-
- public BerTlv parseConstructed(byte[] aBuf, int aOffset, int aLen) {
- ParseResult result = parseWithResult(0, aBuf, aOffset, aLen, true);
- return result.tlv;
- }
-
- public BerTlvs parse(byte[] aBuf) {
- return parse(aBuf, 0, aBuf.length);
- }
-
- public BerTlvs parse(byte[] aBuf, final int aOffset, int aLen) {
- List tlvs = new ArrayList();
- if (aLen == 0)
- return new BerTlvs(tlvs);
-
- int offset = aOffset;
- for (int i = 0; i < 100; i++) {
- ParseResult result = parseWithResult(0, aBuf, offset, aLen - offset, false);
- tlvs.add(result.tlv);
-
- if (result.offset >= aOffset + aLen) {
- break;
- }
-
- offset = result.offset;
-
- }
-
- return new BerTlvs(tlvs);
- }
-
- private ParseResult parseWithResult(int aLevel, byte[] aBuf, int aOffset, int aLen) {
- return parseWithResult(aLevel, aBuf, aOffset, aLen, true);
- }
-
- private ParseResult parseWithResult(int aLevel, byte[] aBuf, int aOffset, int aLen, boolean recurse) {
- String levelPadding = createLevelPadding(aLevel);
- if (aOffset + aLen > aBuf.length) {
- throw new IllegalStateException("Length is out of the range [offset=" + aOffset + ", len=" + aLen
- + ", array.length=" + aBuf.length + ", level=" + aLevel + "]");
- }
- if (log.isDebugEnabled()) {
- log.debug("{}parseWithResult(level={}, offset={}, len={}, buf={})", levelPadding, aLevel, aOffset, aLen,
- HexUtil.toFormattedHexString(aBuf, aOffset, aLen));
- }
-
- // tag
- int tagBytesCount = getTagBytesCount(aBuf, aOffset);
- BerTag tag = createTag(levelPadding, aBuf, aOffset, tagBytesCount);
- if (log.isDebugEnabled()) {
- log.debug("{}tag = {}, tagBytesCount={}, tagBuf={}", levelPadding, tag, tagBytesCount,
- HexUtil.toFormattedHexString(aBuf, aOffset, tagBytesCount));
- }
-
- // length
- int lengthBytesCount = getLengthBytesCount(aBuf, aOffset + tagBytesCount);
- int valueLength = getDataLength(aBuf, aOffset + tagBytesCount);
-
- if (log.isDebugEnabled()) {
- log.debug("{}lenBytesCount = {}, len = {}, lenBuf = {}", levelPadding, lengthBytesCount, valueLength,
- HexUtil.toFormattedHexString(aBuf, aOffset + tagBytesCount, lengthBytesCount));
- }
-
- // value
- if (tag.isConstructed() && recurse) {
-
- ArrayList list = new ArrayList();
- addChildren(aLevel, aBuf, aOffset, levelPadding, tagBytesCount, lengthBytesCount, valueLength, list);
-
- int resultOffset = aOffset + tagBytesCount + lengthBytesCount + valueLength;
- if (log.isDebugEnabled()) {
- log.debug("{}returning constructed offset = {}", levelPadding, resultOffset);
- }
- byte[] value = new byte[valueLength];
- System.arraycopy(aBuf, aOffset + tagBytesCount + lengthBytesCount, value, 0, valueLength);
- return new ParseResult(new BerTlv(tag, list, value), resultOffset);
- } else {
- // value
- byte[] value = new byte[valueLength];
- log.debug("src.length={}, srcPos={}, value.length={}, valueLength={}", aBuf.length,
- aOffset + tagBytesCount + lengthBytesCount, value.length, valueLength);
- System.arraycopy(aBuf, aOffset + tagBytesCount + lengthBytesCount, value, 0, valueLength);
- int resultOffset = aOffset + tagBytesCount + lengthBytesCount + valueLength;
- if (log.isDebugEnabled()) {
- log.debug("{}value = {}", levelPadding, HexUtil.toFormattedHexString(value));
- log.debug("{}returning primitive offset = {}", levelPadding, resultOffset);
- }
- return new ParseResult(new BerTlv(tag, value), resultOffset);
- }
-
- }
-
- /**
- *
- * @param aLevel level for debug
- * @param aBuf buffer
- * @param aOffset offset (first byte)
- * @param levelPadding level padding (for debug)
- * @param aTagBytesCount tag bytes count
- * @param aDataBytesCount data bytes count
- * @param valueLength length
- * @param list list to add
- */
- private void addChildren(int aLevel, byte[] aBuf, int aOffset, String levelPadding, int aTagBytesCount,
- int aDataBytesCount, int valueLength, ArrayList list) {
- int startPosition = aOffset + aTagBytesCount + aDataBytesCount;
- int len = valueLength;
- while (startPosition <= aOffset + valueLength) {
- ParseResult result = parseWithResult(aLevel + 1, aBuf, startPosition, len);
- list.add(result.tlv);
-
- startPosition = result.offset;
- len = valueLength - startPosition;
-
- if (log.isDebugEnabled()) {
- log.debug("{}level {}: adding {} with offset {}, startPosition={}, aDataBytesCount={}, valueLength={}",
- levelPadding, aLevel, result.tlv.getTag(), result.offset, startPosition, aDataBytesCount,
- valueLength);
- }
- }
- }
-
- private String createLevelPadding(int aLevel) {
- if (!log.isDebugEnabled()) {
- return "";
- }
-
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < aLevel * 4; i++) {
- sb.append(' ');
- }
- return sb.toString();
- }
-
- private static class ParseResult {
- public ParseResult(BerTlv aTlv, int aOffset) {
- tlv = aTlv;
- offset = aOffset;
- }
-
- @Override
- public String toString() {
- return "ParseResult{" + "tlv=" + tlv + ", offset=" + offset + '}';
- }
-
- private final BerTlv tlv;
- private final int offset;
- }
-
- public BerTag createTag(String aLevelPadding, byte[] aBuf, int aOffset, int aLength) {
- if (log.isDebugEnabled()) {
- log.debug("{}Creating tag {}...", aLevelPadding, HexUtil.toFormattedHexString(aBuf, aOffset, aLength));
- }
- return new BerTag(aBuf, aOffset, aLength);
- }
-
- public int getTagBytesCount(byte[] aBuf, int aOffset) {
- if ((aBuf[aOffset] & 0x1F) == 0x1F) { // see subsequent bytes
- int len = 2;
- for (int i = aOffset + 1; i < aOffset + 10; i++) {
- if ((aBuf[i] & 0x80) != 0x80) {
- break;
- }
- len++;
- }
- return len;
- } else {
- return 1;
- }
- }
-
- public int getDataLength(byte[] aBuf, int aOffset) {
-
- int length = aBuf[aOffset] & 0xff;
-
- if ((length & 0x80) == 0x80) {
- int numberOfBytes = length & 0x7f;
- if (numberOfBytes > 3) {
- throw new IllegalStateException(
- String.format("At position %d the len is more then 3 [%d]", aOffset, numberOfBytes));
- }
-
- length = 0;
- for (int i = aOffset + 1; i < aOffset + 1 + numberOfBytes; i++) {
- length = length * 0x100 + (aBuf[i] & 0xff);
- }
-
- }
- return length;
- }
-
- public static int getLengthBytesCount(byte aBuf[], int aOffset) {
-
- int len = aBuf[aOffset] & 0xff;
- if ((len & 0x80) == 0x80) {
- return 1 + (len & 0x7f);
- } else {
- return 1;
- }
- }
-
- private static final IBerTlvLogger EMPTY_LOGGER = new IBerTlvLogger() {
- @Override
- public boolean isDebugEnabled() {
- return false;
- }
-
- @Override
- public void debug(String aFormat, Object... args) {
- }
- };
-
-}
\ No newline at end of file
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvs.java b/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvs.java
deleted file mode 100644
index b10fcb0f..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/BerTlvs.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package gov.gsa.pivconformance.tlv;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class BerTlvs {
-
- protected BerTlvs(List aTlvs) {
- tlvs = aTlvs;
- }
-
- public BerTlv find(BerTag aTag) {
- for (BerTlv tlv : tlvs) {
- BerTlv found = tlv.find(aTag);
- if (found != null) {
- return found;
- }
- }
- return null;
- }
-
- public List findAll(BerTag aTag) {
- List list = new ArrayList();
- for (BerTlv tlv : tlvs) {
- list.addAll(tlv.findAll(aTag));
- }
- return list;
- }
-
- public List getList() {
- return tlvs;
- }
-
- private final List tlvs;
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- BerTlvs berTlvs = (BerTlvs) o;
-
- return tlvs != null ? tlvs.equals(berTlvs.tlvs) : berTlvs.tlvs == null;
- }
-
- @Override
- public int hashCode() {
- return tlvs != null ? tlvs.hashCode() : 0;
- }
-
- @Override
- public String toString() {
- return "BerTlvs{" + "tlvs=" + tlvs + '}';
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/CCTTlvLogger.java b/cardlib/src/main/java/gov/gsa/pivconformance/tlv/CCTTlvLogger.java
deleted file mode 100644
index 34612c8f..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/CCTTlvLogger.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package gov.gsa.pivconformance.tlv;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class CCTTlvLogger implements IBerTlvLogger {
-
- private Logger m_logger = null;
-
- public CCTTlvLogger(Class> clazz) {
- m_logger = LoggerFactory.getLogger(clazz.toString() + ".TLVParser");
- }
-
- @Override
- public boolean isDebugEnabled() {
- return m_logger != null && m_logger.isDebugEnabled();
- }
-
- @Override
- public void debug(String aFormat, Object... args) {
- if (m_logger == null)
- return;
- // m_logger.debug(aFormat, args);
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/HexUtil.java b/cardlib/src/main/java/gov/gsa/pivconformance/tlv/HexUtil.java
deleted file mode 100644
index 1a90697b..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/HexUtil.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package gov.gsa.pivconformance.tlv;
-
-public class HexUtil {
-
- private static final char[] CHARS_TABLES = "0123456789ABCDEF".toCharArray();
- static final byte[] BYTES = new byte[128];
-
- static {
- for (int i = 0; i < 10; i++) {
- BYTES['0' + i] = (byte) i;
- BYTES['A' + i] = (byte) (10 + i);
- BYTES['a' + i] = (byte) (10 + i);
- }
- }
-
- public static String toHexString(byte[] aBytes) {
- return toHexString(aBytes, 0, aBytes.length);
- }
-
- public static String toFormattedHexString(byte[] aBytes) {
- return toFormattedHexString(aBytes, 0, aBytes.length);
- }
-
- public static String toHexString(byte[] aBytes, int aLength) {
- return toHexString(aBytes, 0, aLength);
- }
-
- public static byte[] parseHex(String aHexString) {
- char[] src = aHexString.replace("\n", "").replace(" ", "").toUpperCase().toCharArray();
- byte[] dst = new byte[src.length / 2];
-
- for (int si = 0, di = 0; di < dst.length; di++) {
- byte high = BYTES[src[si++] & 0x7f];
- byte low = BYTES[src[si++] & 0x7f];
- dst[di] = (byte) ((high << 4) + low);
- }
-
- return dst;
- }
-
- public static String toFormattedHexString(byte[] aBytes, int aOffset, int aLength) {
- StringBuilder sb = new StringBuilder();
- sb.append("[");
- sb.append(aLength);
- sb.append("] :");
- for (int si = aOffset, di = 0; si < aOffset + aLength; si++, di++) {
- byte b = aBytes[si];
- if (di % 4 == 0) {
- sb.append(" ");
- } else {
- sb.append(' ');
- }
- sb.append(CHARS_TABLES[(b & 0xf0) >>> 4]);
- sb.append(CHARS_TABLES[(b & 0x0f)]);
-
- }
-
- return sb.toString();
-
- }
-
- public static String toHexString(byte[] aBytes, int aOffset, int aLength) {
- char[] dst = new char[aLength * 2];
-
- for (int si = aOffset, di = 0; si < aOffset + aLength; si++) {
- byte b = aBytes[si];
- dst[di++] = CHARS_TABLES[(b & 0xf0) >>> 4];
- dst[di++] = CHARS_TABLES[(b & 0x0f)];
- }
-
- return new String(dst);
- }
-
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/IBerTlvLogger.java b/cardlib/src/main/java/gov/gsa/pivconformance/tlv/IBerTlvLogger.java
deleted file mode 100644
index 18e54414..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/IBerTlvLogger.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package gov.gsa.pivconformance.tlv;
-
-public interface IBerTlvLogger {
-
- boolean isDebugEnabled();
-
- void debug(String aFormat, Object... args);
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/README.md b/cardlib/src/main/java/gov/gsa/pivconformance/tlv/README.md
deleted file mode 100644
index ab821491..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tlv/README.md
+++ /dev/null
@@ -1,207 +0,0 @@
-== TLV parser ==
-
-Classes here are adapted from [@evsinev/ber-tlv](https://github.com/evsinev/ber-tlv) and are used under the Apache 2.0 license, reproduced at the bottom of this file.
-
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tools/ConformanceTestRunner.java b/cardlib/src/main/java/gov/gsa/pivconformance/tools/ConformanceTestRunner.java
deleted file mode 100644
index fd3ffa31..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tools/ConformanceTestRunner.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package gov.gsa.pivconformance.tools;
-
-import gov.gsa.pivconformance.utils.PCSCUtils;
-import gov.gsa.pivconformance.utils.VersionUtils;
-import org.apache.commons.cli.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class ConformanceTestRunner {
- // slf4j will thunk this through to an appropriately configured logging library
- private static final Logger s_logger = LoggerFactory.getLogger(ConformanceTestRunner.class);
- private static final Options s_options = new Options();
- static {
- s_options.addOption("h", "help", false, "Print this help and exit");
- s_options.addOption(null, "listReaders", false, "Print a list of connected readers and exit");
- // Specify a specific reader, as given by --listReaders. If none is specified
- // the first reader will be used
- s_options.addOption("r", "reader", true, "Use the specified reader for conformance tests");
- s_options.addOption("t", "testConfig", true, "Use the specified test configuration file");
- s_options.addOption(null, "runSelected", true, "Run only the specified test cases (comma separated)");
- s_options.addOption(null, "runTagged", true, "Run all test cases with the specified tags (comma separated)");
- s_options.addOption(null, "dryRun", true,
- "Collect and configure test cases, and log which ones would be run, but do not execute");
- s_options.addOption("l", "logConfig", true, "Use the specified log configuration file");
- s_options.addOption("o", "outDir", true, "Specify the output directory for reports");
- s_options.addOption("n", false, "Number of times to repeat the run");
- }
-
- private static void PrintHelpAndExit(int exitCode) {
- new HelpFormatter().printHelp("ConformanceTestRunner ", s_options);
- System.exit(exitCode);
- }
-
- private static List CheckIncompatibleOptions(String option, String incompatibleOptions, CommandLine cmd) {
- List incompatibleOptionList = Arrays.asList(incompatibleOptions.split("\\s*,\\s*"));
- // if the split didn't find anything, just treat the whole string as an option
- // to test
- if (incompatibleOptionList.isEmpty()) {
- incompatibleOptionList.add(incompatibleOptions);
- }
- ArrayList messages = new ArrayList<>();
- for (String opt : incompatibleOptionList) {
- if (cmd.hasOption(opt)) {
- messages.add(option + " cannot be combined with " + opt + ".");
- }
- }
- return messages;
- }
-
- private static List CheckRequiredOptions(String option, String requiredOptions, CommandLine cmd) {
- List requiredOptionList = Arrays.asList(requiredOptions.split("\\s*,\\s*"));
- // if the split didn't find anything, just treat the whole string as an option
- // to test
- if (requiredOptionList.isEmpty()) {
- requiredOptionList.add(requiredOptions);
- }
- ArrayList messages = new ArrayList<>();
- for (String opt : requiredOptionList) {
- if (!cmd.hasOption(opt)) {
- messages.add(option + " requires that " + opt + " also be specified.");
- }
- }
- return messages;
- }
-
- private static void LogErrorsIfNonEmptyAndExit(String msg, List messages, int exitCode) {
- if (!messages.isEmpty()) {
- if (msg != null && !msg.isEmpty())
- s_logger.error(msg);
- for (String message : messages) {
- s_logger.error(message);
- }
- System.exit(exitCode);
- }
- }
-
- public static void main(String[] args) {
- s_logger.info("main class: {}", MethodHandles.lookup().lookupClass().getSimpleName());
- s_logger.info("package version: {}", VersionUtils.GetPackageVersionString());
- s_logger.info("build time: {}", VersionUtils.GetPackageBuildTime());
-
- CommandLineParser p = new DefaultParser();
- CommandLine cmd = null;
- try {
- cmd = p.parse(s_options, args);
- } catch (ParseException e) {
- s_logger.error("Failed to parse command line arguments", e);
- PrintHelpAndExit(1);
- }
-
- if (cmd.hasOption("help")) {
- PrintHelpAndExit(0);
- }
-
- if (cmd.hasOption("listReaders")) {
- List messages = CheckIncompatibleOptions("listReaders",
- "reader,testConfig,runSingle,runTagged,outDir,n", cmd);
- LogErrorsIfNonEmptyAndExit("Incompatible command line options found", messages, 1);
- List readers = PCSCUtils.GetConnectedReaders();
- if (!readers.isEmpty()) {
- s_logger.info("Currently connected readers:");
- int currReader = 0;
- for (String reader : readers) {
- currReader++;
- s_logger.info("{}: {}", currReader, reader);
- }
- } else {
- s_logger.info("No readers are connected.");
- }
- System.exit(0);
- }
-
- if (cmd.hasOption("testConfig")) {
- List messages = CheckIncompatibleOptions("testConfig", "listReaders", cmd);
- LogErrorsIfNonEmptyAndExit("Incompatible command line options found", messages, 1);
- messages = CheckRequiredOptions("testConfig", "outDir", cmd);
- LogErrorsIfNonEmptyAndExit("Required command line options missing", messages, 1);
- }
-
- }
-}
diff --git a/cardlib/src/main/java/gov/gsa/pivconformance/tools/PIVRunner.java b/cardlib/src/main/java/gov/gsa/pivconformance/tools/PIVRunner.java
deleted file mode 100644
index 646f9a41..00000000
--- a/cardlib/src/main/java/gov/gsa/pivconformance/tools/PIVRunner.java
+++ /dev/null
@@ -1,706 +0,0 @@
-package gov.gsa.pivconformance.tools;
-
-import gov.gsa.pivconformance.card.client.*;
-import gov.gsa.pivconformance.tlv.*;
-import gov.gsa.pivconformance.utils.PCSCUtils;
-import gov.gsa.pivconformance.utils.VersionUtils;
-import org.apache.commons.cli.*;
-import org.apache.commons.codec.binary.Hex;
-import org.bouncycastle.cms.CMSSignedData;
-import org.bouncycastle.cms.SignerId;
-import org.bouncycastle.cms.SignerInformation;
-import org.bouncycastle.cms.SignerInformationStore;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import java.text.SimpleDateFormat;
-
-import javax.smartcardio.*;
-import java.lang.invoke.MethodHandles;
-import java.security.cert.X509Certificate;
-import java.util.*;
-
-public class PIVRunner {
- // slf4j will thunk this through to an appropriately configured logging library
- private static final Logger s_logger = LoggerFactory.getLogger(PIVRunner.class);
- private static final Options s_options = new Options();
-
- static {
- s_options.addOption("a", "all", false, "Scan all readers");
- s_options.addOption("h", "help", false, "Print this help and exit");
- }
-
- private static void PrintHelpAndExit(int exitCode) {
- new HelpFormatter().printHelp("PIVRunner ", s_options);
- System.exit(exitCode);
- }
-
- public static boolean TestCard(CardHandle c) {
- if (c.isValid()) {
- CardTerminal t = c.getConnectionDescription().getTerminal();
- try {
- if (t.isCardPresent()) {
- s_logger.info("Card found in reader {}", t.getName());
- } else {
- s_logger.error("No card was present in reader {}", t.getName());
- return false;
- }
- } catch (CardException e) {
- s_logger.error("Card communication error", e);
- }
- Card conn = c.getCard();
- s_logger.info("Card connected.");
- s_logger.info("Card protocol: {}", conn.getProtocol());
- s_logger.info("Card ATR: {}", Hex.encodeHexString(conn.getATR().getBytes()));
- ApplicationProperties cardAppProperties = new ApplicationProperties();
- DefaultPIVApplication piv = new DefaultPIVApplication();
- ApplicationAID aid = new ApplicationAID();
- s_logger.info("Attempting to select default PIV application");
- MiddlewareStatus result = piv.pivSelectCardApplication(c, aid, cardAppProperties);
- s_logger.info("pivSelectCardApplication() returned {}", result);
- if (result == MiddlewareStatus.PIV_OK) {
- byte[] pcap = cardAppProperties.getBytes();
-
- byte[] appID = cardAppProperties.getAppID();
- String appLabel = cardAppProperties.getAppLabel();
- String url = cardAppProperties.getURL();
- List cryptoAlgs = cardAppProperties.getCryptoAlgs();
- byte[] coexistentTagAllocationAuthority = cardAppProperties.getCoexistentTagAllocationAuthority();
-
- if (appID != null)
- s_logger.info("Application identifier of application: {}", Hex.encodeHexString(appID));
-
- if (coexistentTagAllocationAuthority != null)
- s_logger.info("Coexistent tag allocation authority: {}",
- Hex.encodeHexString(coexistentTagAllocationAuthority));
-
- if (appLabel != "")
- s_logger.info("Application label: {}", appLabel);
-
- if (url != "")
- s_logger.info("Uniform resource locator: {}", url);
-
- if (cryptoAlgs != null) {
- for (byte[] b : cryptoAlgs) {
-
- s_logger.info("Cryptographic algorithms supported:");
- s_logger.info("Algorithm ID: {} Algorithm Description: {}", Hex.encodeHexString(b),
- TagConstants.algMAP.get(b));
- }
- }
-
- s_logger.info("PCAP: {}", Hex.encodeHexString(pcap));
- BerTlvParser tp = new BerTlvParser(new CCTTlvLogger(PIVRunner.class));
- BerTlv outer = tp.parseConstructed(pcap);
- List values = outer.getValues();
- for (BerTlv tlv : values) {
- if (tlv.isPrimitive()) {
- s_logger.info("PCAP Tag {}: {}", Hex.encodeHexString(tlv.getTag().bytes),
- Hex.encodeHexString(tlv.getBytesValue()));
- } else {
- s_logger.info("PCAP object: {}", Hex.encodeHexString(tlv.getTag().bytes));
- }
- }
-
- result = MiddlewareStatus.PIV_AUTHENTICATION_FAILURE;
-
- if (result != MiddlewareStatus.PIV_OK)
- s_logger.error("Error authenticating to the smartcard: {}", result.toString());
-
- X509Certificate signingCertificate = null;
-
- HashMap soDataElements = new HashMap();
- PIVDataObject securityObject = null;
-
- for (String containerOID : APDUConstants.MandatoryContainers()) {
- PIVDataObject dataObject = PIVDataObjectFactory.createDataObjectForOid(containerOID);
- s_logger.info("Attempting to read data object for OID {} ({})", containerOID,
- APDUConstants.oidNameMap.get(containerOID));
- result = piv.pivGetData(c, containerOID, dataObject);
- if (result != MiddlewareStatus.PIV_OK)
- continue;
- boolean decoded = dataObject.decode();
- s_logger.info("{} {}", dataObject.getFriendlyName(),
- decoded ? "decoded successfully" : "failed to decode");
- s_logger.info("pivGetData returned {}", result);
- s_logger.info(dataObject.toString());
-
- if (containerOID.equals(APDUConstants.CARD_CAPABILITY_CONTAINER_OID)) {
-
- s_logger.info("Card Identifier: {}",
- Hex.encodeHexString(((CardCapabilityContainer) dataObject).getCardIdentifier()));
- s_logger.info("Capability Container Version Number: {}", Hex.encodeHexString(
- ((CardCapabilityContainer) dataObject).getCapabilityContainerVersionNumber()));
- s_logger.info("Capability Grammar Version Number: {}", Hex.encodeHexString(
- ((CardCapabilityContainer) dataObject).getCapabilityGrammarVersionNumber()));
-
- List appCardURLList = ((CardCapabilityContainer) dataObject).getAppCardURL();
-
- if (appCardURLList.size() > 0) {
- s_logger.info("Applications CardURL List");
- for (byte[] u : appCardURLList) {
- s_logger.info("{}", Hex.encodeHexString(u));
- }
- }
-
- s_logger.info("Registered Data Model number: {}", Hex.encodeHexString(
- ((CardCapabilityContainer) dataObject).getRegisteredDataModelNumber()));
- s_logger.info("Access Control Rule Table: {}", Hex
- .encodeHexString(((CardCapabilityContainer) dataObject).getAccessControlRuleTable()));
-
- s_logger.info("Card APDUs Tag Present: {}",
- ((CardCapabilityContainer) dataObject).getCardAPDUs());
- s_logger.info("RedirectionTag Tag Present: {}",
- ((CardCapabilityContainer) dataObject).getRedirectionTag());
- s_logger.info("Capability Tuples Tag Present: {}",
- ((CardCapabilityContainer) dataObject).getCapabilityTuples());
- s_logger.info("Status Tuples Tag Present: {}",
- ((CardCapabilityContainer) dataObject).getStatusTuples());
- s_logger.info("Next CCC Tag Present: {}", ((CardCapabilityContainer) dataObject).getNextCCC());
-
- if (((CardCapabilityContainer) dataObject).getExtendedApplicationCardURL() != null) {
-
- List extendedAppCardURLList = ((CardCapabilityContainer) dataObject)
- .getExtendedApplicationCardURL();
-
- if (extendedAppCardURLList.size() > 0) {
- s_logger.info("Extended Application CardURL List:");
- for (byte[] u2 : extendedAppCardURLList) {
- s_logger.info(" {}", Hex.encodeHexString(u2));
- }
- }
- }
-
- if (((CardCapabilityContainer) dataObject).getSecurityObjectBuffer() != null)
- s_logger.info("Security Object Buffer: {}", Hex
- .encodeHexString(((CardCapabilityContainer) dataObject).getSecurityObjectBuffer()));
-
- s_logger.info("Error Detection Code Tag Present: {}",
- ((CardCapabilityContainer) dataObject).getErrorDetectionCode());
-
- soDataElements.put(APDUConstants.CARD_CAPABILITY_CONTAINER_OID,
- ((CardCapabilityContainer) dataObject).getSignedContent());
- }
-
- if (containerOID.equals(APDUConstants.CARD_HOLDER_UNIQUE_IDENTIFIER_OID)) {
- if (((CardHolderUniqueIdentifier) dataObject).getBufferLength() != null) {
- s_logger.info("Buffer Length: {}",
- Hex.encodeHexString(((CardHolderUniqueIdentifier) dataObject).getBufferLength()));
- }
- s_logger.info("FASC-N: {}",
- Hex.encodeHexString(((CardHolderUniqueIdentifier) dataObject).getfASCN()));
- if (((CardHolderUniqueIdentifier) dataObject).getOrganizationalIdentifier() != null) {
- s_logger.info("Organizational Identifier: {}", Hex.encodeHexString(
- ((CardHolderUniqueIdentifier) dataObject).getOrganizationalIdentifier()));
- }
- if (((CardHolderUniqueIdentifier) dataObject).getdUNS() != null) {
- s_logger.info("DUNS: {}",
- Hex.encodeHexString(((CardHolderUniqueIdentifier) dataObject).getdUNS()));
- }
- s_logger.info("GUID: {}",
- Hex.encodeHexString(((CardHolderUniqueIdentifier) dataObject).getgUID()));
-
- SimpleDateFormat sdfmt = new SimpleDateFormat("MM/dd/yyyy");
- s_logger.info("Expiration Date: {}",
- sdfmt.format(((CardHolderUniqueIdentifier) dataObject).getExpirationDate()));
-
- s_logger.info("Cardholder UUID: {}",
- Hex.encodeHexString(((CardHolderUniqueIdentifier) dataObject).getCardholderUUID()));
- s_logger.info("Issuer Asymmetric Signature Info:");
-
- CMSSignedData sd = ((CardHolderUniqueIdentifier) dataObject).getAsymmetricSignature();
- SignerInformationStore signers = sd.getSignerInfos();
- Collection collection = signers.getSigners();
- Iterator it = collection.iterator();
-
- while (it.hasNext()) {
- SignerInformation signer = it.next();
- SignerId sid = signer.getSID();
- String issuer = sid.getIssuer().toString();
- String serial = Hex.encodeHexString(sid.getSerialNumber().toByteArray());
- String skid = "";
- if (sid.getSubjectKeyIdentifier() != null)
- skid = Hex.encodeHexString(sid.getSubjectKeyIdentifier());
-
- if (sid.getSubjectKeyIdentifier() != null)
- s_logger.info("Signer skid: {} ", skid);
- else
- s_logger.info("Signer Issuer: {}, Serial Number: {} ", issuer, serial);
-
- }
- s_logger.info("Signature valid: {}",
- ((CardHolderUniqueIdentifier) dataObject).verifySignature());
- signingCertificate = ((CardHolderUniqueIdentifier) dataObject).getSignerCert();
-
- s_logger.info("Error Detection Code Tag Present: {}",
- ((CardHolderUniqueIdentifier) dataObject).getErrorDetectionCode());
-
- soDataElements.put(APDUConstants.CARD_HOLDER_UNIQUE_IDENTIFIER_OID,
- ((CardHolderUniqueIdentifier) dataObject).getChuidContainer());
- }
-
- if (containerOID.equals(APDUConstants.X509_CERTIFICATE_FOR_PIV_AUTHENTICATION_OID)) {
- X509Certificate pibAuthCert = ((X509CertificateDataObject) dataObject).getCertificate();
-
- s_logger.info("PIV Auth Cert SubjectName: {}", pibAuthCert.getSubjectDN().getName());
- s_logger.info("PIV Auth Cert SerialNumber: {}",
- Hex.encodeHexString(pibAuthCert.getSerialNumber().toByteArray()));
- s_logger.info("PIV Auth Cert IssuerName: {}", pibAuthCert.getSubjectDN().getName());
- }
-
- if (containerOID.equals(APDUConstants.CARDHOLDER_FINGERPRINTS_OID)) {
-
- s_logger.info("Fingerprint I & II: {}",
- Hex.encodeHexString(((CardHolderBiometricData) dataObject).getBiometricData()));
-
- s_logger.info("Biometric Creation Date: {}",
- ((CardHolderBiometricData) dataObject).getBiometricCreationDate());
- s_logger.info("Validity Period From: {}",
- ((CardHolderBiometricData) dataObject).getValidityPeriodFrom());
- s_logger.info("Validity Period To: {}",
- ((CardHolderBiometricData) dataObject).getValidityPeriodTo());
-
- CMSSignedData sd = ((SignedPIVDataObject) dataObject).getAsymmetricSignature();
- SignerInformationStore signers = sd.getSignerInfos();
- Collection collection = signers.getSigners();
- Iterator it = collection.iterator();
-
- while (it.hasNext()) {
- SignerInformation signer = it.next();
- SignerId sid = signer.getSID();
- String issuer = sid.getIssuer().toString();
- String serial = Hex.encodeHexString(sid.getSerialNumber().toByteArray());
- String skid = "";
- if (sid.getSubjectKeyIdentifier() != null)
- skid = Hex.encodeHexString(sid.getSubjectKeyIdentifier());
-
- if (sid.getSubjectKeyIdentifier() != null)
- s_logger.info("Signer skid: {} ", skid);
- else
- s_logger.info("Signer Issuer: {}, Serial Number: {} ", issuer, serial);
-
- }
- if (signingCertificate != null)
- s_logger.info("Is signatue valid: {}",
- ((SignedPIVDataObject) dataObject).verifySignature());
- else
- s_logger.info("Missing signing certificate to verify signature.");
-
- s_logger.info("Error Detection Code Tag Present: {}",
- ((CardHolderBiometricData) dataObject).getErrorDetectionCode());
-
- soDataElements.put(APDUConstants.CARDHOLDER_FINGERPRINTS_OID,
- ((CardHolderBiometricData) dataObject).getCbeffContainer());
- }
-
- if (containerOID.equals(APDUConstants.SECURITY_OBJECT_OID)) {
-
- s_logger.info("RAW Mapping of DG to ContainerID value: {}",
- Hex.encodeHexString(((SecurityObject) dataObject).getMapping()));
-
- HashMap idMap = ((SecurityObject) dataObject).getContainerIDList();
-
- s_logger.info("List of containers included in the Security Object:");
- for (HashMap.Entry entry : idMap.entrySet()) {
- s_logger.info("Container ID: {}, Container Name: {}, Container OID: {}", entry.getKey(),
- entry.getValue(), APDUConstants.oidNameMap.get(entry.getValue()));
- }
-
- CMSSignedData sd = ((SignedPIVDataObject) dataObject).getAsymmetricSignature();
- SignerInformationStore signers = sd.getSignerInfos();
- Collection collection = signers.getSigners();
- Iterator