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.emv.cryptogram;
020
021import org.jpos.emv.EMVStandardTagType;
022import org.jpos.emv.IssuerApplicationData;
023import org.jpos.iso.ISOUtil;
024import org.jpos.tlv.TLVList;
025
026import java.util.*;
027
028
029/**
030 * Interface that provides methods to build strings for ARPC and ARQC generation
031 *
032 * @author Rainer Reyes
033 */
034public interface CryptogramDataBuilder {
035
036    enum PaddingMethod {
037        NO_PADDING,
038
039        /**
040         * ISO/IEC 9797-1 padding method 1
041         * for Block size 8,  n = 64
042         */
043        ISO9797Method1 {
044            @Override
045            public String apply(String data) {
046                return data.isEmpty() ?
047                        "0000000000000000" :
048                        ISOUtil.zeropadRight(data, data.length() % 16 == 0 ? data.length() : data.length() + 16 - data.length() % 16);
049            }
050        },
051
052        /**
053         * ISO/IEC 9797-1 padding method 2
054         * for Block size 8,  n = 64
055         */
056        ISO9797Method2 {
057            @Override
058            public String apply(String data) {
059                return ISO9797Method1.apply(data + "80");
060            }
061        },
062
063        /**
064         * ISO/IEC 9797-1 padding method 3
065         * for Block size 8,  n = 64
066         */
067        ISO9797Method3 {
068            @Override
069            public String apply(String data) {
070                StringBuilder sb = new StringBuilder();
071                String D = ISO9797Method1.apply(data);
072                String Ld = ISOUtil.byte2hex(ISOUtil.int2byte(data.length() / 2));
073                String Lp = ISO9797Method1.apply(Ld);
074                Lp = Ld.length() % 16 == 0 ? "" : Lp.substring(Ld.length());
075                return sb.append(Lp).append(Ld).append(D).toString();
076            }
077        };
078
079        public String apply(String data) {
080            return data;
081        }
082    }
083
084    /**
085     * Method that selects the  minimum set of data elements recommended for
086     * the generation of application cryptograms described in EMV Book 2 sec 8.1.1
087     *
088     * @param data ICC data
089     * @return Minimum Set of Data Elements for Application Cryptogram Generation
090     */
091    static List<String> minimumSetOfDataElement(TLVList data) {
092        return Arrays.asList(
093                data.getString(EMVStandardTagType.AMOUNT_AUTHORISED_NUMERIC_0x9F02.getTagNumber()),
094                Optional.ofNullable(data.getString(EMVStandardTagType.AMOUNT_OTHER_NUMERIC_0x9F03.getTagNumber()))
095                        .orElse("000000000000"),
096                data.getString(EMVStandardTagType.TERMINAL_COUNTRY_CODE_0x9F1A.getTagNumber()),
097                data.getString(EMVStandardTagType.TERMINAL_VERIFICATION_RESULTS_0x95.getTagNumber()),
098                data.getString(EMVStandardTagType.TRANSACTION_CURRENCY_CODE_0x5F2A.getTagNumber()),
099                data.getString(EMVStandardTagType.TRANSACTION_DATE_0x9A.getTagNumber()),
100                data.getString(EMVStandardTagType.TRANSACTION_TYPE_0x9C.getTagNumber()),
101                data.getString(EMVStandardTagType.UNPREDICTABLE_NUMBER_0x9F37.getTagNumber()),
102                data.getString(EMVStandardTagType.APPLICATION_INTERCHANGE_PROFILE_0x82.getTagNumber()),
103                data.getString(EMVStandardTagType.APPLICATION_TRANSACTION_COUNTER_0x9F36.getTagNumber())
104        );
105    }
106
107    /**
108     * Method that returns default issuer response data (ARC or CSU)
109     *
110     * @param approved true if transaction was approved, otherwise false
111     * @return String representing  default issuer response data that will be used to generate the ARPC
112     */
113    String getDefaultARPCRequest(boolean approved);
114
115    /**
116     * Select necessary data elements and create the string used to generate the ARQC with no padding
117     * <p>
118     *
119     * @param data ICC data received
120     * @param iad  Issuer application Data
121     * @return String used to generate the ARQC
122     */
123    String buildARQCRequest(TLVList data, IssuerApplicationData iad);
124
125    /**
126     * Select necessary data elements and create the string used to generate the ARQC with padding
127     * <p>
128     *
129     * @param data          ICC data received
130     * @param iad           Issuer application Data
131     * @return String used to generate the ARQC
132     */
133    default String buildARQCRequest_padded(TLVList data, IssuerApplicationData iad) {
134        return getPaddingMethod().apply(buildARQCRequest(data, iad));
135    }
136
137    /** Defines how to pad the request data when generating the ARQC.
138     * @return PaddingMethod this builder uses
139     */
140    PaddingMethod getPaddingMethod();
141
142}