Permalink
Browse files

Merge pull request #63 from demsey/security

SMAdapter: add methods for CAVV/AAV calculation and verification.
  • Loading branch information...
2 parents d8190bf + b24bdc5 commit c7cfdcad747318fa7edb5c512810f1cd0350e25f @ar ar committed Jul 22, 2014
@@ -667,6 +667,31 @@ public String calculateCVV(String accountNo, SecureDESKey cvkA, SecureDESKey cvk
}
@Override
+ public String calculateCAVV(String accountNo, SecureDESKey cvk, String upn,
+ String authrc, String sfarc) throws SMException {
+
+ List<Loggeable> cmdParameters = new ArrayList<Loggeable>();
+ cmdParameters.add(new SimpleMsg("parameter", "account number", accountNo));
+ cmdParameters.add(new SimpleMsg("parameter", "cvk", cvk == null ? "" : cvk));
+ cmdParameters.add(new SimpleMsg("parameter", "unpredictable number", upn));
+ cmdParameters.add(new SimpleMsg("parameter", "auth rc", authrc));
+ cmdParameters.add(new SimpleMsg("parameter", "second factor auth rc", sfarc));
+ LogEvent evt = new LogEvent(this, "s-m-operation");
+ evt.addMessage(new SimpleMsg("command", "Calculate CAVV/AAV", cmdParameters));
+ String result = null;
+ try {
+ result = calculateCAVVImpl(accountNo, cvk, upn, authrc, sfarc);
+ evt.addMessage(new SimpleMsg("result", "Calculated CAVV/AAV", result));
+ } catch (Exception e) {
+ evt.addMessage(e);
+ throw e instanceof SMException ? (SMException) e : new SMException(e);
+ } finally {
+ Logger.log(evt);
+ }
+ return result;
+ }
+
+ @Override
public boolean verifyCVV(String accountNo , SecureDESKey cvkA, SecureDESKey cvkB,
String cvv, Date expDate, String serviceCode) throws SMException {
@@ -693,6 +718,32 @@ public boolean verifyCVV(String accountNo , SecureDESKey cvkA, SecureDESKey cvkB
}
@Override
+ public boolean verifyCAVV(String accountNo, SecureDESKey cvk, String cavv,
+ String upn, String authrc, String sfarc) throws SMException {
+
+ List<Loggeable> cmdParameters = new ArrayList<Loggeable>();
+ cmdParameters.add(new SimpleMsg("parameter", "account number", accountNo));
+ cmdParameters.add(new SimpleMsg("parameter", "cvk", cvk == null ? "" : cvk));
+ cmdParameters.add(new SimpleMsg("parameter", "cavv", cavv == null ? "" : cavv));
+ cmdParameters.add(new SimpleMsg("parameter", "unpredictable number", upn));
+ cmdParameters.add(new SimpleMsg("parameter", "auth rc", authrc));
+ cmdParameters.add(new SimpleMsg("parameter", "second factor auth rc", sfarc));
+ LogEvent evt = new LogEvent(this, "s-m-operation");
+ evt.addMessage(new SimpleMsg("command", "Verify CAVV/AAV", cmdParameters));
+ boolean r = false;
+ try {
+ r = verifyCAVVImpl(accountNo, cvk, cavv, upn, authrc, sfarc);
+ evt.addMessage(new SimpleMsg("result", "Verification status", r));
+ } catch (Exception e) {
+ evt.addMessage(e);
+ throw e instanceof SMException ? (SMException) e : new SMException(e);
+ } finally {
+ Logger.log(evt);
+ }
+ return r;
+ }
+
+ @Override
public boolean verifydCVV(String accountNo, SecureDESKey imkac, String dcvv,
Date expDate, String serviceCode, byte[] atc, MKDMethod mkdm)
throws SMException {
@@ -1309,6 +1360,22 @@ protected String calculateCVVImpl(String accountNo, SecureDESKey cvkA, SecureDES
throw new SMException("Operation not supported in: " + this.getClass().getName());
}
+
+ /**
+ * Your SMAdapter should override this method if it has this functionality
+ * @param accountNo
+ * @param cvk
+ * @param upn
+ * @param authrc
+ * @param sfarc
+ * @return Cardholder Authentication Verification Value
+ * @throws SMException
+ */
+ protected String calculateCAVVImpl(String accountNo, SecureDESKey cvk, String upn,
+ String authrc, String sfarc) throws SMException {
+ throw new SMException("Operation not supported in: " + this.getClass().getName());
+ }
+
/**
* Your SMAdapter should override this method if it has this functionality
* @param accountNo
@@ -1328,6 +1395,22 @@ protected boolean verifyCVVImpl(String accountNo, SecureDESKey cvkA, SecureDESKe
/**
* Your SMAdapter should override this method if it has this functionality
* @param accountNo
+ * @param cvk
+ * @param cavv
+ * @param upn
+ * @param authrc
+ * @param sfarc
+ * @return Cardholder Authentication Verification Value
+ * @throws SMException
+ */
+ protected boolean verifyCAVVImpl(String accountNo, SecureDESKey cvk, String cavv,
+ String upn, String authrc, String sfarc) throws SMException {
+ throw new SMException("Operation not supported in: " + this.getClass().getName());
+ }
+
+ /**
+ * Your SMAdapter should override this method if it has this functionality
+ * @param accountNo
* @param imkac
* @param dcvv
* @param expDate
@@ -739,6 +739,34 @@ public String calculateCVV(String accountNo, SecureDESKey cvkA, SecureDESKey cvk
Date expDate, String serviceCode) throws SMException;
+ /**
+ * Calaculate a 3-D Secure CAVV/AAV.
+ *
+ * <ul>
+ * <li>Visa uses CAVV (Cardholder Authentication Verification Value)</li>
+ * <li>MasterCard uses AAV (Accountholder Authentication Value)</li>
+ * </ul>
+ * <p>NOTE: Algorithm used to calculation CAVV/AAV is same as for CVV/CVC
+ * calculation. Only has been changed meaning of parameters
+ * {@code expDate} and {@code serviceCode}.
+ *
+ * @param accountNo the account number including BIN and the check digit.
+ * @param cvk the key used to CVV/CVC generation
+ * @param upn the unpredictable number. Calculated value based
+ * on Transaction Identifier (xid) from PAReq.
+ * A 4 decimal digits value must be supplied.
+ * @param authrc the Authentication Results Code. A value based on
+ * the Transaction Status (status) that will be used in
+ * PARes. A 1 decimal digit value must be supplied.
+ * @param sfarc the Second Factor Authentication Results Code.
+ * A value based on the result of second factor authentication.
+ * A 2 decimal digits value must be suppiled.
+ * @return Cardholder Authentication Verification Value/Accountholder
+ * Authentication Value
+ * @throws SMException
+ */
+ public String calculateCAVV(String accountNo, SecureDESKey cvk, String upn,
+ String authrc, String sfarc) throws SMException;
/**
* Verify a Card Verification Code/Value
@@ -758,13 +786,43 @@ public String calculateCVV(String accountNo, SecureDESKey cvkA, SecureDESKey cvk
* <li>"000" for verifing CVV2/CVC2 printed on card's signature stripe</li>
* <li>"999" for verifing iCVV/Chip CVC included on EMV chip card</li>
* </ul>
- * @return true if CVV/CVC is falid or false if not
+ * @return true if CVV/CVC is valid or false if not
* @throws SMException
*/
public boolean verifyCVV(String accountNo, SecureDESKey cvkA, SecureDESKey cvkB,
String cvv, Date expDate, String serviceCode) throws SMException;
+ /**
+ * Verify a 3-D Secure CAVV/AAV.
+ *
+ * <ul>
+ * <li>Visa uses CAVV (Cardholder Authentication Verification Value)</li>
+ * <li>MasterCard uses AAV (Accountholder Authentication Value)</li>
+ * </ul>
+ * <p>NOTE: Algorithm used to verification CAVV/AAV is same as for CVV/CVC
+ * verification. Only has been changed meaning of parameters
+ * {@code expDate} and {@code serviceCode}.
+ *
+ * @param accountNo the account number including BIN and the check digit.
+ * @param cvk the key used to CVV/CVC generation
+ * @param cavv the Cardholder Authentication Verification Value
+ * or Accountholder Authentication Value.
+ * @param upn the unpredictable number. Calculated value based
+ * on Transaction Identifier (xid) from PAReq.
+ * A 4 decimal digits value must be supplied.
+ * @param authrc the Authentication Results Code. A value based on
+ * the Transaction Status (status) that will be used in
+ * PARes. A 1 decimal digit value must be supplied.
+ * @param sfarc the Second Factor Authentication Results Code.
+ * A value based on the result of second factor authentication.
+ * A 2 decimal digits value must be suppiled.
+ * @return true if CAVV/AAV is valid or false if not
+ * @throws SMException
+ */
+ public boolean verifyCAVV(String accountNo, SecureDESKey cvk, String cavv,
+ String upn, String authrc, String sfarc) throws SMException;
+
/**
* Verify a Dynamic Card Verification Value (CVV)
@@ -364,12 +364,18 @@ private Key concatKeys(SecureDESKey keyA, SecureDESKey keyB)
private String calculateCVV(String accountNo, Key cvk, Date expDate,
String serviceCode) throws SMException {
+ String ed = ISODate.formatDate(expDate, "yyMM");
+ return calculateCVD(accountNo, cvk, ed, serviceCode);
+ }
+
+ private String calculateCVD(String accountNo, Key cvk, String expDate,
+ String serviceCode) throws SMException {
Key udka = jceHandler.formDESKey(SMAdapter.LENGTH_DES
,Arrays.copyOfRange(cvk.getEncoded(), 0, 8));
byte[] block = ISOUtil.hex2byte(
ISOUtil.zeropadRight(accountNo
- + ISODate.formatDate(expDate, "yyMM")
+ + expDate
+ serviceCode, 32));
byte[] ba = Arrays.copyOfRange(block, 0, 8);
byte[] bb = Arrays.copyOfRange(block, 8,16);
@@ -388,6 +394,33 @@ protected String calculateCVVImpl(String accountNo, SecureDESKey cvkA, SecureDES
return calculateCVV(accountNo,concatKeys(cvkA, cvkB),expDate,serviceCode);
}
+ protected void checkCAVVArgs(String upn, String authrc, String sfarc)
+ throws SMException {
+ if (upn == null)
+ throw new SMException("Unpredictable Number can not be null");
+ if (authrc == null)
+ throw new SMException("Authorization Result Code can not be null");
+ if (sfarc == null)
+ throw new SMException("Secend Factor Authorization Result Code"
+ + " can not be null");
+ if (upn.length() != 4 )
+ throw new SMException("Length of Unpredictable Number"
+ + " must be 4 but got "+upn.length());
+ if (authrc.length() != 1 )
+ throw new SMException("Length of Authorization Result Code"
+ + " must be 1 but got "+authrc.length());
+ if (sfarc.length() != 2 )
+ throw new SMException("Length of Secend Factor Authorization Result"
+ + " Code must be 2 but got "+sfarc.length());
+ }
+
+ @Override
+ protected String calculateCAVVImpl(String accountNo, SecureDESKey cvk, String upn,
+ String authrc, String sfarc) throws SMException {
+ checkCAVVArgs(upn, authrc,sfarc);
+ return calculateCVD(accountNo,concatKeys(cvk, null),upn,authrc+sfarc);
+ }
+
@Override
protected boolean verifyCVVImpl(String accountNo, SecureDESKey cvkA, SecureDESKey cvkB,
String cvv, Date expDate, String serviceCode) throws SMException {
@@ -396,6 +429,14 @@ protected boolean verifyCVVImpl(String accountNo, SecureDESKey cvkA, SecureDESKe
}
@Override
+ protected boolean verifyCAVVImpl(String accountNo, SecureDESKey cvk, String cavv,
+ String upn, String authrc, String sfarc) throws SMException {
+ checkCAVVArgs(upn, authrc,sfarc);
+ String result = calculateCVD(accountNo, concatKeys(cvk, null), upn, authrc+sfarc);
+ return result.equals(cavv);
+ }
+
+ @Override
protected boolean verifydCVVImpl(String accountNo, SecureDESKey imkac, String dcvv,
Date expDate, String serviceCode, byte[] atc, MKDMethod mkdm)
throws SMException {
@@ -614,6 +614,63 @@ public void testVerifyCVVImpl2() throws Throwable {
assertTrue(result);
}
+
+ @Test
+ public void testCalculateCAVVImpl1() throws Throwable {
+ String accountNo = "123456789012";
+ String upn = "1108";
+ String authrc = "0";
+ String sfarc = "00";
+ String expected = "204";
+ String cavv = jcesecmod.calculateCAVV(accountNo, cvk, upn, authrc, sfarc);
+ assertEquals(expected, cavv);
+ }
+
+ @Test
+ public void testVerifyCAVVImpl1() throws Throwable {
+ String accountNo = "123456789012";
+ String upn = "1108";
+ String authrc = "0";
+ String sfarc = "00";
+ String cavv = "204";
+ boolean result = jcesecmod.verifyCAVV(accountNo, cvk, cavv, upn, authrc, sfarc);
+ assertTrue(result);
+ }
+
+ @Test
+ public void testCalculateCAVVImpl2() throws Throwable {
+ String accountNo = "123456789012";
+ String upn = "1108";
+ String authrc = "1";
+ String sfarc = "00";
+ String expected = "439";
+ String cavv = jcesecmod.calculateCAVV(accountNo, cvk, upn, authrc, sfarc);
+ assertEquals(expected, cavv);
+ }
+
+ @Test
+ public void testVerifyCAVVImpl2() throws Throwable {
+ String accountNo = "123456789012";
+ String upn = "1108";
+ String authrc = "1";
+ String sfarc = "00";
+ String cavv = "439";
+ boolean result = jcesecmod.verifyCAVV(accountNo, cvk, cavv, upn, authrc, sfarc);
+ assertTrue(result);
+ }
+
+ @Test( expected = SMException.class)
+ public void testCalculateCAVVImplSMException() throws Throwable {
+ String accountNo = "123456789012";
+ String cavv = jcesecmod.calculateCAVV(accountNo, cvk, null, null, null);
+ }
+
+ @Test( expected = SMException.class)
+ public void testVerifyCAVVImplSMException() throws Throwable {
+ String accountNo = "123456789012";
+ boolean result = jcesecmod.verifyCAVV(accountNo, cvk, null, null, null, null);
+ }
+
@Test
public void testCalculatePVVImpl1() throws Throwable {
SecureDESKey pvk = tpk; //pvk and zpk are same type

0 comments on commit c7cfdca

Please sign in to comment.