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;
020
021import org.jpos.iso.ISOUtil;
022import org.jpos.util.Loggeable;
023import org.jpos.iso.ISOException;
024
025import java.io.PrintStream;
026import java.io.Serializable;
027import java.security.InvalidParameterException;
028
029
030/**
031 * <p>
032 * The PIN (Personal Identification Number), is used to authenticate card
033 * holders. A user enters his/her PIN on the pin-pad (also called pin entry device)
034 * of a terminal (whether ATM or POS). The terminal forms the PIN Block, which
035 * is a mix of the PIN and the account number.<br>
036 * In a typical environment, the PIN Block (not the PIN) gets encrypted and sent
037 * to the acquirer. This Encrypted PIN Block is the typical content of the
038 * PIN Data ISO Field (Field 52).
039 * This class represents an encrypted PIN, no matter by whom it is encrypted.
040 * Typically a PIN is encrypted under one of these three:<br>
041 * 1- Under a terminal PIN key (like TPK or DUKPT)<br>
042 * 2- Under an Interchange PIN key (like ZPK)<br>
043 * 3- Under the the security module itself (i.e. under LMK)<br>
044 * This class knows nothing about, who encrypted it.
045 * </p>
046 * <p>
047 * This class represents an encrypted PIN using:<br>
048 * 1- The PIN Block (encrypted)<br>
049 * 2- The account number (the 12 right-most digits of the account number excluding the check digit)<br>
050 * 3- The PIN Block Format<br>
051 * </p>
052 * <p>
053 * The PIN Block Format specifies how the clear pin (as entered by the card holder)
054 * and the account number get mixed to form the PIN Block.<br>
055 * </p>
056 * @author Hani Samuel Kirollos
057 * @version $Revision$ $Date$
058 * @see SMAdapter
059 */
060public class EncryptedPIN
061        implements Serializable, Loggeable {
062
063    private static final long serialVersionUID = -9117335317030664867L;
064    /**
065     * Account Number (the 12 right-most digits of the account number excluding the check digit)
066     */
067    String accountNumber;
068    /**
069     * This is the ByteArray holding the PIN Block
070     * The PIN Block can be either clear or encrypted
071     * It is typically DES or 3DES encrypted, with length 8 bytes.
072     */
073    byte[] pinBlock;
074    /**
075     * The PIN Block Format
076     * value -1 means no block format defined
077     */
078    byte pinBlockFormat;
079
080    /** Default constructor. Creates an empty EncryptedPIN. */
081    public EncryptedPIN () {
082        super();
083    }
084
085    /**
086     * Constructs an EncryptedPIN from raw PIN block bytes.
087     * @param pinBlock the encrypted PIN block bytes
088     * @param pinBlockFormat the PIN block format identifier
089     * @param accountNumber account number, including BIN and the check digit
090     */
091    public EncryptedPIN (byte[] pinBlock, byte pinBlockFormat, String accountNumber) {
092        setPINBlock(pinBlock);
093        setPINBlockFormat(pinBlockFormat);
094        setAccountNumber(extractAccountNumberPart(accountNumber));
095    }
096    /**
097     * Constructs an EncryptedPIN from raw PIN block bytes, optionally extracting the account number part.
098     * @param pinBlock the encrypted PIN block bytes
099     * @param pinBlockFormat the PIN block format identifier
100     * @param accountNumber if {@code extract} is false then account number, including BIN and the check digit;
101     *        if {@code extract} is true then 12 right-most digits of the account number, excluding the check digit
102     * @param extract true to extract 12 right-most digits off the account number
103     */
104    public EncryptedPIN (byte[] pinBlock, byte pinBlockFormat, String accountNumber, boolean extract) {
105        setPINBlock(pinBlock);
106        setPINBlockFormat(pinBlockFormat);
107        setAccountNumber(extract ? extractAccountNumberPart(accountNumber) : accountNumber);
108    }
109
110
111    /**
112     * Constructs an EncryptedPIN from a hex-encoded PIN block string.
113     * @param pinBlockHexString the PIN block represented as a hex string instead of a byte[]
114     * @param pinBlockFormat the PIN block format identifier
115     * @param accountNumber account number, including BIN and the check digit
116     */
117    public EncryptedPIN (String pinBlockHexString, byte pinBlockFormat, String accountNumber) {
118        this(ISOUtil.hex2byte(pinBlockHexString), pinBlockFormat, accountNumber);
119    }
120    /**
121     * Constructs an EncryptedPIN from a hex-encoded PIN block string, optionally extracting the account number part.
122     * @param pinBlockHexString the PIN block represented as a hex string instead of a byte[]
123     * @param pinBlockFormat the PIN block format identifier
124     * @param accountNumber if {@code extract} is false then account number, including BIN and the check digit;
125     *        if {@code extract} is true then 12 right-most digits of the account number, excluding the check digit
126     * @param extract true to extract 12 right-most digits off the account number
127     */
128    public EncryptedPIN (String pinBlockHexString, byte pinBlockFormat, String accountNumber, boolean extract) {
129        this(ISOUtil.hex2byte(pinBlockHexString), pinBlockFormat, accountNumber, extract);
130    }
131
132    /**
133     * dumps PIN basic information
134     * @param p a PrintStream usually supplied by Logger
135     * @param indent indention string, usually suppiled by Logger
136     * @see org.jpos.util.Loggeable
137     */
138    public void dump (PrintStream p, String indent) {
139        String inner = indent + "  ";
140        p.print(indent + "<encrypted-pin");
141        p.print(" format=\"0" + getPINBlockFormat() + "\"");
142        p.println(">");
143        p.println(inner + "<pin-block>" + ISOUtil.hexString(getPINBlock()) + "</pin-block>");
144        p.println(inner + "<account-number>" + getAccountNumber() + "</account-number>");
145        p.println(indent + "</encrypted-pin>");
146    }
147
148    /**
149     * Sets the encrypted PIN block bytes.
150     * @param pinBlock the encrypted PIN block bytes
151     */
152    public void setPINBlock (byte[] pinBlock) {
153        this.pinBlock = pinBlock;
154    }
155
156    /**
157     * Returns the encrypted PIN block bytes.
158     * @return the encrypted PIN block bytes
159     */
160    public byte[] getPINBlock () {
161        return  pinBlock;
162    }
163
164    /**
165     * Sets the PIN block format identifier.
166     * @param pinBlockFormat the PIN block format identifier
167     */
168    public void setPINBlockFormat (byte pinBlockFormat) {
169        this.pinBlockFormat = pinBlockFormat;
170    }
171
172    /**
173     * Returns the PIN block format identifier.
174     * @return the PIN block format identifier
175     */
176    public byte getPINBlockFormat () {
177        return  this.pinBlockFormat;
178    }
179
180    /**
181     * Sets the 12 right-most digits of the account number excluding the check digit
182     * @param extractedAccountNumber  12 right-most digits of the account number, excluding the check digit.
183     */
184    public void setAccountNumber (String extractedAccountNumber) {
185        if(extractedAccountNumber.length() != 12)
186           throw new InvalidParameterException(
187               "Extracted account number length should be 12, got '"
188              +ISOUtil.protect(extractedAccountNumber) + "'"
189           );
190        this.accountNumber = extractedAccountNumber;
191    }
192
193    /**
194     * Returns the extracted account number part.
195     * @return the 12 right-most digits of the account number, excluding the check digit
196     */
197    public String getAccountNumber () {
198        return  accountNumber;
199    }
200
201    /**
202     * This method extracts the 12 right-most digits of the account number,
203     * execluding the check digit.
204     * @param accountNumber (PAN) consists of the BIN (Bank Identification Number), accountNumber
205     * and a check digit.
206     * @return the 12 right-most digits of the account number, execluding the check digit.
207     *         In case if account number length is lower that 12 proper count of 0 digts is added
208     *         on the left side for align to 12
209     */
210    public static String extractAccountNumberPart (String accountNumber) {
211        String accountNumberPart = null;
212        try {
213            accountNumberPart = ISOUtil.takeLastN(accountNumber, 13);
214            accountNumberPart = ISOUtil.takeFirstN(accountNumberPart, 12);
215        } catch(ISOException ignored) {
216            // NOPMD return original accountNumber
217        }
218        return  accountNumberPart;
219    }
220
221
222}
223
224
225