001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package  org.jpos.security.jceadapter;
020
021import org.javatuples.Pair;
022import org.jpos.core.Configuration;
023import org.jpos.core.ConfigurationException;
024import org.jpos.core.Environment;
025import org.jpos.iso.ISODate;
026import org.jpos.iso.ISOException;
027import org.jpos.iso.ISOUtil;
028import org.jpos.security.*;
029import org.jpos.util.LogEvent;
030import org.jpos.util.Logger;
031import org.jpos.util.SimpleMsg;
032
033import javax.crypto.SecretKey;
034import java.io.BufferedInputStream;
035import java.io.BufferedOutputStream;
036import java.io.File;
037import java.io.FileInputStream;
038import java.io.FileOutputStream;
039import java.io.InputStream;
040import java.io.OutputStream;
041import java.nio.ByteBuffer;
042import java.security.*;
043import java.util.*;
044import java.util.regex.Matcher;
045import java.util.regex.Pattern;
046import javax.crypto.Cipher;
047
048/**
049 * JCESecurityModule is an implementation of a security module in software.
050 * <p>
051 * It doesn't require any hardware device to work.<br>
052 * JCESecurityModule also implements the SMAdapter, so you can view it: either
053 * as a self contained security module adapter that doesn't need a security module
054 * or a security module that plugs directly to jpos, so doesn't need
055 * a separate adapter.<br>
056 * It relies on Java(tm) Cryptography Extension (JCE), hence its name.<br>
057 * JCESecurityModule relies on the JCEHandler class to do the low level JCE work.
058 * <p>
059 * WARNING: This version of JCESecurityModule is meant for testing purposes and
060 * NOT for life operation, since the Local Master Keys are stored in CLEAR on
061 * the system's disk. Comming versions of JCESecurity Module will rely on
062 * java.security.KeyStore for a better protection of the Local Master Keys.
063 *
064 * @author Hani Samuel Kirollos
065 * @author Robert Demski
066 * @version $Revision$ $Date$
067 */
068@SuppressWarnings("unchecked")
069public class JCESecurityModule extends BaseSMAdapter<SecureDESKey> {
070    /**
071     * Pattern representing key type string value.
072     */
073    private static final Pattern KEY_TYPE_PATTERN = Pattern.compile("([^:;]*)([:;])?([^:;])?([^:;])?");
074
075    /**
076     * Pattern for split two clear pins.
077     */
078    private static final Pattern SPLIT_PIN_PATTERN = Pattern.compile("[ :;,/.]");
079
080    /**
081     * NUmber of LMK pairs
082     */
083    private final static int LMK_PAIRS_NO = 0xe;
084
085    /**
086     * LMK variants appled to first byte of LMK pair
087     */
088    private final static int[] variants = {
089        0x00, 0xa6, 0x5a, 0x6a, 0xde, 0x2b, 0x50, 0x74, 0x9c,0xfa
090    };
091
092    /**
093     * LMK scheme variants appiled to first byte of second key in pair
094     */
095    private final static int[] schemeVariants = {
096        0x00, 0xa6, 0x5a, 0x6a, 0xde, 0x2b
097    };
098
099    /**
100     * Index of scheme variant for left LMK key for double length keys
101     */
102    private final static int KEY_U_LEFT      = 1;
103    /**
104     * Index of scheme variant for right LMK key for double length keys
105     */
106    private final static int KEY_U_RIGHT     = 2;
107    /**
108     * Index of scheme variant for left LMK key for triple length keys
109     */
110    private final static int KEY_T_LEFT      = 3;
111    /**
112     * Index of scheme variant for middle LMK key for triple length keys
113     */
114    private final static int KEY_T_MEDIUM    = 4;
115    /**
116     * Index of scheme variant for right LMK key for triple length keys
117     */
118    private final static int KEY_T_RIGHT     = 5;
119
120    private static MessageDigest SHA1_MESSAGE_DIGEST = null;
121
122    private static final byte[] _VARIANT_RIGHT_HALF = new byte[]
123            {
124                    (byte) 0xC0, (byte) 0xC0, (byte) 0xC0, (byte) 0xC0,
125                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
126            };
127
128    static {
129      try {
130          SHA1_MESSAGE_DIGEST = MessageDigest.getInstance("SHA-1");
131      } catch (NoSuchAlgorithmException ex) {} //NOPMD: SHA-1 is a standard digest
132    }
133
134    /**
135     * Creates an uninitialized JCE Security Module, you need to setConfiguration to initialize it
136     */
137    public JCESecurityModule () {
138        super();
139    }
140
141    /**
142     * @param lmkFile Local Master Keys filename of the JCE Security Module
143     * @throws SMException
144     */
145    public JCESecurityModule (String lmkFile) throws SMException
146    {
147        Objects.requireNonNull(lmkFile);
148        init(null, lmkFile, false);
149    }
150
151    public JCESecurityModule (String lmkFile, String jceProviderClassName) throws SMException
152    {
153        Objects.requireNonNull(lmkFile);
154        init(jceProviderClassName, lmkFile, false);
155    }
156
157    public JCESecurityModule (Configuration cfg, Logger logger, String realm) throws ConfigurationException
158    {
159        setLogger(logger, realm);
160        setConfiguration(cfg);
161    }
162
163    /**
164     * Configures a JCESecurityModule
165     * @param cfg The following properties are read:<br>
166     *    lmk: Local Master Keys file (The only required parameter)<br>
167     *    jce: JCE Provider Class Name, if not provided, it defaults to: com.sun.crypto.provider.SunJCE<br>
168     *    rebuildlmk: (true/false), rebuilds the Local Master Keys file with new keys (WARNING: old keys will be erased)<br>
169     *    cbc-mac: Cipher Block Chaining MAC algorithm name for given JCE Provider.<br>
170     *             Default is ISO9797ALG3MACWITHISO7816-4PADDING from BouncyCastle provider (known as Retail-MAC)<br>
171     *             that is suitable for most of interfaces with double length MAC key<br>
172     *             ANSI X9.19 aka ISO/IEC 9797-1 MAC algorithm 3 padding method 2 - ISO7816<br>
173     *    ede-mac: Encrypt Decrypt Encrypt MAC algorithm name for given JCE Provider.<br>
174     *             Default is DESEDEMAC from BouncyCastle provider<br>
175     *             that is suitable for BASE24 with double length MAC key<br>
176     *             ANSI X9.19<br>
177     * @throws ConfigurationException
178     */
179    @Override
180    public void setConfiguration (Configuration cfg) throws ConfigurationException {
181        super.setConfiguration(cfg);
182        try {
183            init(cfg.get("provider"), cfg.get("lmk", null), cfg.getBoolean("rebuildlmk"));
184        } catch (SMException e) {
185            throw  new ConfigurationException(e);
186        }
187    }
188
189    @Override
190    public SecureDESKey generateKeyImpl (short keyLength, String keyType) throws SMException {
191        Key generatedClearKey = jceHandler.generateDESKey(keyLength);
192        return encryptToLMK(keyLength, keyType, generatedClearKey);
193    }
194
195    @Override
196    public SecureDESKey importKeyImpl (short keyLength, String keyType, byte[] encryptedKey,
197            SecureDESKey kek, boolean checkParity) throws SMException {
198        // decrypt encrypted key
199        Key clearKEY = jceHandler.decryptDESKey(keyLength, encryptedKey, decryptFromLMK(kek),
200                checkParity);
201        // Encrypt Key under LMK
202        return encryptToLMK(keyLength, keyType, clearKEY);
203    }
204
205    @Override
206    public byte[] exportKeyImpl (SecureDESKey key, SecureDESKey kek) throws SMException {
207        // get key in clear
208        Key clearKey = decryptFromLMK(key);
209        // Encrypt key under kek
210        return jceHandler.encryptDESKey(key.getKeyLength(), clearKey, decryptFromLMK(kek));
211    }
212
213    private int getKeyTypeIndex (short keyLength, String keyType) throws SMException {
214        int index;
215        if (keyType==null)
216            return 0;
217        String majorType = getMajorType(keyType);
218        if (!keyTypeToLMKIndex.containsKey(majorType))
219            throw new SMException("Unsupported key type: " + majorType);
220        index = keyTypeToLMKIndex.get(majorType);
221        index |= getVariant(keyType) << 8;
222        return index;
223    }
224
225    private static String getMajorType (String keyType) {
226        Matcher m = KEY_TYPE_PATTERN.matcher(keyType);
227        m.find();
228        if (m.group(1) != null)
229            return m.group(1);
230        throw new IllegalArgumentException("Missing key type");
231    }
232
233    private static int getVariant (String keyType) {
234        int variant = 0;
235        Matcher m = KEY_TYPE_PATTERN.matcher(keyType);
236        m.find();
237        if (m.group(3) != null)
238            try {
239                variant = Integer.valueOf(m.group(3));
240            } catch (NumberFormatException ex){
241                throw new NumberFormatException("Value "+m.group(4)+" is not valid key variant");
242            }
243        return variant;
244    }
245
246    private static KeyScheme getScheme (int keyLength, String keyType) {
247        KeyScheme scheme = KeyScheme.Z;
248        switch (keyLength){
249            case SMAdapter.LENGTH_DES:
250                scheme = KeyScheme.Z; break;
251            case SMAdapter.LENGTH_DES3_2KEY:
252                scheme = KeyScheme.X; break;
253            case SMAdapter.LENGTH_DES3_3KEY:
254                scheme = KeyScheme.Y; break;
255        }
256        if (keyType==null)
257            return scheme;
258        Matcher m = KEY_TYPE_PATTERN.matcher(keyType);
259        m.find();
260        if (m.group(4) != null)
261            try {
262                scheme = KeyScheme.valueOf(m.group(4));
263            } catch (IllegalArgumentException ex){
264                throw new IllegalArgumentException("Value "+m.group(4)+" is not valid key scheme");
265            }
266        return scheme;
267    }
268
269    @Override
270    public EncryptedPIN encryptPINImpl (String pin, String accountNumber) throws SMException {
271        byte[] clearPINBlock = calculatePINBlock(pin, FORMAT00, accountNumber);
272        // Encrypt
273        byte[] translatedPINBlock = jceHandler.encryptData(clearPINBlock, getLMK(PINLMKIndex));
274        return new EncryptedPIN(translatedPINBlock, FORMAT00, accountNumber, false);
275    }
276
277    @Override
278    protected EncryptedPIN encryptPINImpl(String pin, String accountNumber, SecureDESKey pek) throws SMException {
279        byte[] clearPINBlock = calculatePINBlock(pin, FORMAT00, accountNumber);
280        Key clearPEK = decryptFromLMK(pek);
281        byte[] translatedPINBlock = jceHandler.encryptData(clearPINBlock, clearPEK);
282        return new EncryptedPIN(translatedPINBlock, FORMAT00, accountNumber, false);
283    }
284
285    @Override
286    public String decryptPINImpl (EncryptedPIN pinUnderLmk) throws SMException {
287        byte[] clearPINBlock = jceHandler.decryptData(pinUnderLmk.getPINBlock(),
288                getLMK(PINLMKIndex));
289        return calculatePIN(clearPINBlock, pinUnderLmk.getPINBlockFormat(), pinUnderLmk.getAccountNumber());
290    }
291
292    @Override
293    public EncryptedPIN importPINImpl (EncryptedPIN pinUnderKd1, SecureDESKey kd1) throws SMException {
294        // read inputs
295        String accountNumber = pinUnderKd1.getAccountNumber();
296        // Use FORMAT00 for encrypting PIN under LMK
297        byte destinationPINBlockFormat = FORMAT00;
298        // get clear PIN
299        byte[] clearPINBlock = jceHandler.decryptData(pinUnderKd1.getPINBlock(),
300                decryptFromLMK(kd1));
301        // extract clear pin (as entered by card holder)
302        String pin = calculatePIN(clearPINBlock, pinUnderKd1.getPINBlockFormat(),
303                accountNumber);
304        // Format PIN Block using proprietary FORMAT00 to be encrypetd under LMK
305        clearPINBlock = calculatePINBlock(pin, destinationPINBlockFormat, accountNumber);
306        // encrypt PIN
307        byte[] translatedPINBlock = jceHandler.encryptData(clearPINBlock, getLMK(PINLMKIndex));
308        return new EncryptedPIN(translatedPINBlock, destinationPINBlockFormat,
309                accountNumber, false);
310    }
311
312    @Override
313    public EncryptedPIN exportPINImpl (EncryptedPIN pinUnderLmk, SecureDESKey kd2,
314            byte destinationPINBlockFormat) throws SMException {
315        String accountNumber = pinUnderLmk.getAccountNumber();
316        // process
317        // get clear PIN
318        byte[] clearPINBlock = jceHandler.decryptData(pinUnderLmk.getPINBlock(),
319                getLMK(PINLMKIndex));
320        // extract clear pin
321        String pin = calculatePIN(clearPINBlock, pinUnderLmk.getPINBlockFormat(),
322                accountNumber);
323        clearPINBlock = calculatePINBlock(pin, destinationPINBlockFormat, accountNumber);
324        // encrypt PIN
325        byte[] translatedPINBlock = jceHandler.encryptData(clearPINBlock, decryptFromLMK(kd2));
326        return new EncryptedPIN(translatedPINBlock, destinationPINBlockFormat,
327                accountNumber, false);
328    }
329
330    @Override
331    public EncryptedPIN generatePINImpl(String accountNumber, int pinLen, List<String> excludes)
332            throws SMException {
333        if (excludes==null)
334          excludes = Arrays.asList();
335        String pin;
336        {
337          Random rd = new SecureRandom();
338          int max = (int)Math.pow(10, Math.min(pinLen, 9));
339          int max2 = (int)Math.pow(10, Math.max(pinLen - 9,0));
340          do {
341            long pinl = rd.nextInt(max);
342            if (pinLen > 9){
343              pinl *= max2;
344              pinl += rd.nextInt(max2);
345            }
346            pin = ISOUtil.zeropad(pinl, pinLen);
347          } while (excludes.contains(pin));
348        }
349
350        return encryptPINImpl(pin, accountNumber);
351    }
352
353    /**
354     * Visa way to decimalize data block
355     * @param b
356     * @return decimalized data string
357     */
358    private static String decimalizeVisa(byte[] b){
359        char[] bec = ISOUtil.hexString(b).toUpperCase().toCharArray();
360        char[] bhc = new char[bec.length];
361        int k = 0;
362        //Select 0-9 chars
363        for ( char c : bec )
364          if (c<'A')
365            bhc[k++] = c;
366        //Select A-F chars and map them to 0-5
367        char adjust = 'A'-'0';
368        for ( char c : bec )
369          if (c>='A')
370            bhc[k++] = (char) (c-adjust);
371        return new String(bhc);
372    }
373
374    protected Key concatKeys(SecureDESKey keyA, SecureDESKey keyB)
375            throws SMException {
376        if ( keyA!=null && keyA.getKeyLength()==SMAdapter.LENGTH_DES
377          && keyB!=null && keyB.getKeyLength()==SMAdapter.LENGTH_DES) {
378          Key cvkAclear = decryptFromLMK(keyA);
379          Key cvkBclear = decryptFromLMK(keyB);
380          return jceHandler.formDESKey(SMAdapter.LENGTH_DES3_2KEY
381                  ,ISOUtil.concat(cvkAclear.getEncoded(), cvkBclear.getEncoded()));
382        }
383        if (keyA!=null && keyA.getKeyLength()!=SMAdapter.LENGTH_DES)
384          return decryptFromLMK(keyA);
385        if (keyB!=null && keyB.getKeyLength()!=SMAdapter.LENGTH_DES)
386          return decryptFromLMK(keyB);
387        return null;
388    }
389
390    protected String calculateCVV(String accountNo, Key cvk, Date expDate,
391                                String serviceCode) throws SMException {
392        String ed = ISODate.formatDate(expDate, "yyMM");
393        return calculateCVD(accountNo, cvk, ed, serviceCode);
394    }
395
396    protected String calculateCVD(String accountNo, Key cvk, String expDate,
397                                String serviceCode) throws SMException {
398        Key udka = jceHandler.formDESKey(SMAdapter.LENGTH_DES
399                ,Arrays.copyOfRange(cvk.getEncoded(), 0, 8));
400
401        byte[] block = ISOUtil.hex2byte(
402                ISOUtil.zeropadRight(accountNo
403                    + expDate
404                    + serviceCode, 32));
405        byte[] ba = Arrays.copyOfRange(block, 0, 8);
406        byte[] bb = Arrays.copyOfRange(block, 8,16);
407
408        //Encrypt ba with udka
409        byte[] bc = jceHandler.encryptData(ba, udka);
410        byte[] bd = ISOUtil.xor(bc, bb);
411        //Encrypt bd Tripple DES
412        byte[] be = jceHandler.encryptData(bd, cvk);
413        return decimalizeVisa(be).substring(0,3);
414    }
415
416    @Override
417    protected String calculateCVVImpl(String accountNo, SecureDESKey cvkA, SecureDESKey cvkB,
418                                   Date expDate, String serviceCode) throws SMException {
419        return calculateCVV(accountNo,concatKeys(cvkA, cvkB),expDate,serviceCode);
420    }
421
422    @Override
423    protected String calculateCVDImpl(String accountNo, SecureDESKey cvkA, SecureDESKey cvkB,
424                                   String expDate, String serviceCode) throws SMException {
425        return calculateCVD(accountNo, concatKeys(cvkA, cvkB), expDate, serviceCode);
426    }
427
428    protected void checkCAVVArgs(String upn, String authrc, String sfarc)
429            throws SMException {
430        if (upn == null)
431            throw new SMException("Unpredictable Number can not be null");
432        if (authrc == null)
433            throw new SMException("Authorization Result Code can not be null");
434        if (sfarc == null)
435            throw new SMException("Secend Factor Authorization Result Code"
436                    + " can not be null");
437        if (upn.length() != 4 )
438            throw new SMException("Length of Unpredictable Number"
439                  + " must be 4 but got "+upn.length());
440        if (authrc.length() != 1 )
441            throw new SMException("Length of Authorization Result Code"
442                  + " must be 1 but got "+authrc.length());
443        if (sfarc.length() != 2 )
444            throw new SMException("Length of Secend Factor Authorization Result"
445                  + " Code must be 2 but got "+sfarc.length());
446    }
447
448    @Override
449    protected String calculateCAVVImpl(String accountNo, SecureDESKey cvk, String upn,
450                                       String authrc, String sfarc) throws SMException {
451        checkCAVVArgs(upn, authrc,sfarc);
452        return calculateCVD(accountNo,concatKeys(cvk, null),upn,authrc+sfarc);
453    }
454
455    @Override
456    protected boolean verifyCVVImpl(String accountNo, SecureDESKey cvkA, SecureDESKey cvkB,
457                     String cvv, Date expDate, String serviceCode) throws SMException {
458        String result = calculateCVV(accountNo, concatKeys(cvkA, cvkB), expDate, serviceCode);
459        return result.equals(cvv);
460    }
461
462    @Override
463    protected boolean verifyCVVImpl(String accountNo, SecureDESKey cvkA, SecureDESKey cvkB,
464                     String cvv, String expDate, String serviceCode) throws SMException {
465        String result = calculateCVD(accountNo, concatKeys(cvkA, cvkB), expDate, serviceCode);
466        return result.equals(cvv);
467    }
468
469    @Override
470    protected boolean verifyCAVVImpl(String accountNo, SecureDESKey cvk, String cavv,
471                     String upn, String authrc, String sfarc) throws SMException {
472        checkCAVVArgs(upn, authrc,sfarc);
473        String result = calculateCVD(accountNo, concatKeys(cvk, null), upn, authrc+sfarc);
474        return result.equals(cavv);
475    }
476
477    protected String calculatedCVV(String accountNo, SecureDESKey imkac,
478                     String expDate, String serviceCode, byte[] atc, MKDMethod mkdm)
479                     throws SMException {
480
481        if (mkdm==null)
482          mkdm = MKDMethod.OPTION_A;
483
484        byte[] panpsn = formatPANPSN_dCVD(accountNo, null, mkdm);
485        Key mkac = deriveICCMasterKey(decryptFromLMK(imkac), panpsn);
486
487        String alteredPAN = ISOUtil.hexString(atc) + accountNo.substring(4);
488
489        return calculateCVD(alteredPAN, mkac, expDate, serviceCode);
490    }
491
492    @Override
493    protected boolean verifydCVVImpl(String accountNo, SecureDESKey imkac, String dcvv,
494                     Date expDate, String serviceCode, byte[] atc, MKDMethod mkdm)
495                     throws SMException {
496
497        String ed = ISODate.formatDate(expDate, "yyMM");
498        String res = calculatedCVV(accountNo, imkac, ed,
499                serviceCode, atc, mkdm
500        );
501        return res.equals(dcvv);
502    }
503
504    @Override
505    protected boolean verifydCVVImpl(String accountNo, SecureDESKey imkac, String dcvv,
506                     String expDate, String serviceCode, byte[] atc, MKDMethod mkdm)
507                     throws SMException {
508
509        String res = calculatedCVV(accountNo, imkac, expDate,
510                serviceCode, atc, mkdm
511        );
512        return res.equals(dcvv);
513    }
514
515    protected String calculateCVC3(SecureDESKey imkcvc3, String accountNo, String acctSeqNo,
516                     byte[] atc, byte[] upn, byte[] data, MKDMethod mkdm)
517                     throws SMException {
518        if (mkdm==null)
519          mkdm = MKDMethod.OPTION_A;
520        byte[] panpsn = formatPANPSN_dCVD(accountNo, acctSeqNo, mkdm);
521        Key mkcvc3 = deriveICCMasterKey(decryptFromLMK(imkcvc3), panpsn);
522        byte[] ivcvc3 = data;
523        if (ivcvc3.length != 2)
524          //Compute IVCVC3
525          ivcvc3 = calculateIVCVC3(mkcvc3, data);
526        //Concatenate IVCVC3, UPN and ATC
527        byte[] b = ISOUtil.concat(ivcvc3, upn);
528        b = ISOUtil.concat(b, atc);
529        //Encrypt prepared blok
530        b = jceHandler.encryptData(b, mkcvc3);
531        //Format last two bytes to integer
532        int cvc3l = (b[6] & 0xff) << 8;
533        cvc3l |= b[7] & 0xff;
534        //Convert to string representation
535        return String.format("%05d",cvc3l);
536    }
537
538    @Override
539    protected boolean verifyCVC3Impl(SecureDESKey imkcvc3, String accountNo, String acctSeqNo,
540                     byte[] atc, byte[] upn, byte[] data, MKDMethod mkdm, String cvc3)
541                     throws SMException {
542
543        String calcCVC3 = calculateCVC3(imkcvc3, accountNo, acctSeqNo,
544                atc, upn, data, mkdm
545        );
546        cvc3 = cvc3==null?"":cvc3;
547        //get some last digits
548        calcCVC3 = calcCVC3.substring(5-cvc3.length());
549        return calcCVC3.equals(cvc3);
550    }
551
552    private byte[] calculateIVCVC3(Key mkcvc3, byte[] data)
553            throws JCEHandlerException {
554        byte[] paddedData = paddingISO9797Method2(data);
555        byte[] mac = calculateMACISO9797Alg3(mkcvc3, paddedData);
556        return Arrays.copyOfRange(mac,6,8);
557    }
558
559    /**
560     * ISO/IEC 9797-1 padding method 2
561     * @param d da to be padded
562     * @return padded data
563     */
564    private byte[] paddingISO9797Method2(byte[] d) {
565        //Padding - first byte 0x80 rest 0x00
566        byte[] t = new byte[d.length - d.length%8 + 8];
567        System.arraycopy(d, 0, t, 0, d.length);
568        for (int i=d.length;i<t.length;i++)
569          t[i] = (byte)(i==d.length?0x80:0x00);
570        d = t;
571        return d;
572    }
573
574    /**
575     * Calculate MAC according to ISO/IEC 9797-1 Alg 3
576     * @param key DES double length key
577     * @param d data to calculate MAC on it
578     * @return 8 byte of mac value
579     * @throws JCEHandlerException
580     */
581    private byte[] calculateMACISO9797Alg3(Key key, byte[] d) throws JCEHandlerException {
582        Key kl = jceHandler.formDESKey(SMAdapter.LENGTH_DES
583                            ,Arrays.copyOfRange(key.getEncoded(), 0, 8));
584        Key kr = jceHandler.formDESKey(SMAdapter.LENGTH_DES
585                            ,Arrays.copyOfRange(key.getEncoded(), 8, 16));
586        if (d.length%8 != 0) {
587            //Padding with 0x00 bytes
588            byte[] t = new byte[d.length - d.length%8 + 8];
589            System.arraycopy(d, 0, t, 0, d.length);
590            d = t;
591        }
592        //MAC_CBC alg 3
593        byte[] y_i = ISOUtil.hex2byte("0000000000000000");
594        byte[] yi  = new byte[8];
595        for ( int i=0;i<d.length;i+=8){
596            System.arraycopy(d, i, yi, 0, yi.length);
597            y_i = jceHandler.encryptData(ISOUtil.xor(yi, y_i), kl);
598        }
599        y_i = jceHandler.decryptData(y_i, kr);
600        y_i = jceHandler.encryptData(y_i, kl);
601        return y_i;
602    }
603
604    /**
605     * Prepare 8-bytes data from PAN and PAN Sequence Number.
606     * <p>
607     * Used at EMV operations: {@link #verifyARQCImpl ARQC verification},
608     * {@link #generateARPCImpl ARPC generation},
609     * {@link #generateSM_MACImpl secure message MAC generation}
610     * and {@link #translatePINGenerateSM_MACImpl secure message PIN translation}
611     */
612    private static byte[] formatPANPSN(String pan, String psn, MKDMethod mkdm)
613            throws SMException {
614        switch (mkdm){
615            case OPTION_A:
616                return formatPANPSNOptionA(pan, psn);
617            case OPTION_B:
618                if ( pan.length() <= 16)
619                    //use OPTION_A
620                    return formatPANPSNOptionA(pan, psn);
621                return formatPANPSNOptionB(pan, psn);
622            default:
623                throw new SMException("Unsupported ICC Master Key derivation method");
624        }
625    }
626
627    /**
628     * Prepare 8-bytes data from PAN and PAN Sequence Number.
629     * <p>
630     * Used at dCVV verification {@link #verifydCVVImpl verifydCVV}
631     * and CVC3 {@link #verifyCVC3Impl verifyCVC3}
632     */
633    private static byte[] formatPANPSN_dCVD(String pan, String psn, MKDMethod mkdm)
634            throws SMException {
635        switch (mkdm){
636            case OPTION_A:
637                return formatPANPSNOptionA(pan, psn);
638            case OPTION_B:
639                return formatPANPSNOptionB(pan, psn);
640            default:
641                throw new SMException("Unsupported ICC Master Key derivation method");
642        }
643    }
644
645    /**
646     * Format bytes representing Application PAN and
647     * PAN Sequence Number in BCD format.
648     * <p>
649     * Concatenate from left to right decimal digits of PAN and
650     * PAN Sequence Number digits. If {@code psn} is not present, it is
651     * replaced by a "00" digits. If the result is less than 16 digits long,
652     * pad it to the left with hexadecimal zeros in order to obtain an
653     * 16-digit number. If the Application PAN has an odd number of decimal
654     * digits then concatenate a "0" padding digit to the left thereby
655     * ensuring that the result is an even number of digits.
656     *
657     * @param pan application primary account number
658     * @param psn PAN Sequence Number
659     * @return up to 11 bytes representing Application PAN
660     */
661    private static byte[] preparePANPSN(String pan, String psn){
662        if (psn == null || psn.isEmpty())
663            psn = "00";
664        String ret = pan + psn;
665        //convert digits to bytes and padd with "0"
666        //to left for ensure even number of digits
667        return ISOUtil.hex2byte(ret);
668    }
669
670    /**
671     * Prepare 8-bytes data from PAN and PAN Sequence Number (Option A)
672     * <ul>
673     * <li> Prepare Application PAN and PAN Sequence Number by {@see #preparePANPSN}
674     * <li> Select first 16 digits
675     * </ul>
676     * @param pan application primary account number
677     * @param psn PAN Sequence Number
678     * @return 8-bytes representing rightmost 16 digits
679     */
680    private static byte[] formatPANPSNOptionA(String pan, String psn){
681        if ( pan.length() < 14 )
682            try {
683                pan = ISOUtil.zeropad(pan, 14);
684            } catch( ISOException ex ) {} //NOPMD: ISOException condition is checked before.
685        byte[] b = preparePANPSN(pan, psn);
686        return Arrays.copyOfRange(b, b.length-8, b.length);
687    }
688
689    /**
690     * Prepare bytes data from PAN and PAN Sequence Number (Option B)
691     *
692     * <h5>Do the following:</h5>
693     * <ul>
694     * <li> Prepare Application PAN and PAN Sequence Number by {@see #preparePANPSN}
695     * <li> Hash the prepared result using the SHA-1 hashing algorithm
696     *      to obtain the 20-byte hash result
697     * <li> Decimalize result of hasing by {@see #decimalizeVisa }
698     * <li> Select first 16 decimal digits
699     * </ul>
700     * @param pan application primary account number
701     * @param psn PAN Sequence Number
702     * @return 8-bytes representing first 16 decimalised digits
703     */
704    private static byte[] formatPANPSNOptionB(String pan, String psn){
705        byte[] b = preparePANPSN(pan, psn);
706        //20-bytes sha-1 digest
707        byte[] r = SHA1_MESSAGE_DIGEST.digest(b);
708        //decimalized HEX string of digest
709        String rs = decimalizeVisa(r);
710        //return 16-bytes decimalizd digest
711        return ISOUtil.hex2byte(rs.substring(0,16));
712    }
713
714    /**
715     * Derive ICC Master Key from Issuer Master Key and preformated PAN/PANSeqNo
716     *
717     * Compute two 8-byte numbers:
718     * <li> left part is a result of Tripple-DES encription {@code panpsn}
719     * with {@code imk} as the key
720     * <li> right part is a result of Tripple-DES binary inverted
721     * {@code panpsn} with {@code imk} as the key
722     * <li> concatenate left and right parts
723     * <br>
724     * Described in EMV v4.2 Book 2, Annex A1.4.1 Master Key Derivation point 2
725     *
726     * @param imk 16-bytes Issuer Master Key
727     * @param panpsn preformated PAN and PAN Sequence Number
728     * @return derived 16-bytes ICC Master Key with adjusted DES parity
729     * @throws JCEHandlerException
730     */
731    protected Key deriveICCMasterKey(Key imk, byte[] panpsn)
732            throws JCEHandlerException {
733        byte[] l = Arrays.copyOfRange(panpsn, 0, 8);
734        //left part of derived key
735        l = jceHandler.encryptData(l, imk);
736        byte[] r = Arrays.copyOfRange(panpsn, 0, 8);
737        //inverse clear right part of key
738        r = ISOUtil.xor(r, JCESecurityModule.fPaddingBlock);
739        //right part of derived key
740        r = jceHandler.encryptData(r,imk);
741        //derived key
742        byte[] mk = ISOUtil.concat(l,r);
743        //fix DES parity of key
744        Util.adjustDESParity(mk);
745        //form JCE Tripple-DES Key
746        return jceHandler.formDESKey(SMAdapter.LENGTH_DES3_2KEY, mk);
747    }
748
749    protected String calculatePVV(EncryptedPIN pinUnderLmk, Key key, int keyIdx
750                               ,List<String> excludes) throws SMException {
751        String pin = decryptPINImpl(pinUnderLmk);
752        if (excludes!=null && excludes.contains(pin))
753          throw new WeakPINException("Given PIN is on excludes list");
754        String block = pinUnderLmk.getAccountNumber().substring(1);
755        block += Integer.toString(keyIdx%10);
756        block += pin.substring(0, 4);
757        byte[] b = ISOUtil.hex2byte(block);
758        b = jceHandler.encryptData(b, key);
759
760        return decimalizeVisa(b).substring(0,4);
761    }
762
763    @Override
764    protected String calculatePVVImpl(EncryptedPIN pinUnderLmk, SecureDESKey pvkA,
765                               SecureDESKey pvkB, int pvkIdx, List<String> excludes)
766            throws SMException {
767        Key key = concatKeys(pvkA, pvkB);
768        return calculatePVV(pinUnderLmk, key, pvkIdx, excludes);
769    }
770
771    @Override
772    protected String calculatePVVImpl(EncryptedPIN pinUnderKd1, SecureDESKey kd1,
773                       SecureDESKey pvkA, SecureDESKey pvkB, int pvkIdx,
774                       List<String> excludes) throws SMException {
775        Key key = concatKeys(pvkA, pvkB);
776        EncryptedPIN pinUnderLmk = importPINImpl(pinUnderKd1, kd1);
777        return calculatePVV(pinUnderLmk, key, pvkIdx, excludes);
778    }
779
780    @Override
781    public boolean verifyPVVImpl(EncryptedPIN pinUnderKd1, SecureDESKey kd1, SecureDESKey pvkA,
782                             SecureDESKey pvkB, int pvki, String pvv) throws SMException {
783        Key key = concatKeys(pvkA, pvkB);
784        EncryptedPIN pinUnderLmk = importPINImpl(pinUnderKd1, kd1);
785        String result = calculatePVV(pinUnderLmk, key, pvki, null);
786        return result.equals(pvv);
787    }
788
789    @Override
790    public EncryptedPIN translatePINImpl (EncryptedPIN pinUnderKd1, SecureDESKey kd1,
791            SecureDESKey kd2, byte destinationPINBlockFormat) throws SMException {
792        EncryptedPIN translatedPIN;
793        Key clearKd1 = decryptFromLMK(kd1);
794        Key clearKd2 = decryptFromLMK(kd2);
795        translatedPIN = translatePINExt(null, pinUnderKd1, clearKd1, clearKd2
796                        ,destinationPINBlockFormat, null, PaddingMethod.MCHIP);
797        return translatedPIN;
798    }
799
800    private EncryptedPIN translatePINExt (EncryptedPIN oldPinUnderKd1, EncryptedPIN pinUnderKd1, Key kd1,
801            Key kd2, byte destinationPINBlockFormat, Key udk, PaddingMethod padm) throws SMException {
802        String accountNumber = pinUnderKd1.getAccountNumber();
803        // get clear PIN
804        byte[] clearPINBlock = jceHandler.decryptData(pinUnderKd1.getPINBlock(), kd1);
805        String pin = calculatePIN(clearPINBlock, pinUnderKd1.getPINBlockFormat(),
806                accountNumber);
807        // Reformat PIN Block
808        byte[] translatedPINBlock;
809        if (isVSDCPinBlockFormat(destinationPINBlockFormat)) {
810          String udka = ISOUtil.hexString(Arrays.copyOfRange(udk.getEncoded(), 0, 8));
811          if (destinationPINBlockFormat == SMAdapter.FORMAT42 ) {
812              byte[] oldClearPINBlock = jceHandler.decryptData(oldPinUnderKd1.getPINBlock(), kd1);
813              String oldPIN = calculatePIN(oldClearPINBlock, oldPinUnderKd1.getPINBlockFormat(),
814                      accountNumber);
815              clearPINBlock = calculatePINBlock(pin+":"+oldPIN, destinationPINBlockFormat, udka);
816          } else
817              clearPINBlock = calculatePINBlock(pin, destinationPINBlockFormat, udka);
818
819          accountNumber = udka.substring(4);
820        } else {
821          clearPINBlock = calculatePINBlock(pin, destinationPINBlockFormat, accountNumber);
822        }
823
824        switch(padm){
825          case VSDC:
826              //Add VSDC pin block padding
827              clearPINBlock = ISOUtil.concat(new byte[]{0x08}, clearPINBlock);
828              clearPINBlock = paddingISO9797Method2(clearPINBlock);
829              break;
830          case CCD:
831              //Add CCD pin block padding
832              clearPINBlock = paddingISO9797Method2(clearPINBlock);
833              break;
834          default:
835        }
836
837        // encrypt PIN
838        if (padm == PaddingMethod.CCD)
839          translatedPINBlock = jceHandler.encryptDataCBC(clearPINBlock, kd2
840                  ,Arrays.copyOf(zeroBlock, zeroBlock.length));
841        else
842          translatedPINBlock = jceHandler.encryptData(clearPINBlock, kd2);
843        return new EncryptedPIN(translatedPINBlock
844                        ,destinationPINBlockFormat, accountNumber, false);
845    }
846
847    /**
848     * Derive the session key used for Application Cryptogram verification
849     * or for secure messaging, the diversification value is the ATC
850     * @param mkac unique ICC Master Key for Application Cryptogams or Secure Messaging
851     * @param atc ICC generated Application Transaction Counter as diversification value
852     * @return derived 16-bytes Session Key with adjusted DES parity
853     * @throws JCEHandlerException
854     */
855    private Key deriveSK_VISA(Key mkac, byte[] atc) throws JCEHandlerException {
856
857        byte[] skl = new byte[8];
858        System.arraycopy(atc, atc.length-2, skl, 6, 2);
859        skl = ISOUtil.xor(skl, Arrays.copyOfRange(mkac.getEncoded(),0 ,8));
860
861        byte[] skr = new byte[8];
862        System.arraycopy(atc, atc.length-2, skr, 6, 2);
863        skr = ISOUtil.xor(skr, ISOUtil.hex2byte("000000000000FFFF"));
864        skr = ISOUtil.xor(skr, Arrays.copyOfRange(mkac.getEncoded(),8 ,16));
865        Util.adjustDESParity(skl);
866        Util.adjustDESParity(skr);
867        return jceHandler.formDESKey(SMAdapter.LENGTH_DES3_2KEY, ISOUtil.concat(skl,skr));
868    }
869
870    /**
871     * Common Session Key Derivation Method for secure messaging.
872     * <p>
873     * The diversification value is the <em>RAND</em>, which is ARQC
874     * incremeted by 1 (with overflow) after each script command
875     * for that same ATC value.
876     * Described in EMV v4.2 Book 2, Annex A1.3.1 Common Session Key
877     * Derivation Option for secure messaging.
878     *
879     * @param mksm unique ICC Master Key for Secure Messaging
880     * @param rand Application Cryptogram as diversification value
881     * @return derived 16-bytes Session Key with adjusted DES parity
882     * @throws JCEHandlerException
883     */
884    private Key deriveCommonSK_SM(Key mksm, byte[] rand) throws JCEHandlerException {
885      byte[] rl = Arrays.copyOf(rand,8);
886      rl[2] = (byte)0xf0;
887      byte[] skl = jceHandler.encryptData(rl, mksm);
888      byte[] rr = Arrays.copyOf(rand,8);
889      rr[2] = (byte)0x0f;
890      byte[] skr = jceHandler.encryptData(rr, mksm);
891      Util.adjustDESParity(skl);
892      Util.adjustDESParity(skr);
893      return jceHandler.formDESKey(SMAdapter.LENGTH_DES3_2KEY, ISOUtil.concat(skl,skr));
894    }
895
896    /**
897     * Common Session Key Derivation Method for Application Cryptograms.
898     * <p>
899     * Described in EMV v4.2 Book 2, Annex A1.3.1 Common Session Key Derivation
900     * Option for Application Cryptograms.
901     *
902     * @param mkac unique ICC Master Key for Application Cryptogams.
903     * @param atc ICC generated Application Transaction Counter as diversification value.
904     * @return derived 16-bytes Session Key with adjusted DES parity.
905     * @throws JCEHandlerException
906     */
907    private Key deriveCommonSK_AC(Key mkac, byte[] atc) throws JCEHandlerException {
908
909        byte[] r = new byte[8];
910        System.arraycopy(atc, atc.length-2, r, 0, 2);
911
912        return deriveCommonSK_SM(mkac, r);
913    }
914
915    /**
916     * MasterCard Proprietary Session Key Derivation (SKD) method.
917     * <p>
918     * Described in M/Chip 4 version 1.1 Security & Key Management manual
919     * paragraph 7 ICC Session Key Derivation.
920     *
921     * @param mkac unique ICC Master Key for Application Cryptogams
922     * @param atc ICC generated Application Transaction Counter as diversification value
923     * @param upn terminal generated random as diversification value
924     * @return derived 16-bytes Session Key with adjusted DES parity
925     * @throws JCEHandlerException
926     */
927    private Key deriveSK_MK(Key mkac, byte[] atc, byte[] upn) throws JCEHandlerException {
928
929        byte[] r = new byte[8];
930        System.arraycopy(atc, atc.length-2, r, 0, 2);
931        System.arraycopy(upn, upn.length-4, r, 4, 4);
932
933        return deriveCommonSK_SM(mkac, r);
934    }
935
936    private void constraintMKDM(MKDMethod mkdm, SKDMethod skdm) throws SMException {
937        if (mkdm == MKDMethod.OPTION_B)
938            throw new SMException("Master Key Derivation Option B"
939                    + " is not used in practice with scheme "+skdm);
940    }
941
942    private void constraintARPCM(SKDMethod skdm, ARPCMethod arpcMethod) throws SMException {
943        if (arpcMethod == ARPCMethod.METHOD_2 )
944            throw new SMException("ARPC generation method 2"
945                    + " is not used in practice with scheme "+skdm);
946    }
947
948    /**
949     * Calculate ARQC.
950     * <p>
951     * Entry point e.g. for simulator systems
952     */
953    protected byte[] calculateARQC(MKDMethod mkdm, SKDMethod skdm
954            ,SecureDESKey imkac, String accountNo, String accntSeqNo, byte[] atc
955            ,byte[] upn, byte[] transData) throws SMException {
956        if (mkdm==null)
957            mkdm = MKDMethod.OPTION_A;
958
959        byte[] panpsn = formatPANPSN(accountNo, accntSeqNo, mkdm);
960        Key mkac = deriveICCMasterKey(decryptFromLMK(imkac), panpsn);
961        Key skac = mkac;
962        switch(skdm){
963            case VSDC:
964                constraintMKDM(mkdm, skdm);
965                break;
966            case MCHIP:
967                constraintMKDM(mkdm, skdm);
968                skac = deriveSK_MK(mkac,atc,upn);
969                break;
970            case EMV_CSKD:
971                skac = deriveCommonSK_AC(mkac, atc);
972                break;
973            default:
974                throw new SMException("Session Key Derivation "+skdm+" not supported");
975        }
976
977        return calculateMACISO9797Alg3(skac, transData);
978    }
979
980    @Override
981    protected boolean verifyARQCImpl(MKDMethod mkdm, SKDMethod skdm, SecureDESKey imkac
982            ,String accountNo, String accntSeqNo, byte[] arqc, byte[] atc
983            ,byte[] upn, byte[] transData) throws SMException {
984
985        byte[] res = calculateARQC(mkdm, skdm, imkac, accountNo, accntSeqNo
986                ,atc, upn, transData);
987        return Arrays.equals(arqc, res);
988    }
989
990    @Override
991    public byte[] generateARPCImpl(MKDMethod mkdm, SKDMethod skdm, SecureDESKey imkac
992            ,String accountNo, String accntSeqNo, byte[] arqc, byte[] atc, byte[] upn
993            ,ARPCMethod arpcMethod, byte[] arc, byte[] propAuthData)
994            throws SMException {
995        if (mkdm==null)
996            mkdm = MKDMethod.OPTION_A;
997
998        byte[] panpsn = formatPANPSN(accountNo, accntSeqNo, mkdm);
999        Key mkac = deriveICCMasterKey(decryptFromLMK(imkac), panpsn);
1000        Key skarpc = mkac;
1001        switch(skdm){
1002            case VSDC:
1003                constraintMKDM(mkdm, skdm);
1004                constraintARPCM(skdm, arpcMethod);
1005                break;
1006            case MCHIP:
1007                constraintMKDM(mkdm, skdm);
1008                constraintARPCM(skdm, arpcMethod);
1009                break;
1010            case EMV_CSKD:
1011                skarpc = deriveCommonSK_AC(mkac, atc);
1012                break;
1013            default:
1014                throw new SMException("Session Key Derivation "+skdm+" not supported");
1015        }
1016
1017        return calculateARPC(skarpc, arqc, arpcMethod, arc, propAuthData);
1018    }
1019
1020    @Override
1021    public byte[] verifyARQCGenerateARPCImpl(MKDMethod mkdm, SKDMethod skdm, SecureDESKey imkac
1022            ,String accountNo, String accntSeqNo, byte[] arqc, byte[] atc, byte[] upn 
1023            ,byte[] transData, ARPCMethod arpcMethod, byte[] arc, byte[] propAuthData)
1024            throws SMException {
1025        if (mkdm==null)
1026            mkdm = MKDMethod.OPTION_A;
1027
1028        byte[] panpsn = formatPANPSN(accountNo, accntSeqNo, mkdm);
1029        Key mkac = deriveICCMasterKey(decryptFromLMK(imkac), panpsn);
1030        Key skac = mkac;
1031        Key skarpc = mkac;
1032        switch(skdm){
1033            case VSDC:
1034                constraintMKDM(mkdm, skdm);
1035                constraintARPCM(skdm, arpcMethod);
1036                break;
1037            case MCHIP:
1038                constraintMKDM(mkdm, skdm);
1039                constraintARPCM(skdm, arpcMethod);
1040                skac = deriveSK_MK(mkac,atc,upn);
1041                break;
1042            case EMV_CSKD:
1043                skac = deriveSK_MK(mkac, atc, new byte[4]);
1044                skarpc = skac;
1045                break;
1046            default:
1047                throw new SMException("Session Key Derivation "+skdm+" not supported");
1048        }
1049
1050        if (!Arrays.equals(arqc, calculateMACISO9797Alg3(skac, transData)))
1051            return null;
1052
1053        return calculateARPC(skarpc, arqc, arpcMethod, arc, propAuthData);
1054    }
1055
1056    /**
1057     * Calculate ARPC.
1058     * <p>
1059     * Entry point e.g. for simulator systems
1060     */
1061    protected byte[] calculateARPC(Key skarpc, byte[] arqc, ARPCMethod arpcMethod
1062             ,byte[] arc, byte[] propAuthData) throws SMException {
1063        if (arpcMethod == null)
1064            arpcMethod = ARPCMethod.METHOD_1;
1065
1066        byte[] b = new byte[8];
1067        switch(arpcMethod) {
1068            case METHOD_1:
1069                System.arraycopy(arc, arc.length-2, b, 0, 2);
1070                b = ISOUtil.xor(arqc, b);
1071                return jceHandler.encryptData(b, skarpc);
1072            case METHOD_2:
1073                b = ISOUtil.concat(arqc, arc);
1074                if (propAuthData != null)
1075                    b = ISOUtil.concat(b, propAuthData);
1076                b = paddingISO9797Method2(b);
1077                b = calculateMACISO9797Alg3(skarpc, b);
1078                return Arrays.copyOf(b, 4);
1079            default:
1080                throw new SMException("ARPC Method "+arpcMethod+" not supported");
1081        }
1082    }
1083
1084    @Override
1085    protected byte[] generateSM_MACImpl(MKDMethod mkdm, SKDMethod skdm
1086            ,SecureDESKey imksmi, String accountNo, String accntSeqNo
1087            ,byte[] atc, byte[] arqc, byte[] data) throws SMException {
1088        if (mkdm==null)
1089          mkdm = MKDMethod.OPTION_A;
1090        byte[] panpsn = formatPANPSN(accountNo, accntSeqNo, mkdm);
1091        Key mksmi = deriveICCMasterKey(decryptFromLMK(imksmi), panpsn);
1092        Key smi;
1093        switch(skdm){
1094          case VSDC:
1095            smi = deriveSK_VISA(mksmi, atc);
1096            data = paddingISO9797Method2(data);
1097            break;
1098          case MCHIP:
1099          case EMV_CSKD:
1100            smi = deriveCommonSK_SM(mksmi,arqc);
1101            data = paddingISO9797Method2(data);
1102            break;
1103          default:
1104            throw new SMException("Session Key Derivation "+skdm+" not supported");
1105        }
1106        return calculateMACISO9797Alg3(smi, data);
1107    }
1108
1109    @Override
1110    protected Pair<EncryptedPIN,byte[]> translatePINGenerateSM_MACImpl(
1111            MKDMethod mkdm, SKDMethod skdm, PaddingMethod padm, SecureDESKey imksmi
1112           ,String accountNo, String accntSeqNo, byte[] atc, byte[] arqc
1113           ,byte[] data, EncryptedPIN currentPIN, EncryptedPIN newPIN
1114           ,SecureDESKey kd1, SecureDESKey imksmc, SecureDESKey imkac
1115           ,byte destinationPINBlockFormat) throws SMException {
1116        if (mkdm==null)
1117          mkdm = MKDMethod.OPTION_A;
1118        byte[] panpsn = formatPANPSN(accountNo, accntSeqNo, mkdm);
1119        Key mksmc = deriveICCMasterKey(decryptFromLMK(imksmc), panpsn);
1120        Key smc;
1121        PaddingMethod derivedPADM;
1122        switch(skdm){
1123          case VSDC:
1124            smc = deriveSK_VISA(mksmc, atc);
1125            derivedPADM = PaddingMethod.VSDC;
1126            break;
1127          case MCHIP:
1128            smc = deriveCommonSK_SM(mksmc,arqc);
1129            derivedPADM = PaddingMethod.MCHIP;
1130            break;
1131          case EMV_CSKD:
1132            smc = deriveCommonSK_SM(mksmc,arqc);
1133            derivedPADM = PaddingMethod.CCD;
1134            break;
1135          default:
1136            throw new SMException("Session Key Derivation "+skdm+" not supported");
1137        }
1138
1139        //Use derived Padding Method if not specified
1140        if ( padm == null )
1141          padm = derivedPADM;
1142
1143        Key udk = null;
1144        if (isVSDCPinBlockFormat(destinationPINBlockFormat))
1145          udk = deriveICCMasterKey(decryptFromLMK(imkac), panpsn);
1146
1147        EncryptedPIN pin =  translatePINExt(currentPIN, newPIN, decryptFromLMK(kd1)
1148                            ,smc, destinationPINBlockFormat, udk, padm);
1149        data = ISOUtil.concat(data, pin.getPINBlock());
1150        byte[] mac = generateSM_MACImpl(mkdm, skdm, imksmi, accountNo
1151                               ,accntSeqNo, atc, arqc, data);
1152        return new Pair(pin, mac);
1153    }
1154
1155    private boolean isVSDCPinBlockFormat(byte pinBlockFormat) {
1156        return pinBlockFormat==SMAdapter.FORMAT41 || pinBlockFormat==SMAdapter.FORMAT42;
1157    }
1158
1159    @Override
1160    public byte[] encryptDataImpl(CipherMode cipherMode, SecureDESKey kd
1161            ,byte[] data, byte[] iv) throws SMException {
1162        Key dek = decryptFromLMK(kd);
1163        return jceHandler.doCryptStuff(data, dek, Cipher.ENCRYPT_MODE, cipherMode, iv);
1164    }
1165
1166    @Override
1167    public byte[] decryptDataImpl(CipherMode cipherMode, SecureDESKey kd
1168            ,byte[] data, byte[] iv) throws SMException {
1169        Key dek = decryptFromLMK(kd);
1170        return jceHandler.doCryptStuff(data, dek, Cipher.DECRYPT_MODE, cipherMode, iv);
1171    }
1172
1173    /**
1174     * Generates CBC-MAC (Cipher Block Chaining Message Authentication Code)
1175     * for some data.
1176     *
1177     * @param data the data to be MACed
1178     * @param kd the key used for MACing
1179     * @return generated CBC-MAC bytes
1180     * @throws SMException
1181     */
1182    @Override
1183    protected byte[] generateCBC_MACImpl (byte[] data, SecureDESKey kd) throws SMException {
1184        LogEvent evt = new LogEvent(this, "jce-provider-cbc-mac");
1185        try {
1186          return generateMACImpl(data,kd,cfg.get("cbc-mac","ISO9797ALG3MACWITHISO7816-4PADDING"),evt);
1187        } catch (Exception e) {
1188          Logger.log(evt);
1189          throw  e instanceof SMException ? (SMException)e : new SMException(e);
1190        }
1191    }
1192
1193    /**
1194     * Generates EDE-MAC (Encrypt Decrypt Encrypt Message Authentication Code)
1195     * for some data.
1196     *
1197     * @param data the data to be MACed
1198     * @param kd the key used for MACing
1199     * @return generated EDE-MAC bytes
1200     * @throws SMException
1201     */
1202    @Override
1203    protected byte[] generateEDE_MACImpl (byte[] data, SecureDESKey kd) throws SMException {
1204        LogEvent evt = new LogEvent(this, "jce-provider-ede-mac");
1205        try {
1206          return generateMACImpl(data,kd,cfg.get("ede-mac","DESEDEMAC"),evt);
1207        } catch (Exception e) {
1208          Logger.log(evt);
1209          throw  e instanceof SMException ? (SMException)e : new SMException(e);
1210        }
1211    }
1212
1213    private byte[] generateMACImpl (byte[] data, SecureDESKey kd,
1214        String macAlgorithm, LogEvent evt) throws SMException {
1215        try {
1216          return jceHandler.generateMAC(data,decryptFromLMK(kd),macAlgorithm);
1217        } catch (JCEHandlerException e){
1218          evt.addMessage(e);
1219          if (e.getCause() instanceof InvalidKeyException)
1220            throw new SMException(e);
1221          else
1222            throw new SMException("Unable to load MAC algorithm whose name is: " + macAlgorithm +
1223              ". Check that is used correct JCE provider and/or it is proper configured for this module.",e);
1224        }
1225    }
1226
1227    /**
1228     * Generates a random clear key component.
1229     * @param keyLength
1230     * @return clear key componenet
1231     * @throws SMException
1232     */
1233    public String generateClearKeyComponent (short keyLength) throws SMException {
1234        String clearKeyComponenetHexString;
1235        SimpleMsg[] cmdParameters =  {
1236            new SimpleMsg("parameter", "Key Length", keyLength)
1237        };
1238        LogEvent evt = new LogEvent(this, "s-m-operation");
1239        evt.addMessage(new SimpleMsg("command", "Generate Clear Key Component", cmdParameters));
1240        
1241        try {
1242            Key clearKey = jceHandler.generateDESKey(keyLength);
1243            byte[] clearKeyData = jceHandler.extractDESKeyMaterial(keyLength, clearKey);
1244            clearKeyComponenetHexString = ISOUtil.hexString(clearKeyData);
1245            evt.addMessage(new SimpleMsg("result", "Generated Clear Key Componenet", clearKeyComponenetHexString));
1246        } catch (JCEHandlerException e) {
1247            evt.addMessage(e);
1248            throw  e;
1249        } finally {
1250            Logger.log(evt);
1251        }
1252        return  clearKeyComponenetHexString;
1253    }
1254
1255    /**
1256     * Generates key check value.<br>
1257     * @param secureDESKey SecureDESKey with untrusted or fake Key Check Value
1258     * @return generated Key Check Value
1259     * @throws SMException
1260     */
1261    @Override
1262    protected byte[] generateKeyCheckValueImpl (SecureDESKey secureDESKey) throws SMException {
1263        return  calculateKeyCheckValue(decryptFromLMK(secureDESKey));
1264    }
1265
1266    @Override
1267    public SecureDESKey translateKeySchemeImpl (SecureDESKey key, KeyScheme keyScheme)
1268            throws SMException {
1269
1270        if (key == null)
1271            throw new SMException("Missing key to change");
1272
1273        if (keyScheme == null)
1274            throw new SMException("Missing desired key schame");
1275
1276        if (keyScheme == key.getScheme())
1277            return key;
1278
1279        switch (key.getScheme()) {
1280            case Z:
1281                throw new SMException("Single length DES key using the variant method"
1282                        + " can not be converted to any other key");
1283            case U:
1284            case T:
1285                throw new SMException("DES key using the variant method can not "
1286                        + "be converted to less secure key using X9.17 method");
1287            case X:
1288                if (keyScheme != KeyScheme.U)
1289                    throw new SMException("Double length key using X9.17 method may be  "
1290                            + "converted only to double length key using variant method");
1291                break;
1292            case Y:
1293                if (keyScheme != KeyScheme.T)
1294                    throw new SMException("Triple length key using X9.17 method may be  "
1295                            + "converted only to triple length key using variant method");
1296                break;
1297            default:
1298                throw new SMException("Change key scheme allowed only for keys"
1299                        + "using variant method");
1300        }
1301
1302        // get key in clear
1303        Key clearKey = decryptFromLMK(key);
1304        // Encrypt key under LMK
1305        String keyType = getMajorType(key.getKeyType()) + ":" + key.getVariant() + keyScheme;
1306        return encryptToLMK(key.getKeyLength(), keyType, clearKey);
1307    }
1308
1309    /**
1310     * Forms a key from 3 clear components and returns it encrypted under its corresponding LMK
1311     * The corresponding LMK is determined from the keyType
1312     * @param keyLength e.g. LENGTH_DES, LENGTH_DES3_2, LENGTH_DES3_3, ..
1313     * @param keyType possible values are those defined in the SecurityModule inteface. e.g., ZMK, TMK,...
1314     * @param clearComponent1HexString HexString containing the first component
1315     * @param clearComponent2HexString HexString containing the second component
1316     * @param clearComponent3HexString HexString containing the second component
1317     * @return forms an SecureDESKey from two clear components
1318     * @throws SMException
1319     */
1320    public SecureDESKey formKEYfromThreeClearComponents(
1321            short keyLength,
1322            String keyType,
1323            String clearComponent1HexString,
1324            String clearComponent2HexString,
1325            String clearComponent3HexString) throws SMException {
1326        SecureDESKey secureDESKey;
1327        LogEvent evt = new LogEvent(this, "s-m-operation");
1328
1329        try {
1330            byte[] clearComponent1 = ISOUtil.hex2byte(clearComponent1HexString);
1331            byte[] clearComponent2 = clearComponent2HexString != null ? ISOUtil.hex2byte(clearComponent2HexString) : new byte[keyLength >> 3];
1332            byte[] clearComponent3 = clearComponent3HexString != null ? ISOUtil.hex2byte(clearComponent3HexString) : new byte[keyLength >> 3];
1333            byte[] clearKeyBytes = ISOUtil.xor(ISOUtil.xor(clearComponent1, clearComponent2),
1334                    clearComponent3);
1335            Key clearKey = jceHandler.formDESKey(keyLength, clearKeyBytes);
1336            secureDESKey = encryptToLMK(keyLength, keyType, clearKey);
1337            SimpleMsg[] cmdParameters =  {
1338                new SimpleMsg("parameter", "Key Length", keyLength),
1339                new SimpleMsg("parameter", "Key Type", keyType),
1340                new SimpleMsg("parameter", "Component 1 Check Value", calculateKeyCheckValue(jceHandler.formDESKey(keyLength, clearComponent1))),
1341                new SimpleMsg("parameter", "Component 2 Check Value", calculateKeyCheckValue(jceHandler.formDESKey(keyLength, clearComponent2))),
1342                new SimpleMsg("parameter", "Component 3 Check Value", calculateKeyCheckValue(jceHandler.formDESKey(keyLength, clearComponent3)))
1343            };                        
1344            evt.addMessage(new SimpleMsg("command", "Form Key from Three Clear Components", cmdParameters));            
1345            evt.addMessage(new SimpleMsg("result", "Formed Key", secureDESKey));
1346        } catch (JCEHandlerException e) {
1347            evt.addMessage(e);
1348            throw  e;
1349        } finally {
1350            Logger.log(evt);
1351        }
1352        return  secureDESKey;
1353    }
1354
1355    @Override
1356    public SecureDESKey formKEYfromClearComponents (short keyLength, String keyType, String... components)
1357      throws SMException
1358    {
1359        if (components.length < 1 || components.length > 3)
1360            throw new SMException("Invalid number of clear key components");
1361        String[] s = new String[3];
1362        int i=0;
1363        for (String component : components)
1364            s[i++] = component;
1365
1366        return formKEYfromThreeClearComponents(keyLength, keyType, s[0], s[1], s[2]);
1367    }
1368
1369
1370    /**
1371     * Calculates a key check value over a clear key
1372     * @param key
1373     * @return the key check value
1374     * @exception SMException
1375     */
1376    protected byte[] calculateKeyCheckValue (Key key) throws SMException {
1377        byte[] encryptedZeroBlock = jceHandler.encryptData(zeroBlock, key);
1378        return ISOUtil.trim(encryptedZeroBlock, 3);
1379    }
1380
1381    /**
1382     * Encrypts a clear DES Key under LMK to form a SecureKey
1383     * @param keyLength
1384     * @param keyType
1385     * @param clearDESKey
1386     * @return secureDESKey
1387     * @throws SMException
1388     */
1389    protected SecureDESKey encryptToLMK (short keyLength, String keyType, Key clearDESKey) throws SMException {
1390        Key novar, left, medium, right;
1391        byte[] clearKeyBytes = jceHandler.extractDESKeyMaterial(keyLength, clearDESKey);
1392        byte[] bl = new byte[SMAdapter.LENGTH_DES>>3];
1393        byte[] bm = new byte[SMAdapter.LENGTH_DES>>3];
1394        byte[] br = new byte[SMAdapter.LENGTH_DES>>3];
1395        byte[] encrypted = null;
1396        // enforce correct (odd) parity before encrypting the key
1397        Util.adjustDESParity(clearKeyBytes);
1398        int lmkIndex = getKeyTypeIndex(keyLength, keyType);
1399        switch ( getScheme(keyLength, keyType) ){
1400            case Z:
1401            case X:
1402            case Y:
1403                novar = getLMK( lmkIndex );
1404                encrypted = jceHandler.encryptData(clearKeyBytes, novar);
1405                break;
1406            case U:
1407                left  = getLMK( KEY_U_LEFT  << 12 | lmkIndex & 0xfff );
1408                right = getLMK( KEY_U_RIGHT << 12 | lmkIndex & 0xfff );
1409                System.arraycopy(clearKeyBytes, 0, bl, 0, bl.length);
1410                System.arraycopy(clearKeyBytes, bl.length, br, 0, br.length);
1411                bl = jceHandler.encryptData(bl, left);
1412                br = jceHandler.encryptData(br, right);
1413                encrypted = ISOUtil.concat(bl, br);
1414                break;
1415            case T:
1416                left  = getLMK( KEY_T_LEFT   << 12 | lmkIndex & 0xfff );
1417                medium= getLMK( KEY_T_MEDIUM << 12 | lmkIndex & 0xfff );
1418                right = getLMK( KEY_T_RIGHT  << 12 | lmkIndex & 0xfff );
1419                System.arraycopy(clearKeyBytes, 0, bl, 0, bl.length);
1420                System.arraycopy(clearKeyBytes, bl.length, bm, 0, bm.length);
1421                System.arraycopy(clearKeyBytes, bl.length+bm.length, br, 0, br.length);
1422                bl = jceHandler.encryptData(bl, left);
1423                bm = jceHandler.encryptData(bm, medium);
1424                br = jceHandler.encryptData(br, right);
1425                encrypted = ISOUtil.concat(bl, bm);
1426                encrypted = ISOUtil.concat(encrypted, br);
1427                break;
1428        }
1429        SecureDESKey secureDESKey = new SecureDESKey(keyLength, keyType, encrypted,
1430                calculateKeyCheckValue(clearDESKey));
1431        return  secureDESKey;
1432    }
1433
1434    /**
1435     * Decrypts a secure DES key from encryption under LMK
1436     * @param secureDESKey (Key under LMK)
1437     * @return clear key
1438     * @throws SMException
1439     */
1440    protected Key decryptFromLMK (SecureDESKey secureDESKey) throws SMException {
1441        Key left, medium, right;
1442        byte[] keyBytes = secureDESKey.getKeyBytes();
1443        byte[] bl = new byte[SMAdapter.LENGTH_DES>>3];
1444        byte[] bm = new byte[SMAdapter.LENGTH_DES>>3];
1445        byte[] br = new byte[SMAdapter.LENGTH_DES>>3];
1446        byte[] clearKeyBytes = null;
1447        Integer lmkIndex = getKeyTypeIndex(secureDESKey.getKeyLength(), secureDESKey.getKeyType());
1448        if (lmkIndex==null)
1449           throw new SMException("Unsupported key type: " + secureDESKey.getKeyType());
1450        lmkIndex |= secureDESKey.getVariant()<<8;
1451        switch ( secureDESKey.getScheme() ) {
1452            case Z:
1453            case X:
1454            case Y:
1455                clearKeyBytes = jceHandler.decryptData(keyBytes, getLMK( lmkIndex ));
1456                break;
1457            case U:
1458                left  = getLMK( KEY_U_LEFT  << 12 | lmkIndex & 0xfff );
1459                right = getLMK( KEY_U_RIGHT << 12 | lmkIndex & 0xfff );
1460                System.arraycopy(keyBytes, 0, bl, 0, bl.length);
1461                System.arraycopy(keyBytes, bl.length, br, 0, br.length);
1462                bl = jceHandler.decryptData(bl, left);
1463                br = jceHandler.decryptData(br, right);
1464                clearKeyBytes = ISOUtil.concat(bl, br);
1465                clearKeyBytes = ISOUtil.concat(clearKeyBytes, 0, clearKeyBytes.length, clearKeyBytes, 0, br.length );
1466                break;
1467            case T:
1468                left  = getLMK( KEY_T_LEFT   << 12 | lmkIndex & 0xfff );
1469                medium= getLMK( KEY_T_MEDIUM << 12 | lmkIndex & 0xfff );
1470                right = getLMK( KEY_T_RIGHT  << 12 | lmkIndex & 0xfff );
1471                System.arraycopy(keyBytes, 0, bl, 0, bl.length);
1472                System.arraycopy(keyBytes, bl.length, bm, 0, bm.length);
1473                System.arraycopy(keyBytes, bl.length+bm.length, br, 0, br.length);
1474                bl = jceHandler.decryptData(bl, left);
1475                bm = jceHandler.decryptData(bm, medium);
1476                br = jceHandler.decryptData(br, right);
1477                clearKeyBytes = ISOUtil.concat(bl, bm);
1478                clearKeyBytes = ISOUtil.concat(clearKeyBytes, br);
1479                break;
1480        }
1481        if (!Util.isDESParityAdjusted(clearKeyBytes))
1482            throw new JCEHandlerException("Parity not adjusted");
1483        return jceHandler.formDESKey(secureDESKey.getKeyLength(), clearKeyBytes);
1484    }
1485
1486    private char[] formatPINBlock(String pin, int checkDigit){
1487      char[] block = ISOUtil.hexString(fPaddingBlock).toCharArray();
1488      char[] pinLenHex = String.format("%02X", pin.length()).toCharArray();
1489      pinLenHex[0] = (char)('0' + checkDigit);
1490
1491      // pin length then pad with 'F'
1492      System.arraycopy(pinLenHex, 0, block, 0, pinLenHex.length);
1493      System.arraycopy(pin.toCharArray(), 0
1494                      ,block, pinLenHex.length, pin.length());
1495      return block;
1496    }
1497
1498    private String[] splitPins(String pins) {
1499      String[] pin = new String[2];
1500      String[] p = SPLIT_PIN_PATTERN.split(pins);
1501      pin[0] = p[0];
1502      if (p.length >= 2)
1503          pin[1] = p[1];
1504      return pin;
1505    }
1506
1507    /**
1508     * Calculates the clear PIN Block
1509     * @param pin as entered by the card holder on the PIN entry device
1510     * @param pinBlockFormat
1511     * @param accountNumber (the 12 right-most digits of the account number excluding the check digit)
1512     * @return The clear PIN Block
1513     * @throws SMException
1514     *
1515     */
1516    protected byte[] calculatePINBlock (String pin, byte pinBlockFormat, String accountNumber) throws SMException {
1517        byte[] pinBlock = null;
1518        String oldPin = null;
1519        if (pinBlockFormat==SMAdapter.FORMAT42){
1520          String[] p = splitPins(pin);
1521          pin = p[0];
1522          oldPin = p[1];
1523          if (oldPin.length() < MIN_PIN_LENGTH || oldPin.length() > MAX_PIN_LENGTH)
1524              throw  new SMException("Invalid OLD PIN length: " + oldPin.length());
1525          if (!ISOUtil.isNumeric(oldPin, 10))
1526              throw  new SMException("Invalid OLD PIN decimal digits: " + oldPin);
1527        }
1528        if (pin.length() < MIN_PIN_LENGTH || pin.length() > MAX_PIN_LENGTH)
1529            throw  new SMException("Invalid PIN length: " + pin.length());
1530        if (!ISOUtil.isNumeric(pin, 10))
1531            throw  new SMException("Invalid PIN decimal digits: " + pin);
1532        if (isVSDCPinBlockFormat(pinBlockFormat)) {
1533          if (accountNumber.length() != 16 )
1534            throw  new SMException("Invalid UDK-A: " + accountNumber
1535                    + ". The length of the UDK-A must be 16 hexadecimal digits");
1536        } else if (accountNumber.length() != 12)
1537            throw  new SMException("Invalid Account Number: " + accountNumber + ". The length of the account number must be 12 (the 12 right-most digits of the account number excluding the check digit)");
1538        switch (pinBlockFormat) {
1539            case FORMAT00: // same as FORMAT01
1540            case FORMAT01:
1541                {
1542                    // Block 1
1543                    byte[] block1 = ISOUtil.hex2byte(new String(formatPINBlock(pin,0x0)));
1544
1545                    // Block 2
1546                    byte[] block2 = ISOUtil.hex2byte("0000" + accountNumber);
1547                    // pinBlock
1548                    pinBlock = ISOUtil.xor(block1, block2);
1549                }
1550                break;
1551            case FORMAT03:
1552                {
1553                    char[] block = ISOUtil.hexString(fPaddingBlock).toCharArray();
1554                    System.arraycopy(pin.toCharArray(), 0
1555                                    ,block, 0, pin.length());
1556                    pinBlock = ISOUtil.hex2byte (new String(block));
1557                }
1558                break;
1559            case FORMAT05:
1560                {
1561                    // Block 1
1562                    char[] block1 = formatPINBlock(pin, 0x1);
1563
1564                    // Block rnd
1565                    byte[] rnd = new byte[8];
1566                    Random rd = new SecureRandom();
1567                    rd.nextBytes(rnd);
1568
1569                    // Block 2
1570                    char[] block2 = ISOUtil.hexString(rnd).toCharArray();
1571
1572                    // merge blocks
1573                    System.arraycopy(block1, 0
1574                                    ,block2, 0, pin.length() + 2
1575                    );
1576                    // pinBlock
1577                    pinBlock = ISOUtil.hex2byte(new String(block2));
1578                }
1579                break;
1580            case FORMAT34:
1581                {
1582                    pinBlock = ISOUtil.hex2byte (new String(formatPINBlock(pin,0x2)));
1583                }
1584                break;
1585            case FORMAT35:
1586                {
1587                    // Block 1
1588                    byte[] block1 = ISOUtil.hex2byte(new String(formatPINBlock(pin,0x2)));
1589
1590                    // Block 2
1591                    byte[] block2 = ISOUtil.hex2byte("0000" + accountNumber);
1592                    // pinBlock
1593                    pinBlock = ISOUtil.xor(block1, block2);
1594                }
1595                break;
1596            case FORMAT41:
1597                {
1598                    // Block 1
1599                    byte[] block1 = ISOUtil.hex2byte(new String(formatPINBlock(pin,0x0)));
1600
1601                    // Block 2 - account number should contain Unique DEA Key A (UDK-A)
1602                    byte[] block2 = ISOUtil.hex2byte("00000000"
1603                                + accountNumber.substring(accountNumber.length()-8) );
1604                    // pinBlock
1605                    pinBlock = ISOUtil.xor(block1, block2);
1606                }
1607                break;
1608            case FORMAT42:
1609                {
1610                    // Block 1
1611                    byte[] block1 = ISOUtil.hex2byte(new String(formatPINBlock(pin,0x0)));
1612
1613                    // Block 2 - account number should contain Unique DEA Key A (UDK-A)
1614                    byte[] block2 = ISOUtil.hex2byte("00000000"
1615                                + accountNumber.substring(accountNumber.length()-8) );
1616                    // Block 3 - old pin
1617                    byte[] block3 = ISOUtil.hex2byte(ISOUtil.zeropadRight(oldPin, 16));
1618                    // pinBlock
1619                    pinBlock = ISOUtil.xor(block1, block2);
1620                    pinBlock = ISOUtil.xor(pinBlock, block3);
1621                }
1622                break;
1623            default:
1624                throw  new SMException("Unsupported PIN format: " + pinBlockFormat);
1625        }
1626        return  pinBlock;
1627    }
1628
1629    private void validatePinBlock(char[] pblock, int checkDigit, int padidx, int offset)
1630            throws SMException {
1631      validatePinBlock(pblock, checkDigit, padidx, offset, 'F');
1632    }
1633
1634    private void validatePinBlock(char[] pblock, int checkDigit
1635                             ,int padidx, int offset, char padDigit)
1636            throws SMException {
1637      // test pin block check digit
1638      if (checkDigit >= 0 && pblock[0] - '0' != checkDigit)
1639          throw new SMException("PIN Block Error - invalid check digit");
1640      // test pin block pdding
1641      int i = pblock.length - 1;
1642      while (i >= padidx)
1643          if (pblock[i--] != padDigit && padDigit > 0)
1644              throw new SMException("PIN Block Error - invalid padding");
1645      // test pin block digits
1646      while (i >= offset)
1647          if (pblock[i--] >= 'A')
1648              throw new SMException("PIN Block Error - illegal pin digit");
1649      // test pin length
1650      int pinLength = padidx - offset;
1651      if (pinLength < MIN_PIN_LENGTH || pinLength > MAX_PIN_LENGTH)
1652          throw new SMException("PIN Block Error - invalid pin length: " + pinLength);
1653    }
1654
1655    /**
1656     * Calculates the clear pin (as entered by card holder on the pin entry device)
1657     * givin the clear PIN block
1658     * @param pinBlock clear PIN Block
1659     * @param pinBlockFormat
1660     * @param accountNumber
1661     * @return the pin
1662     * @throws SMException
1663     */
1664    protected String calculatePIN (byte[] pinBlock, byte pinBlockFormat, String accountNumber) throws SMException {
1665        String pin = null;
1666        if (isVSDCPinBlockFormat(pinBlockFormat)) {
1667          if (accountNumber.length() != 16 )
1668            throw  new SMException("Invalid UDK-A: " + accountNumber
1669                    + ". The length of the UDK-A must be 16 hexadecimal digits");
1670        } else if (accountNumber.length() != 12)
1671            throw  new SMException("Invalid Account Number: " + accountNumber + ". The length of the account number must be 12 (the 12 right-most digits of the account number excluding the check digit)");
1672        switch (pinBlockFormat) {
1673            case FORMAT00: // same as format 01
1674            case FORMAT01:
1675                {
1676                    // Block 2
1677                    byte[] bl2 = ISOUtil.hex2byte("0000" + accountNumber);
1678                    // get Block1
1679                    byte[] bl1 = ISOUtil.xor(pinBlock, bl2);
1680                    int pinLength = bl1[0] & 0x0f;
1681                    char[] block1 = ISOUtil.hexString(bl1).toCharArray();
1682                    int offset = 2;
1683                    int checkDigit = 0x0;
1684                    int padidx = pinLength + offset;
1685                    // test pin block
1686                    validatePinBlock(block1,checkDigit,padidx,offset);
1687                    // get pin
1688                    pin = new String(Arrays.copyOfRange(block1, offset, padidx));
1689                }
1690                break;
1691            case FORMAT03: 
1692                {
1693                    String bl1 = ISOUtil.hexString(pinBlock);
1694                    int padidx = bl1.indexOf('F');
1695                    if ( padidx < 0) padidx = 12;
1696                    char[] block1 = bl1.toCharArray();
1697                    int checkDigit = -0x1;
1698                    int offset = 0;
1699
1700                    // test pin block
1701                    validatePinBlock(block1,checkDigit,padidx,offset);
1702                    // get pin
1703                    pin = new String(Arrays.copyOfRange(block1, offset, padidx));
1704                }
1705                break;
1706            case FORMAT05:
1707                {
1708                    // get Block1
1709                    byte[] bl1 = pinBlock;
1710                    int pinLength = bl1[0] & 0x0f;
1711                    char[] block1 = ISOUtil.hexString(bl1).toCharArray();
1712                    int offset = 2;
1713                    int checkDigit = 0x01;
1714                    int padidx = pinLength + offset;
1715                    // test pin block
1716                    validatePinBlock(block1, checkDigit, padidx, offset, (char) 0);
1717                    // get pin
1718                    pin = new String(Arrays.copyOfRange(block1, offset, padidx));
1719                }
1720                break;
1721            case FORMAT34:
1722                {
1723                    int pinLength = pinBlock[0] & 0x0f;
1724                    char[] block1 = ISOUtil.hexString(pinBlock).toCharArray();
1725                    int offset = 2;
1726                    int checkDigit = 0x2;
1727                    int padidx = pinLength + offset;
1728                    // test pin block
1729                    validatePinBlock(block1,checkDigit,padidx,offset);
1730                    // get pin
1731                    pin = new String(Arrays.copyOfRange(block1, offset, padidx));
1732                }
1733                break;
1734            case FORMAT35:
1735                {
1736                    // Block 2
1737                    byte[] bl2 = ISOUtil.hex2byte("0000" + accountNumber);
1738                    // get Block1
1739                    byte[] bl1 = ISOUtil.xor(pinBlock, bl2);
1740                    int pinLength = bl1[0] & 0x0f;
1741                    char[] block1 = ISOUtil.hexString(bl1).toCharArray();
1742                    int offset = 2;
1743                    int checkDigit = 0x2;
1744                    int padidx = pinLength + offset;
1745                    // test pin block
1746                    validatePinBlock(block1,checkDigit,padidx,offset);
1747                    // get pin
1748                    pin = new String(Arrays.copyOfRange(block1, offset, padidx));
1749                }
1750                break;
1751            case FORMAT41:
1752                {
1753                    // Block 2 - account number should contain Unique DEA Key A (UDK-A)
1754                    byte[] bl2 = ISOUtil.hex2byte("00000000"
1755                                + accountNumber.substring(accountNumber.length()-8) );
1756                    // get Block1
1757                    byte[] bl1 = ISOUtil.xor(pinBlock, bl2);
1758                    int pinLength = bl1[0] & 0x0f;
1759                    char[] block1 = ISOUtil.hexString(bl1).toCharArray();
1760                    int offset = 2;
1761                    int checkDigit = 0x0;
1762                    int padidx = pinLength + offset;
1763                    // test pin block
1764                    validatePinBlock(block1,checkDigit,padidx,offset);
1765                    // get pin
1766                    pin = new String(Arrays.copyOfRange(block1, offset, padidx));
1767                }
1768                break;
1769            case FORMAT42:
1770                {
1771                    // Block 2 - account number should contain Unique DEA Key A (UDK-A)
1772                    byte[] bl2 = ISOUtil.hex2byte("00000000"
1773                                + accountNumber.substring(accountNumber.length()-8) );
1774                    // get Block1
1775                    byte[] bl1 = ISOUtil.xor(pinBlock, bl2);
1776                    int pinLength = bl1[0] & 0x0f;
1777                    char[] block1 = ISOUtil.hexString(bl1).toCharArray();
1778                    int offset = 2;
1779                    int checkDigit = 0x0;
1780                    int padidx = pinLength + offset;
1781                    // test pin block
1782                    validatePinBlock(block1,checkDigit,padidx,offset,'0');
1783                    // get pin
1784                    pin = new String(Arrays.copyOfRange(block1, offset, padidx));
1785                }
1786                break;
1787            default:
1788                throw  new SMException("Unsupported PIN Block format: " + pinBlockFormat);
1789        }
1790        return  pin;
1791    }
1792
1793    /**
1794     * Initializes the JCE Security Module
1795     * @param jceProviderClassName
1796     * @param lmkFile Local Master Keys File used by JCE Security Module to store the LMKs
1797     * @param lmkRebuild if set to true, the lmkFile gets overwritten with newly generated keys (WARNING: this would render all your previously stored SecureKeys unusable)
1798     * @throws SMException
1799     */
1800    private void init (String jceProviderClassName, String lmkFile, boolean lmkRebuild) throws SMException {
1801        File lmk = lmkFile != null ? new File(lmkFile) : null;
1802        if (lmk == null && !lmkRebuild)
1803            throw new SMException ("null lmkFile - needs rebuild");
1804        try {
1805            keyTypeToLMKIndex = new TreeMap<>();
1806            keyTypeToLMKIndex.put(SMAdapter.TYPE_ZMK, 0x000);
1807            keyTypeToLMKIndex.put(SMAdapter.TYPE_ZPK, 0x001);
1808            keyTypeToLMKIndex.put(SMAdapter.TYPE_PVK, 0x002);
1809            keyTypeToLMKIndex.put(SMAdapter.TYPE_TPK, 0x002);
1810            keyTypeToLMKIndex.put(SMAdapter.TYPE_TMK, 0x002);
1811            keyTypeToLMKIndex.put(SMAdapter.TYPE_TAK, 0x003);
1812//            keyTypeToLMKIndex.put(PINLMKIndex,        0x004);
1813            keyTypeToLMKIndex.put(SMAdapter.TYPE_CVK, 0x402);
1814            keyTypeToLMKIndex.put(SMAdapter.TYPE_ZAK, 0x008);
1815            keyTypeToLMKIndex.put(SMAdapter.TYPE_BDK, 0x009);
1816            keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_AC, 0x109);
1817            keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_SMI,  0x209);
1818            keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_SMC,  0x309);
1819            keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_DAC,  0x409);
1820            keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_DN,   0x509);
1821            keyTypeToLMKIndex.put(SMAdapter.TYPE_MK_CVC3, 0x709);
1822            keyTypeToLMKIndex.put(SMAdapter.TYPE_ZEK, 0x00A);
1823            keyTypeToLMKIndex.put(SMAdapter.TYPE_DEK, 0x00B);
1824            keyTypeToLMKIndex.put(SMAdapter.TYPE_RSA_SK, 0x00C);
1825            keyTypeToLMKIndex.put(SMAdapter.TYPE_HMAC,   0x10C);
1826            keyTypeToLMKIndex.put(SMAdapter.TYPE_RSA_PK, 0x00D);
1827            Provider provider = null;
1828            LogEvent evt = new LogEvent(this, "jce-provider");
1829            try {
1830                if (jceProviderClassName != null && !jceProviderClassName.isEmpty()) {
1831                    provider = (Provider) Class.forName(jceProviderClassName).getDeclaredConstructor().newInstance();
1832                    Security.addProvider(provider);
1833                    evt.addMessage("name", provider.getName());
1834                }
1835            } catch (Exception e) {
1836                evt.addMessage(e);
1837                throw  new SMException("Unable to load jce provider whose class name is: "
1838                        + jceProviderClassName);
1839            } finally {
1840                Logger.log(evt);
1841            }
1842            jceHandler = new JCEHandler();
1843            if (lmkRebuild) {
1844                // Creat new LMK file
1845                evt = new LogEvent(this, "local-master-keys");
1846                if (lmk != null)
1847                    evt.addMessage("Rebuilding new Local Master Keys in file: \"" + lmk.getCanonicalPath() + "\".");
1848                Logger.log(evt);
1849                // Generate New random Local Master Keys
1850                generateLMK();
1851                // Write the new Local Master Keys to file
1852                evt = new LogEvent(this, "local-master-keys");
1853                if (lmk != null) {
1854                    writeLMK(lmk);
1855                    evt.addMessage("Local Master Keys built successfully in file: \""
1856                      + lmk.getCanonicalPath() + "\".");
1857                } else {
1858                    evt.addMessage("Local Master Keys built successfully");
1859                }
1860                Logger.log(evt);
1861            }
1862            if (lmk != null) {
1863                if (!lmk.exists()) {
1864                    // LMK File does not exist
1865                    throw  new SMException("Error loading Local Master Keys, file: \""
1866                      + lmk.getCanonicalPath() + "\" does not exist."
1867                      + " Please specify a valid LMK file, or rebuild a new one.");
1868                }
1869                else {
1870                    // Read LMK from file
1871                    readLMK(lmk);
1872                    evt = new LogEvent(this, "local-master-keys");
1873                    evt.addMessage("Loaded successfully from file: \"" + lmk.getCanonicalPath()
1874                      + "\"");
1875                    Logger.log(evt);
1876                }
1877            }
1878        } catch (Exception e) {
1879            if (e instanceof SMException) {
1880                throw  (SMException)e;
1881            }
1882            else {
1883                throw  new SMException(e);
1884            }
1885        }
1886    }
1887
1888    private byte[] applySchemeVariant(byte[] lmkdata, int variant){
1889        byte[] vardata = new byte[lmkdata.length];
1890        System.arraycopy(lmkdata, 0, vardata, 0, lmkdata.length);
1891        //XOR first byte of second key with selected variant byte
1892        vardata[8] ^= variant;
1893        return vardata;
1894    }
1895
1896    private byte[] applyVariant(byte[] lmkdata, int variant){
1897        byte[] vardata = new byte[lmkdata.length];
1898        System.arraycopy(lmkdata, 0, vardata, 0, lmkdata.length);
1899        //XOR first byte of first key with selected variant byte
1900        vardata[0] ^= variant;
1901        return vardata;
1902    }
1903
1904    private void spreadLMKVariants(byte[] lmkData, int idx) throws SMException {
1905        int i = 0;
1906        for (int v :variants){
1907            int k = 0;
1908            byte[] variantData = applyVariant(lmkData,v);
1909            for (int sv :schemeVariants){
1910                byte[] svData = applySchemeVariant(variantData,sv);
1911//                System.out.println(String.format("LMK0x%1$d:%2$d:%3$02x=%4$s", k, i, idx
1912//                        ,ISOUtil.hexString(svData)));
1913                // make it 3 components to work with sun JCE
1914                svData = ISOUtil.concat(
1915                    svData, 0, jceHandler.getBytesLength(SMAdapter.LENGTH_DES3_2KEY),
1916                    svData, 0, jceHandler.getBytesLength(SMAdapter.LENGTH_DES)
1917                    );
1918                int key = idx;
1919                key += 0x100*i;
1920                key += 0x1000*k++;
1921                lmks.put(key, (SecretKey)jceHandler.formDESKey(SMAdapter.LENGTH_DES3_2KEY, svData));
1922            }
1923            i++;
1924        }
1925    }
1926
1927    /**
1928     * Generates new LMK keys
1929     * @exception SMException
1930     */
1931    private void generateLMK () throws SMException {
1932        lmks.clear();
1933        try {
1934            for (int i = 0; i <= LMK_PAIRS_NO; i++){
1935              SecretKey lmkKey = (SecretKey)jceHandler.generateDESKey(LMK_KEY_LENGTH);
1936              spreadLMKVariants(lmkKey.getEncoded(), i);
1937            }
1938        } catch (JCEHandlerException e) {
1939            throw  new SMException("Can't generate Local Master Keys", e);
1940        }
1941    }
1942
1943    /**
1944     * reads (loads) LMK's from lmkFile
1945     * @param lmkFile
1946     * @exception SMException
1947     */
1948    private void readLMK (File lmkFile) throws SMException {
1949        lmks.clear();
1950        try {
1951            Properties lmkProps = new Properties();
1952            InputStream in = new BufferedInputStream(new FileInputStream(lmkFile));
1953            try {
1954                lmkProps.load(in);
1955            } finally {
1956                in.close();
1957            }
1958            Enumeration<?> e = lmkProps.propertyNames();
1959            while (e.hasMoreElements()) {
1960                String propName = (String) e.nextElement();
1961                lmkProps.put(propName, Environment.get(lmkProps.getProperty(propName)));
1962            }
1963            byte[] lmkData;
1964            for (int i = 0; i <= LMK_PAIRS_NO; i++) {
1965                lmkData = ISOUtil.hex2byte(lmkProps.getProperty(
1966                        String.format("LMK0x%1$02x", i)).substring(0, SMAdapter.LENGTH_DES3_2KEY/4));
1967                spreadLMKVariants(lmkData, i);
1968            }
1969        } catch (Exception e) {
1970            throw  new SMException("Can't read Local Master Keys from file: " +
1971                    lmkFile, e);
1972        }
1973    }
1974
1975    /**
1976     * Writes a newly generated LMK's to lmkFile
1977     * @param lmkFile
1978     * @exception SMException
1979     */
1980    private void writeLMK (File lmkFile) throws SMException {
1981        Properties lmkProps = new Properties();
1982        try {
1983            for (int i = 0; i <= LMK_PAIRS_NO; i++) {
1984                lmkProps.setProperty(String.format("LMK0x%1$02x", i),
1985                        ISOUtil.hexString(lmks.get(i).getEncoded()));
1986            }
1987            OutputStream out = new BufferedOutputStream(new FileOutputStream(lmkFile));
1988            try {
1989                lmkProps.store(out, "Local Master Keys");
1990            } finally {
1991                out.close();
1992            }
1993        } catch (Exception e) {
1994            throw  new SMException("Can't write Local Master Keys to file: " + lmkFile,
1995                    e);
1996        }
1997    }
1998
1999    /**
2000     * gets the suitable LMK variant for the key index
2001     * @param lmkIndex
2002     * @return the lmks secret key for the givin key index
2003     * @throws SMException
2004     */
2005    private SecretKey getLMK (Integer lmkIndex) throws SMException {
2006        SecretKey lmk = lmks.get(lmkIndex);
2007        if (lmk==null)
2008            throw  new SMException(String.format("Invalid key code: LMK0x%1$04x", lmkIndex));
2009        return  lmk;
2010    }
2011    /**
2012     * maps a key type to an LMK Index
2013     */
2014    private Map<String,Integer> keyTypeToLMKIndex;
2015    /**
2016     * The clear Local Master Keys
2017     */
2018    private final Map<Integer,SecretKey> lmks = new TreeMap<>();
2019    /**
2020     * A index for the LMK used to encrypt the PINs
2021     */
2022    private static final Integer PINLMKIndex = 0x004;
2023    /**
2024     * The key length (in bits) of the Local Master Keys.
2025     * JCESecurityModule uses Triple DES Local Master Keys
2026     */
2027    private static final short LMK_KEY_LENGTH = LENGTH_DES3_2KEY;
2028    /**
2029     * The minimum length of the PIN
2030     */
2031    private static final short MIN_PIN_LENGTH = 4;
2032    /**
2033     * The maximum length of the PIN
2034     */
2035    private static final short MAX_PIN_LENGTH = 12;
2036
2037    /**
2038     * a 64-bit block of ones used when calculating pin blocks
2039     */
2040    private static final byte[] fPaddingBlock = ISOUtil.hex2byte("FFFFFFFFFFFFFFFF");
2041
2042    /**
2043     * a dummy 64-bit block of zeros used when calculating the check value
2044     */
2045    private static final byte[] zeroBlock = ISOUtil.hex2byte("0000000000000000");
2046
2047    protected JCEHandler jceHandler;
2048
2049    //--------------------------------------------------------------------------------------------------
2050    // DUKPT
2051    //--------------------------------------------------------------------------------------------------
2052
2053    private byte[] encrypt64(byte[] data, byte[] key)
2054            throws JCEHandlerException
2055    {
2056        return jceHandler.encryptData(
2057                data,
2058                jceHandler.formDESKey((short) (key.length << 3), key)
2059        );
2060    }
2061
2062    private byte[] decrypt64(byte[] data, byte[] key)
2063            throws JCEHandlerException
2064    {
2065        return jceHandler.decryptData(
2066                data,
2067                jceHandler.formDESKey((short) (key.length << 3), key)
2068        );
2069    }
2070
2071    protected byte[] specialEncrypt(byte[] data, byte[] key)
2072            throws JCEHandlerException
2073    {
2074        if (key.length == 8)
2075        {
2076            data = ISOUtil.xor(data, key);
2077            data = encrypt64(data, key);
2078            return ISOUtil.xor(data, key);
2079        }
2080        byte[] keyL = new byte[8];
2081        byte[] keyR = new byte[8];
2082        System.arraycopy(key, 0, keyL, 0, 8);
2083        System.arraycopy(key, 8, keyR, 0, 8);
2084        data = encrypt64(data, keyL);
2085        data = decrypt64(data, keyR);
2086        data = encrypt64(data, keyL);
2087        return data;
2088    }
2089
2090    protected byte[] specialDecrypt(byte[] data, byte[] key)
2091            throws JCEHandlerException
2092    {
2093        if (key.length == 8)
2094        {
2095            data = ISOUtil.xor(data, key);
2096            data = decrypt64(data, key);
2097            return ISOUtil.xor(data, key);
2098        }
2099        byte[] keyL = new byte[8];
2100        byte[] keyR = new byte[8];
2101        System.arraycopy(key, 0, keyL, 0, 8);
2102        System.arraycopy(key, 8, keyR, 0, 8);
2103        data = decrypt64(data, keyL);
2104        data = encrypt64(data, keyR);
2105        data = decrypt64(data, keyL);
2106        return data;
2107    }
2108
2109    private void shr(byte[] b)
2110    {
2111        boolean carry = false;
2112        for (int i = 0; i < b.length; i++)
2113        {
2114            byte c = b[i];
2115            b[i] = (byte) (c >>> 1 & 0x7F);
2116            if (carry)
2117            {
2118                b[i] |= 0x80;
2119            }
2120            carry = (c & 0x01) == 1;
2121        }
2122    }
2123
2124    private void or(byte[] b, byte[] mask, int offset)
2125    {
2126        int len = Math.min(b.length - offset, mask.length);
2127        byte[] d = new byte[len];
2128
2129        for (int i = 0; i < len; i++)
2130        {
2131            b[offset++] |= mask[i];
2132        }
2133    }
2134
2135    private byte[] and(byte[] b, byte[] mask, int offset)
2136    {
2137        int len = Math.min(b.length - offset, mask.length);
2138        byte[] d = new byte[b.length];
2139        System.arraycopy(b, 0, d, 0, b.length);
2140
2141        for (int i = 0; i < len; i++, offset++)
2142        {
2143            d[offset] = (byte) (b[offset] & mask[i]);
2144        }
2145        return d;
2146    }
2147
2148    private byte[] and(byte[] b, byte[] mask)
2149    {
2150        return and(b, mask, 0);
2151    }
2152
2153    private boolean notZero(byte[] b)
2154    {
2155        int l = b.length;
2156        for (int i = 0; i < l; i++)
2157        {
2158            if (b[i] != 0)
2159            {
2160                return true;
2161            }
2162        }
2163        return false;
2164    }
2165
2166    private byte[] calculateInitialKey(KeySerialNumber sn, SecureDESKey bdk, boolean tdes)
2167            throws SMException
2168    {
2169        byte[] kl = new byte[8];
2170        byte[] kr = new byte[8];
2171        byte[] kk = decryptFromLMK(bdk).getEncoded();
2172        byte[] ksn = new byte[8];
2173
2174        System.arraycopy(kk, 0, kl, 0, 8);
2175        System.arraycopy(kk, 8, kr, 0, 8);
2176        System.arraycopy (sn.getBytes(), 0, ksn, 0, ksn.length);
2177        ksn[7] &= 0xE0;
2178        
2179        byte[] data = encrypt64(ksn, kl);
2180        data = decrypt64(data, kr);
2181        data = encrypt64(data, kl);
2182
2183        // ANS X9.24:2009 3DES keys (@DFLC/@apr 2011) A.6 steps 5 & 6 (p69)
2184        if (tdes)
2185        {
2186            byte[] kl2 = ISOUtil.xor(kl, _VARIANT_RIGHT_HALF);
2187            byte[] kr2 = ISOUtil.xor(kr, _VARIANT_RIGHT_HALF);
2188            byte[] data2 = encrypt64(ksn, kl2);
2189            data2 = decrypt64(data2, kr2);
2190            data2 = encrypt64(data2, kl2);
2191
2192            byte[] d = new byte[16];
2193            System.arraycopy(data, 0, d, 0, 8);
2194            System.arraycopy(data2, 0, d, 8, 8);
2195            data = d;
2196        }
2197        return data;
2198    }
2199
2200
2201    @Override
2202    public byte[] dataEncrypt (SecureDESKey bdk, byte[] clearText) throws SMException {
2203        try {
2204            byte[] ksnB = jceHandler.generateDESKey ((short) 128).getEncoded();
2205            KeySerialNumber ksn = getKSN (ksnB);
2206            byte[] derivedKey = calculateDerivedKey (ksn, bdk, true, true);
2207            Key dk = jceHandler.formDESKey ((short) 128, derivedKey);
2208            byte[] cypherText = jceHandler.encryptData (lpack(clearText), dk);
2209
2210            ByteBuffer bb = ByteBuffer.allocate (cypherText.length + 32);
2211            bb.put (ksnB);
2212            bb.put (cypherText);
2213            bb.put (hash8 (new byte[][]{ ksnB, cypherText, derivedKey }));
2214            return bb.array();
2215        } catch (JCEHandlerException e) {
2216            throw new SMException (e);
2217        }
2218    }
2219
2220    @Override
2221    public byte[] dataDecrypt (SecureDESKey bdk, byte[] cypherText) throws SMException {
2222        try {
2223            if (cypherText.length < 32) {
2224                throw new SMException (
2225                  "Invalid key block '" + ISOUtil.hexString (cypherText) + "'"
2226                );
2227            }
2228            byte[] ksnB          = new byte[24];
2229            byte[] encryptedData = new byte[cypherText.length - 32];
2230            byte[] mac           = new byte[8];
2231
2232            System.arraycopy (cypherText,  0, ksnB, 0, 24);
2233            System.arraycopy (cypherText, 24, encryptedData, 0, encryptedData.length);
2234            System.arraycopy (cypherText, cypherText.length-8, mac, 0, 8);
2235
2236            KeySerialNumber ksn = getKSN (ksnB);
2237
2238            byte[] derivedKey = calculateDerivedKey (ksn, bdk, true, true);
2239            Key dk = jceHandler.formDESKey ((short) 128, derivedKey);
2240            byte[] clearText = jceHandler.decryptData (encryptedData, dk);
2241            byte[] generatedMac = hash8 (
2242              new byte[][] { ksnB, encryptedData, derivedKey }
2243            );
2244            if (!Arrays.equals (mac, generatedMac))
2245                throw new SMException ("Invalid cyphertext.");
2246            return lunpack (clearText);
2247        } catch (JCEHandlerException e) {
2248            throw new SMException (e);
2249        }
2250   }
2251
2252    protected byte[] calculateDerivedKey(KeySerialNumber ksn, SecureDESKey bdk, boolean tdes, boolean dataEncryption)
2253            throws SMException
2254    {
2255        return tdes?calculateDerivedKeyTDES(ksn,bdk, dataEncryption):calculateDerivedKeySDES(ksn,bdk);
2256    }
2257
2258    private byte[] calculateDerivedKeySDES(KeySerialNumber ksn, SecureDESKey bdk)
2259            throws SMException
2260    {
2261        final byte[] _1FFFFF =
2262                new byte[]{(byte) 0x1F, (byte) 0xFF, (byte) 0xFF};
2263        final byte[] _100000 =
2264                new byte[]{(byte) 0x10, (byte) 0x00, (byte) 0x00};
2265        final byte[] _E00000 =
2266                new byte[]{(byte) 0xE0, (byte) 0x00, (byte) 0x00};
2267
2268        byte[] curkey = calculateInitialKey(ksn, bdk, false);
2269        byte[] smidr = new byte[8];
2270        System.arraycopy (ksn.getBytes(), 2, smidr, 0, smidr.length);
2271
2272        byte[] reg3 = ksn.getTransactionCounterBytes();
2273        reg3 = and(reg3, _1FFFFF);
2274        byte[] shiftr = _100000;
2275        byte[] temp;
2276        byte[] tksnr;
2277        smidr = and(smidr, _E00000, 5);
2278        do
2279        {
2280            temp = and(shiftr, reg3);
2281            if (notZero(temp))
2282            {
2283                or(smidr, shiftr, 5);
2284                tksnr = ISOUtil.xor(smidr, curkey);
2285                tksnr = encrypt64(tksnr, curkey);
2286                curkey = ISOUtil.xor(tksnr, curkey);
2287            }
2288            shr(shiftr);
2289        }
2290        while (notZero(shiftr));
2291        curkey[7] ^= 0xFF;
2292        return curkey;
2293    }
2294    private byte[] calculateDerivedKeyTDES(KeySerialNumber ksn, SecureDESKey bdk, boolean dataEncryption)
2295            throws SMException
2296    {
2297        final byte[] _1FFFFF =
2298                new byte[]{(byte) 0x1F, (byte) 0xFF, (byte) 0xFF};
2299        final byte[] _100000 =
2300                new byte[]{(byte) 0x10, (byte) 0x00, (byte) 0x00};
2301        final byte[] _E00000 =
2302                new byte[]{(byte) 0xE0, (byte) 0x00, (byte) 0x00};
2303
2304        byte[] curkey = calculateInitialKey(ksn, bdk, true);
2305
2306        byte[] smidr = new byte[8];
2307        System.arraycopy (ksn.getBytes(), 2, smidr, 0, smidr.length);
2308
2309        byte[] ksnImage = ksn.getBytes();
2310        byte[] reg3;
2311        if (dataEncryption && ksnImage[0] != (byte) 0xFF && ksnImage[1] != (byte) 0xFF) {
2312            // jPOS 2.x compatibility mode -  
2313             reg3 = new byte[5];
2314             System.arraycopy (ksnImage, 5, reg3, 0, reg3.length);
2315        } else {
2316            reg3 = ksn.getTransactionCounterBytes();
2317        }
2318        reg3 = and(reg3, _1FFFFF);
2319        byte[] shiftr = _100000;
2320        byte[] temp;
2321        byte[] tksnr;
2322        byte[] r8a;
2323        byte[] r8b;
2324        byte[] curkeyL = new byte[8];
2325        byte[] curkeyR = new byte[8];
2326        smidr = and(smidr, _E00000, 5);
2327
2328        do
2329        {
2330            temp = and(shiftr, reg3);
2331            if (notZero(temp))
2332            {
2333                System.arraycopy(curkey, 0, curkeyL, 0, 8);
2334                System.arraycopy(curkey, 8, curkeyR, 0, 8);
2335
2336                or(smidr, shiftr, 5);
2337                // smidr == R8
2338
2339                tksnr = ISOUtil.xor(smidr, curkeyR);
2340                tksnr = encrypt64(tksnr, curkeyL);
2341                tksnr = ISOUtil.xor(tksnr, curkeyR);
2342                // tksnr == R8A
2343                curkeyL = ISOUtil.xor(curkeyL, _VARIANT_RIGHT_HALF);
2344                curkeyR = ISOUtil.xor(curkeyR, _VARIANT_RIGHT_HALF);
2345
2346                r8b = ISOUtil.xor(smidr, curkeyR);
2347                r8b = encrypt64(r8b, curkeyL);
2348                r8b = ISOUtil.xor(r8b, curkeyR);
2349
2350                System.arraycopy(r8b, 0, curkey, 0, 8);
2351                System.arraycopy(tksnr, 0, curkey, 8, 8);
2352            }
2353            shr(shiftr);
2354        }
2355        while (notZero(shiftr));
2356
2357        if (dataEncryption) {
2358            curkey[5] ^= 0xFF;
2359            curkey[13] ^= 0xFF;
2360            System.arraycopy(curkey, 0, curkeyL, 0, 8);
2361            System.arraycopy(curkey, 8, curkeyR, 0, 8);
2362            byte[] L = encrypt64(curkeyL, curkeyL);
2363            L = decrypt64(L, curkeyR);
2364            L = encrypt64(L, curkeyL);
2365
2366            byte[] R = encrypt64(curkeyR, curkeyL); // this is the right implementation
2367            R = decrypt64(R, curkeyR);
2368            R = encrypt64(R, curkeyL);
2369            System.arraycopy (L, 0, curkey, 0, 8);
2370            System.arraycopy (R, 0, curkey, 8, 8);
2371        } else {
2372            curkey[7] ^= 0xFF;
2373            curkey[15] ^= 0xFF;
2374        }
2375        return curkey;
2376    }
2377
2378    public SecureDESKey importBDK(String clearComponent1HexString,
2379                                  String clearComponent2HexString,
2380                                  String clearComponent3HexString) throws SMException
2381    {
2382        return formKEYfromThreeClearComponents((short) 128, "BDK",
2383                clearComponent1HexString,
2384                clearComponent2HexString,
2385                clearComponent3HexString);
2386    }
2387
2388    private KeySerialNumber getKSN(byte[] b) {
2389        ByteBuffer buf = ByteBuffer.allocate(10);
2390        buf.put(b, 0, 10);
2391        return new KeySerialNumber (buf.array());
2392    }
2393
2394    protected EncryptedPIN translatePINImpl
2395            (EncryptedPIN pinUnderDuk, KeySerialNumber ksn,
2396             SecureDESKey bdk, SecureDESKey kd2, byte destinationPINBlockFormat,boolean tdes)
2397            throws SMException
2398    {
2399        byte[] derivedKey = calculateDerivedKey(ksn, bdk, tdes, false);
2400        byte[] clearPinblk = specialDecrypt(
2401                pinUnderDuk.getPINBlock(), derivedKey
2402        );
2403
2404        String pan = pinUnderDuk.getAccountNumber();
2405        String pin = calculatePIN(
2406                clearPinblk, pinUnderDuk.getPINBlockFormat(), pan
2407        );
2408        byte[] translatedPinblk = jceHandler.encryptData(
2409                calculatePINBlock(pin, destinationPINBlockFormat, pan),
2410                decryptFromLMK(kd2)
2411        );
2412        return new EncryptedPIN(translatedPinblk, destinationPINBlockFormat, pan,false);
2413    }
2414    protected EncryptedPIN importPINImpl
2415            (EncryptedPIN pinUnderDuk, KeySerialNumber ksn, SecureDESKey bdk,boolean tdes)
2416            throws SMException
2417    {
2418        byte[] derivedKey = calculateDerivedKey(ksn, bdk,tdes, false);
2419        byte[] clearPinblk = specialDecrypt(
2420                pinUnderDuk.getPINBlock(), derivedKey
2421        );
2422        String pan = pinUnderDuk.getAccountNumber();
2423        String pin = calculatePIN(
2424                clearPinblk, pinUnderDuk.getPINBlockFormat(), pan
2425        );
2426
2427        byte[] pinUnderLmk = jceHandler.encryptData(
2428                calculatePINBlock(pin, SMAdapter.FORMAT00, pan),
2429                getLMK(PINLMKIndex)
2430        );
2431        return new EncryptedPIN(pinUnderLmk, SMAdapter.FORMAT00, pan,false);
2432    }
2433
2434    /**
2435     * Exports PIN to DUKPT Encryption.
2436     *
2437     * @param pinUnderLmk
2438     * @param ksn
2439     * @param bdk
2440     * @param tdes
2441     * @param destinationPINBlockFormat
2442     * @return The encrypted pin
2443     * @throws SMException
2444     */
2445    public EncryptedPIN exportPIN
2446            (EncryptedPIN pinUnderLmk, KeySerialNumber ksn, SecureDESKey bdk, boolean tdes,
2447             byte destinationPINBlockFormat)
2448            throws SMException
2449    {
2450        String accountNumber = pinUnderLmk.getAccountNumber();
2451        // process
2452        // get clear PIN
2453        byte[] clearPINBlock = jceHandler.decryptData(pinUnderLmk.getPINBlock(),
2454                                                      getLMK(PINLMKIndex));
2455        // extract clear pin
2456        String pin = calculatePIN(clearPINBlock, pinUnderLmk.getPINBlockFormat(),
2457                                  accountNumber);
2458
2459        clearPINBlock = calculatePINBlock(pin, destinationPINBlockFormat, accountNumber);
2460
2461        // encrypt PIN
2462        byte[] derivedKey = calculateDerivedKey(ksn, bdk, tdes, false);
2463        byte[] translatedPINBlock = specialEncrypt(clearPINBlock, derivedKey);
2464
2465        return new EncryptedPIN(translatedPINBlock, destinationPINBlockFormat,
2466                                accountNumber, false);
2467    }
2468
2469    /**
2470     * places a length indicator in a byte array.
2471     * @param b the byte array
2472     * @return a byte array with a two-byte length indicator
2473     */
2474    private byte[] lpack (byte[] b) {
2475        int l = b.length;
2476        int adjustedLen = ((l+9) >> 3) << 3;
2477        byte[] d = new byte[adjustedLen];
2478        System.arraycopy (b, 0, d, 2, l);
2479        d[0] = (byte) ((l >> 8) & 0xFF);
2480        d[1] = (byte) (l & 0xFF);
2481        return d;
2482    }
2483    /**
2484     * Unpacks a byte array packed by lPack
2485     * into the former byte[]
2486     * @param b packed byte array
2487     * @return original (unpacked) byte array
2488     */
2489    private byte[] lunpack (byte[] b) {
2490        int l = ((((int)b[0])&0xFF) << 8) | (((int)b[1])&0xFF);
2491        byte[] d = new byte[l];
2492        System.arraycopy (b,2,d,0,l);
2493        return d;
2494    }
2495
2496    private byte[] hash8 (byte[][] bb) throws SMException {
2497        try {
2498            MessageDigest md = MessageDigest.getInstance("SHA");
2499            for (byte[] b : bb) {
2500                md.update(b);
2501            }
2502            return Arrays.copyOf(md.digest(), 8);
2503        } catch (NoSuchAlgorithmException e) {
2504            throw new SMException (e);
2505        }
2506    }
2507}