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