├── .gitignore
├── lib
├── api.jar
├── gpj.jar
├── jctasks.jar
├── converter.jar
├── offcardverifier.jar
└── api_export_files
│ ├── java
│ ├── io
│ │ └── javacard
│ │ │ └── io.exp
│ ├── rmi
│ │ └── javacard
│ │ │ └── rmi.exp
│ └── lang
│ │ └── javacard
│ │ └── lang.exp
│ ├── javacardx
│ └── crypto
│ │ └── javacard
│ │ └── crypto.exp
│ └── javacard
│ ├── security
│ └── javacard
│ │ └── security.exp
│ └── framework
│ ├── javacard
│ └── framework.exp
│ └── service
│ └── javacard
│ └── service.exp
├── README.md
├── .classpath
├── .jcop
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── src
└── openpgpcard
│ ├── PGPKey.java
│ ├── OpenPGPSecureMessaging.java
│ └── OpenPGPApplet.java
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 |
--------------------------------------------------------------------------------
/lib/api.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/api.jar
--------------------------------------------------------------------------------
/lib/gpj.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/gpj.jar
--------------------------------------------------------------------------------
/lib/jctasks.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/jctasks.jar
--------------------------------------------------------------------------------
/lib/converter.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/converter.jar
--------------------------------------------------------------------------------
/lib/offcardverifier.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/offcardverifier.jar
--------------------------------------------------------------------------------
/lib/api_export_files/java/io/javacard/io.exp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/api_export_files/java/io/javacard/io.exp
--------------------------------------------------------------------------------
/lib/api_export_files/java/rmi/javacard/rmi.exp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/api_export_files/java/rmi/javacard/rmi.exp
--------------------------------------------------------------------------------
/lib/api_export_files/java/lang/javacard/lang.exp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/api_export_files/java/lang/javacard/lang.exp
--------------------------------------------------------------------------------
/lib/api_export_files/javacardx/crypto/javacard/crypto.exp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/api_export_files/javacardx/crypto/javacard/crypto.exp
--------------------------------------------------------------------------------
/lib/api_export_files/javacard/security/javacard/security.exp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/api_export_files/javacard/security/javacard/security.exp
--------------------------------------------------------------------------------
/lib/api_export_files/javacard/framework/javacard/framework.exp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/api_export_files/javacard/framework/javacard/framework.exp
--------------------------------------------------------------------------------
/lib/api_export_files/javacard/framework/service/javacard/service.exp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jderuiter/javacard-openpgpcard/HEAD/lib/api_export_files/javacard/framework/service/javacard/service.exp
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **This repository moved to https://codeberg.org/cypherpunk/javacard-openpgpcard**
2 |
3 | # Java Card OpenPGP Card
4 |
5 | This is a Java Card implementation of the OpenPGP smart card specifications.
6 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.jcop:
--------------------------------------------------------------------------------
1 | 1.0D276000124011.0
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | OpenPGP JavaCard
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | com.ibm.bluez.jcop.eclipse.jcopbuilder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | com.ibm.bluez.jcop.eclipse.jcopnature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.1
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.3
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=ignore
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=ignore
11 | org.eclipse.jdt.core.compiler.source=1.3
12 |
--------------------------------------------------------------------------------
/src/openpgpcard/PGPKey.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java Card implementation of the OpenPGP card
3 | * Copyright (C) 2011 Joeri de Ruiter
4 | *
5 | * This program is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU General Public License
7 | * as published by the Free Software Foundation; either version 2
8 | * of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 | */
19 | package openpgpcard;
20 |
21 | import javacard.framework.*;
22 | import javacard.security.*;
23 |
24 | /**
25 | * @author Joeri de Ruiter (joeri@cs.ru.nl)
26 | * @version $Revision: 12 $ by $Author: joeridr $
27 | * $LastChangedDate: 2012-02-23 15:31:33 +0100 (Thu, 23 Feb 2012) $
28 | */
29 | public class PGPKey implements ISO7816 {
30 | public static final short KEY_SIZE = 2048;// 2368;
31 | public static final short KEY_SIZE_BYTES = KEY_SIZE / 8;
32 | public static final short EXPONENT_SIZE = 17;
33 | public static final short EXPONENT_SIZE_BYTES = 3;
34 | public static final short FP_SIZE = 20;
35 |
36 | private KeyPair key;
37 | private byte[] fp;
38 | private byte[] time = { 0x00, 0x00, 0x00, 0x00 };
39 | private byte[] attributes = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x02 };
40 |
41 | public PGPKey() {
42 | key = new KeyPair(KeyPair.ALG_RSA_CRT, KEY_SIZE);
43 |
44 | fp = new byte[FP_SIZE];
45 | Util.arrayFillNonAtomic(fp, (short) 0, (short) fp.length, (byte) 0);
46 |
47 | Util.setShort(attributes, (short) 1, KEY_SIZE);
48 | Util.setShort(attributes, (short) 3, EXPONENT_SIZE);
49 | }
50 |
51 | /**
52 | * Generate the key pair.
53 | */
54 | public void genKeyPair() {
55 | key.genKeyPair();
56 | }
57 |
58 | /**
59 | * Set the fingerprint for the public key.
60 | *
61 | * @param data
62 | * Byte array
63 | * @param offset
64 | * Offset within byte array containing first byte
65 | */
66 | public void setFingerprint(byte[] data, short offset) {
67 | // Check whether there are enough bytes to copy
68 | if ((short) (offset + fp.length) > data.length)
69 | ISOException.throwIt(SW_UNKNOWN);
70 |
71 | Util.arrayCopy(data, offset, fp, (short) 0, (short) fp.length);
72 | }
73 |
74 | /**
75 | * Set the generation time for the key pair.
76 | *
77 | * @param data
78 | * Byte array
79 | * @param offset
80 | * Offset within byte array containing first byte
81 | */
82 | public void setTime(byte[] data, short offset) {
83 | // Check whether there are enough bytes to copy
84 | if ((short) (offset + time.length) > data.length)
85 | ISOException.throwIt(SW_UNKNOWN);
86 |
87 | Util.arrayCopy(data, offset, time, (short) 0, (short) 4);
88 | }
89 |
90 | /**
91 | * Get the fingerprint for the public key.
92 | *
93 | * @param data
94 | * Byte array
95 | * @param offset
96 | * Offset within byte array indicating first byte
97 | */
98 | public short getFingerprint(byte[] data, short offset) {
99 | Util.arrayCopyNonAtomic(fp, (short) 0, data, offset, (short) fp.length);
100 | return (short) (offset + fp.length);
101 | }
102 |
103 | /**
104 | * Get the generation time for the key pair.
105 | *
106 | * @param data
107 | * Byte array
108 | * @param offset
109 | * Offset within byte array indicating first byte
110 | */
111 | public short getTime(byte[] data, short offset) {
112 | Util.arrayCopyNonAtomic(time, (short) 0, data, offset,
113 | (short) time.length);
114 | return (short) (offset + time.length);
115 | }
116 |
117 | /**
118 | * Get the algorithm attributes for the key pair.
119 | *
120 | * @param data
121 | * Byte array
122 | * @param offset
123 | * Offset within byte array indicating first byte
124 | */
125 | public short getAttributes(byte[] data, short offset) {
126 | Util.arrayCopyNonAtomic(attributes, (short) 0, data, offset,
127 | (short) attributes.length);
128 | return (short) (offset + attributes.length);
129 | }
130 |
131 | /**
132 | * @return Public key of the key pair
133 | */
134 | public RSAPublicKey getPublic() {
135 | return (RSAPublicKey) key.getPublic();
136 | }
137 |
138 | /**
139 | * @return Private key of the key pair
140 | */
141 | public RSAPrivateCrtKey getPrivate() {
142 | return (RSAPrivateCrtKey) key.getPrivate();
143 | }
144 |
145 | /**
146 | * @return Length in bytes of the exponent
147 | */
148 | public short getExponentLength() {
149 | // Fixed value of 65537 for exponent
150 | return EXPONENT_SIZE_BYTES;
151 | }
152 |
153 | /**
154 | * @return Length in bytes of the modulus
155 | */
156 | public short getModulusLength() {
157 | return KEY_SIZE_BYTES;
158 | }
159 |
160 | /**
161 | * Sets the value of the DP1 parameter. The plain text data format is
162 | * big-endian and right-aligned (the least significant bit is the least
163 | * significant bit of last byte). Input DP1 parameter data is copied into
164 | * the internal representation.
165 | *
166 | * @param buffer
167 | * The input buffer
168 | * @param offset
169 | * The offset into the input buffer at which the parameter value
170 | * begins
171 | * @param length
172 | * The length of the parameter
173 | */
174 | public void setDP1(byte[] buffer, short offset, short length) {
175 | ((RSAPrivateCrtKey) key.getPrivate()).setDP1(buffer, offset, length);
176 | }
177 |
178 | /**
179 | * Sets the value of the DQ1 parameter. The plain text data format is
180 | * big-endian and right-aligned (the least significant bit is the least
181 | * significant bit of last byte). Input DQ1 parameter data is copied into
182 | * the internal representation.
183 | *
184 | * @param buffer
185 | * The input buffer
186 | * @param offset
187 | * The offset into the input buffer at which the parameter value
188 | * begins
189 | * @param length
190 | * The length of the parameter
191 | */
192 | public void setDQ1(byte[] buffer, short offset, short length) {
193 | ((RSAPrivateCrtKey) key.getPrivate()).setDQ1(buffer, offset, length);
194 | }
195 |
196 | /**
197 | * Sets the value of the P parameter. The plain text data format is
198 | * big-endian and right-aligned (the least significant bit is the least
199 | * significant bit of last byte). Input P parameter data is copied into the
200 | * internal representation.
201 | *
202 | * @param buffer
203 | * The input buffer
204 | * @param offset
205 | * The offset into the input buffer at which the parameter value
206 | * begins
207 | * @param length
208 | * The length of the parameter
209 | */
210 | public void setP(byte[] buffer, short offset, short length) {
211 | ((RSAPrivateCrtKey) key.getPrivate()).setP(buffer, offset, length);
212 | }
213 |
214 | /**
215 | * Sets the value of the PQ parameter. The plain text data format is
216 | * big-endian and right-aligned (the least significant bit is the least
217 | * significant bit of last byte). Input PQ parameter data is copied into the
218 | * internal representation.
219 | *
220 | * @param buffer
221 | * The input buffer
222 | * @param offset
223 | * The offset into the input buffer at which the parameter value
224 | * begins
225 | * @param length
226 | * The length of the parameter
227 | */
228 | public void setPQ(byte[] buffer, short offset, short length) {
229 | ((RSAPrivateCrtKey) key.getPrivate()).setPQ(buffer, offset, length);
230 | }
231 |
232 | /**
233 | * Sets the value of the Q parameter. The plain text data format is
234 | * big-endian and right-aligned (the least significant bit is the least
235 | * significant bit of last byte). Input Q parameter data is copied into the
236 | * internal representation.
237 | *
238 | * @param buffer
239 | * The input buffer
240 | * @param offset
241 | * The offset into the input buffer at which the parameter value
242 | * begins
243 | * @param length
244 | * The length of the parameter
245 | */
246 | public void setQ(byte[] buffer, short offset, short length) {
247 | ((RSAPrivateCrtKey) key.getPrivate()).setQ(buffer, offset, length);
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/src/openpgpcard/OpenPGPSecureMessaging.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java Card implementation of the OpenPGP card
3 | *
4 | * Copyright (C) 2011 Joeri de Ruiter
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU General Public License
8 | * as published by the Free Software Foundation; either version 2
9 | * of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program; if not, write to the Free Software
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | *
20 | * OpenPGPSecureMessaging.java is based on OVSecureMessaging.java which is part
21 | * of OVchip-ng
22 | *
23 | * Copyright (C) Pim Vullers, Radboud University Nijmegen, February 2011.
24 | */
25 |
26 | package openpgpcard;
27 |
28 | import javacard.framework.APDU;
29 | import javacard.framework.ISO7816;
30 | import javacard.framework.ISOException;
31 | import javacard.framework.JCSystem;
32 | import javacard.framework.Util;
33 | import javacard.security.DESKey;
34 | import javacard.security.KeyBuilder;
35 | import javacard.security.Signature;
36 | import javacardx.crypto.Cipher;
37 |
38 | /**
39 | * OV secure messaging functionality.
40 | *
41 | *
OVSecureMessaging is based on PassportCrypto which is part of the
42 | * e-passport Java Card applet from the JMRTD project (http://jmrtd.org/).
43 | *
44 | * @author Pim Vullers
45 | * @version $Revision: 12 $ by $Author: joeridr $
46 | * $LastChangedDate: 2012-02-23 15:31:33 +0100 (Thu, 23 Feb 2012) $
47 | */
48 | public class OpenPGPSecureMessaging {
49 | private static final short SW_INTERNAL_ERROR = (short) 0x6D66;
50 | private static final byte[] PAD_DATA = {(byte) 0x80, 0, 0, 0, 0, 0, 0, 0};
51 | private static final short SSC_SIZE = 8;
52 | private static final short TMP_SIZE = 256;
53 | private static final short MAC_SIZE = 8;
54 | private static final short KEY_SIZE = 16;
55 | private static final byte[] EMPTY_KEY = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
56 |
57 | /**
58 | * The needed cryptographic functionality.
59 | */
60 | private Signature signer;
61 | private Signature verifier;
62 | private Cipher cipher;
63 | private Cipher decipher;
64 |
65 | /**
66 | * The needed keys.
67 | */
68 | private DESKey keyMAC;
69 | private DESKey keyENC;
70 |
71 | /**
72 | * The send sequence counter.
73 | */
74 | private byte[] ssc;
75 |
76 | /**
77 | * Storage for temporary data.
78 | */
79 | private byte[] tmp;
80 |
81 | private boolean[] ssc_set;
82 |
83 | /**
84 | * Construct a new secure messaging wrapper.
85 | */
86 | public OpenPGPSecureMessaging() {
87 | ssc = JCSystem.makeTransientByteArray(SSC_SIZE,
88 | JCSystem.CLEAR_ON_DESELECT);
89 | tmp = JCSystem.makeTransientByteArray(TMP_SIZE,
90 | JCSystem.CLEAR_ON_DESELECT);
91 | signer = Signature.getInstance(
92 | Signature.ALG_DES_MAC8_ISO9797_1_M2_ALG3, false);
93 | verifier = Signature.getInstance(
94 | Signature.ALG_DES_MAC8_ISO9797_1_M2_ALG3, false);
95 | cipher = Cipher.getInstance(
96 | Cipher.ALG_DES_CBC_ISO9797_M2, false);
97 | decipher = Cipher.getInstance(
98 | Cipher.ALG_DES_CBC_ISO9797_M2, false);
99 |
100 | keyMAC = (DESKey) KeyBuilder.buildKey(
101 | KeyBuilder.TYPE_DES_TRANSIENT_DESELECT,
102 | KeyBuilder.LENGTH_DES3_2KEY, false);
103 | keyENC = (DESKey) KeyBuilder.buildKey(
104 | KeyBuilder.TYPE_DES_TRANSIENT_DESELECT,
105 | KeyBuilder.LENGTH_DES3_2KEY, false);
106 |
107 | ssc_set = JCSystem.makeTransientBooleanArray((short)1, JCSystem.CLEAR_ON_DESELECT);
108 | ssc_set[0] = false;
109 | }
110 |
111 | /**
112 | * Set the MAC and encryption (and decryption) session keys. Each key is a
113 | * 16 byte 3DES EDE key. This method may be called at any time and will
114 | * immediately replace the session key.
115 | *
116 | * @param buffer byte array containing the session keys.
117 | * @param offset location of the session keys in the buffer.
118 | */
119 | public void setSessionKeys(byte[] buffer, short offset) {
120 | // Check for empty keys
121 | if(Util.arrayCompare(buffer, (short)0, EMPTY_KEY, (short)0, KEY_SIZE) == 0 ||
122 | Util.arrayCompare(buffer, KEY_SIZE, EMPTY_KEY, (short)0, KEY_SIZE) == 0) {
123 | keyMAC.clearKey();
124 | keyENC.clearKey();
125 | }
126 | else {
127 | keyMAC.setKey(buffer, offset);
128 | keyENC.setKey(buffer, (short) (offset + KEY_SIZE));
129 |
130 | signer.init(keyMAC, Signature.MODE_SIGN);
131 | verifier.init(keyMAC, Signature.MODE_VERIFY);
132 |
133 | cipher.init(keyENC, Cipher.MODE_ENCRYPT);
134 | decipher.init(keyENC, Cipher.MODE_DECRYPT);
135 | }
136 | }
137 |
138 | /**
139 | * Set the MAC session key. Each key is a 16 byte 3DES EDE key. This method
140 | * may be called at any time and will immediately replace the session key.
141 | *
142 | * @param buffer byte array containing the session key.
143 | * @param offset location of the session key in the buffer.
144 | */
145 | public void setSessionKeyMAC(byte[] buffer, short offset) {
146 | // Check for empty keys
147 | if(Util.arrayCompare(buffer, (short)0, EMPTY_KEY, (short)0, KEY_SIZE) == 0) {
148 | keyMAC.clearKey();
149 | keyENC.clearKey();
150 | }
151 | else {
152 | keyMAC.setKey(buffer, offset);
153 |
154 | signer.init(keyMAC, Signature.MODE_SIGN);
155 | verifier.init(keyMAC, Signature.MODE_VERIFY);
156 | }
157 | }
158 |
159 | /**
160 | * Set the encryption session key. Each key is a 16 byte 3DES EDE key. This method
161 | * may be called at any time and will immediately replace the session key.
162 | *
163 | * @param buffer byte array containing the session key.
164 | * @param offset location of the session key in the buffer.
165 | */
166 | public void setSessionKeyEncryption(byte[] buffer, short offset) {
167 | // Check for empty keys
168 | if(Util.arrayCompare(buffer, (short)0, EMPTY_KEY, (short)0, KEY_SIZE) == 0) {
169 | keyMAC.clearKey();
170 | keyENC.clearKey();
171 | }
172 | else {
173 | keyENC.setKey(buffer, (short) (offset + KEY_SIZE));
174 |
175 | cipher.init(keyENC, Cipher.MODE_ENCRYPT);
176 | decipher.init(keyENC, Cipher.MODE_DECRYPT);
177 | }
178 | }
179 |
180 | /**
181 | * Set the MAC and encryption (and decryption) 3DES session keys to zero.
182 | */
183 | public void clearSessionKeys() {
184 | keyMAC.clearKey();
185 | keyENC.clearKey();
186 | }
187 |
188 | /**
189 | * Unwraps (verify and decrypt) the command APDU located in the APDU buffer.
190 | * The command buffer has to be filled by the APDU.setIncomingAndReceive()
191 | * method beforehand. The verified and decrypted command data get placed at
192 | * the start of the APDU buffer.
193 | *
194 | * @return the length value encoded by DO97, 0 if this object is missing.
195 | */
196 | public short unwrapCommandAPDU() {
197 | byte[] buf = APDU.getCurrentAPDUBuffer();
198 | short apdu_p = (short) (ISO7816.OFFSET_CDATA & 0xff);
199 | short start_p = apdu_p;
200 | short le = 0;
201 | short do87DataLen = 0;
202 | short do87Data_p = 0;
203 | short do87LenBytes = 0;
204 | short hdrLen = 4;
205 | short hdrPadLen = (short) (8 - hdrLen);
206 |
207 | incrementSSC();
208 |
209 | if (buf[apdu_p] == (byte) 0x87) {
210 | apdu_p++;
211 | // do87
212 | if ((buf[apdu_p] & 0xff) > 0x80) {
213 | do87LenBytes = (short) (buf[apdu_p] & 0x7f);
214 | apdu_p++;
215 | } else {
216 | do87LenBytes = 1;
217 | }
218 | if (do87LenBytes > 2) { // sanity check
219 | ISOException.throwIt(SW_INTERNAL_ERROR);
220 | }
221 | for (short i = 0; i < do87LenBytes; i++) {
222 | do87DataLen += (short) ((buf[(short)(apdu_p + i)] & 0xff) << (short) ((do87LenBytes - 1 - i) * 8));
223 | }
224 | apdu_p += do87LenBytes;
225 |
226 | if (buf[apdu_p] != 1) {
227 | ISOException.throwIt(SW_INTERNAL_ERROR);
228 | }
229 | // store pointer to data and defer decrypt to after mac check (do8e)
230 | do87Data_p = (short) (apdu_p + 1);
231 | apdu_p += do87DataLen;
232 | do87DataLen--; // compensate for 0x01 marker
233 | }
234 |
235 | if (buf[apdu_p] == (byte) 0x97) {
236 | // do97
237 | if (buf[++apdu_p] != 1)
238 | ISOException.throwIt(SW_INTERNAL_ERROR);
239 | le = (short) (buf[++apdu_p] & 0xff);
240 | apdu_p++;
241 | }
242 |
243 | // do8e
244 | if (buf[apdu_p] != (byte) 0x8e) {
245 | ISOException.throwIt(SW_INTERNAL_ERROR);
246 | }
247 | if (buf[++apdu_p] != 8) {
248 | ISOException.throwIt(ISO7816.SW_DATA_INVALID);
249 | }
250 |
251 | // verify mac
252 | verifier.update(ssc, (short)0, (short)ssc.length);
253 | verifier.update(buf, (short)0, hdrLen);
254 | verifier.update(PAD_DATA, (short)0, hdrPadLen);
255 | if (!verifier.verify(buf, start_p, (short) (apdu_p - 1 - start_p), buf,
256 | (short)(apdu_p + 1), MAC_SIZE)) {
257 | ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
258 | }
259 |
260 | short lc = 0;
261 | if (do87DataLen != 0) {
262 | // decrypt data, and leave room for lc
263 | lc = decipher.doFinal(buf, do87Data_p, do87DataLen, buf,
264 | (short) (hdrLen + 1));
265 | buf[hdrLen] = (byte) (lc & 0xff);
266 | }
267 |
268 | return le;
269 | }
270 |
271 | /**
272 | * Wraps (encrypts and build MAC) the response data and places it in the
273 | * APDU buffer starting at offset 0. The buffer can be any buffer including
274 | * the APDU buffer itself. If the length is zero the buffer will not be
275 | * addressed and no response data will be present in the wrapped output.
276 | *
277 | * @param buffer byte array containing the data which needs to be wrapped.
278 | * @param offset location of the data in the buffer.
279 | * @param length of the data in the buffer (in bytes).
280 | * @param status word which has to be wrapped in the response APDU.
281 | * @return the length of the wrapped data in the buffer
282 | */
283 | public short wrapResponseAPDU(byte[] buffer, short offset, short length,
284 | short status) {
285 | byte[] apdu = APDU.getCurrentAPDUBuffer();
286 | short apdu_p = 0;
287 | // smallest multiple of 8 strictly larger than plaintextLen (length + padding)
288 | short do87DataLen = (short) ((((short) (length + 8)) / 8) * 8);
289 | // for 0x01 marker (indicating padding is used)
290 | do87DataLen++;
291 | short do87DataLenBytes = (short)(do87DataLen > 0xff? 2 : 1);
292 | short do87HeaderBytes = getApduBufferOffset(length);
293 | short do87Bytes = (short)(do87HeaderBytes + do87DataLen - 1); // 0x01 is counted twice
294 | boolean hasDo87 = length > 0;
295 |
296 | incrementSSC();
297 |
298 | short ciphertextLength=0;
299 | if(hasDo87) {
300 | // Copy the plain text to temporary buffer to avoid data corruption.
301 | Util.arrayCopyNonAtomic(buffer, offset, tmp, (short) 0, length);
302 | // Put the cipher text in the proper position.
303 | ciphertextLength = cipher.doFinal(tmp, (short) 0, length, apdu,
304 | do87HeaderBytes);
305 | }
306 | //sanity check
307 | //note that this check
308 | // (possiblyPaddedPlaintextLength != (short)(do87DataLen -1))
309 | //does not always hold because some algs do the padding in the final, some in the init.
310 | if (hasDo87 && (((short) (do87DataLen - 1) != ciphertextLength)))
311 | ISOException.throwIt(SW_INTERNAL_ERROR);
312 |
313 | if (hasDo87) {
314 | // build do87
315 | apdu[apdu_p++] = (byte) 0x87;
316 | if(do87DataLen < 0x80) {
317 | apdu[apdu_p++] = (byte)do87DataLen;
318 | } else {
319 | apdu[apdu_p++] = (byte) (0x80 + do87DataLenBytes);
320 | for(short i = (short) (do87DataLenBytes - 1); i >= 0; i--) {
321 | apdu[apdu_p++] = (byte) ((do87DataLen >>> (i * 8)) & 0xff);
322 | }
323 | }
324 | apdu[apdu_p++] = 0x01;
325 | }
326 |
327 | if(hasDo87) {
328 | apdu_p = do87Bytes;
329 | }
330 |
331 | // build do99
332 | apdu[apdu_p++] = (byte) 0x99;
333 | apdu[apdu_p++] = 0x02;
334 | Util.setShort(apdu, apdu_p, status);
335 | apdu_p += 2;
336 |
337 | // calculate and write mac
338 | signer.update(ssc, (short) 0, (short) ssc.length);
339 | signer.sign(apdu, (short) 0, apdu_p, apdu, (short) (apdu_p + 2));
340 |
341 | // write do8e
342 | apdu[apdu_p++] = (byte) 0x8e;
343 | apdu[apdu_p++] = 0x08;
344 | apdu_p += 8; // for mac written earlier
345 |
346 | return apdu_p;
347 | }
348 |
349 | /**
350 | * Increment the send sequence counter.
351 | */
352 | private void incrementSSC() {
353 | if (ssc == null || ssc.length <= 0) {
354 | return;
355 | }
356 |
357 | for (short s = (short) (ssc.length - 1); s >= 0; s--) {
358 | if ((short) ((ssc[s] & 0xff) + 1) > 0xff) {
359 | ssc[s] = 0;
360 | } else {
361 | ssc[s]++;
362 | break;
363 | }
364 | }
365 | }
366 |
367 | /***
368 | * Get the amount of space to reserve in the buffer when using secure
369 | * messaging.
370 | *
371 | * @param length length of plain text in which this offset depends.
372 | * @return the amount of space to reserve.
373 | */
374 | private short getApduBufferOffset(short length) {
375 | short do87Bytes = 2; // 0x87 length data 0x01
376 | // smallest multiple of 8 strictly larger than plaintextLen + 1
377 | // byte is probably the length of the cipher text (including do87 0x01)
378 | short do87DataLen = (short) ((((short) (length + 8) / 8) * 8) + 1);
379 |
380 | if (do87DataLen < 0x80) {
381 | do87Bytes++;
382 | } else if (do87DataLen <= 0xff) {
383 | do87Bytes += 2;
384 | } else {
385 | do87Bytes += (short) (length > 0xff ? 2 : 1);
386 | }
387 |
388 | return do87Bytes;
389 | }
390 |
391 | /**
392 | * Set the SSC
393 | *
394 | * @param buffer byte array containing the SSC
395 | * @param offset location of the data in the buffer
396 | */
397 | public void setSSC(byte[] buffer, short offset) {
398 | Util.arrayCopy(buffer, offset, ssc, (short)0, SSC_SIZE);
399 | ssc_set[0] = true;
400 | }
401 |
402 | /**
403 | * @return size in bytes of the SSC
404 | */
405 | public short getSSCSize() {
406 | return SSC_SIZE;
407 | }
408 |
409 | /**
410 | * @return boolean indicating whether SSC has been set
411 | */
412 | public boolean isSetSSC() {
413 | return ssc_set[0];
414 | }
415 | }
416 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | , 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------
/src/openpgpcard/OpenPGPApplet.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Java Card implementation of the OpenPGP card
3 | * Copyright (C) 2011 Joeri de Ruiter
4 | *
5 | * This program is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU General Public License
7 | * as published by the Free Software Foundation; either version 2
8 | * of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 | */
19 | package openpgpcard;
20 |
21 | import javacard.framework.*;
22 | import javacard.security.*;
23 | import javacardx.crypto.*;
24 |
25 | /**
26 | * AID of the applet should be according to the OpenPGP card standard v2.0.1
27 | * E.g. D2760001240102000000000000010000:
28 | * D276000124 - RID of FSFE
29 | * 01 - OpenPGP application
30 | * Version needs to be higher than 0.07, otherwise tags for fingerprints are of by one in gpg
31 | * 0200 - Version
32 | * 0000 - Manufacturer
33 | * 00000001 - Serial number
34 | * 0000 - RFU
35 | *
36 | * @author Joeri de Ruiter (joeri@cs.ru.nl)
37 | * @version $Revision: 13 $ by $Author: joeridr $
38 | * $LastChangedDate: 2015-04-13 16:02:31 +0200 (Mon, 13 Apr 2015) $
39 | */
40 | public class OpenPGPApplet extends Applet implements ISO7816 {
41 | //TODO Check atomicity of all storage commands
42 |
43 | private static final short _0 = 0;
44 |
45 | private static final boolean FORCE_SM_GET_CHALLENGE = true;
46 |
47 | private static final byte[] HISTORICAL = { 0x00, 0x73, 0x00, 0x00,
48 | (byte) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
49 | 0x00 };
50 |
51 | private static final byte[] EXTENDED_CAP = {
52 | (byte) 0xF0, // Support for GET CHALLENGE
53 | // Support for Key Import
54 | // PW1 Status byte changeable
55 | 0x00, // Secure messaging using 3DES
56 | 0x00, (byte) 0xFF, // Maximum length of challenges
57 | 0x00, (byte) 0xFF, // Maximum length Cardholder Certificate
58 | 0x00, (byte) 0xFF, // Maximum length command data
59 | 0x00, (byte) 0xFF // Maximum length response data
60 | };
61 |
62 | private static short RESPONSE_MAX_LENGTH = 255;
63 | private static short RESPONSE_SM_MAX_LENGTH = 231;
64 | private static short CHALLENGES_MAX_LENGTH = 255;
65 |
66 | private static short BUFFER_MAX_LENGTH = 674;
67 |
68 | private static short LOGINDATA_MAX_LENGTH = 254;
69 | private static short URL_MAX_LENGTH = 254;
70 | private static short NAME_MAX_LENGTH = 39;
71 | private static short LANG_MAX_LENGTH = 8;
72 | private static short CERT_MAX_LENGTH = 500;
73 |
74 | private static byte PW1_MIN_LENGTH = 6;
75 | private static byte PW1_MAX_LENGTH = 127;
76 | // Default PW1 '123456'
77 | private static byte[] PW1_DEFAULT = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 };
78 | private static byte PW1_MODE_NO81 = 0;
79 | private static byte PW1_MODE_NO82 = 0;
80 |
81 | private static final byte RC_MIN_LENGTH = 8;
82 | private static final byte RC_MAX_LENGTH = 127;
83 |
84 | private static final byte PW3_MIN_LENGTH = 8;
85 | private static final byte PW3_MAX_LENGTH = 127;
86 | // Default PW3 '12345678'
87 | private static final byte[] PW3_DEFAULT = { 0x31, 0x32, 0x33, 0x34, 0x35,
88 | 0x36, 0x37, 0x38 };
89 |
90 | private byte[] loginData = new byte[LOGINDATA_MAX_LENGTH];
91 | private short loginData_length = 0;
92 |
93 | private byte[] url = new byte[URL_MAX_LENGTH];
94 | private short url_length = 0;
95 |
96 | private byte[] name = new byte[NAME_MAX_LENGTH];
97 | private short name_length = 0;
98 |
99 | private byte[] lang = new byte[LANG_MAX_LENGTH];
100 | private short lang_length = 0;
101 |
102 | private byte[] cert = new byte[CERT_MAX_LENGTH];
103 | private short cert_length = 0;
104 |
105 | private byte sex = 0x39;
106 |
107 | private OwnerPIN pw1;
108 | private byte pw1_length = 0;
109 | private byte pw1_status = 0x00;
110 | private boolean[] pw1_modes;
111 |
112 | private OwnerPIN rc;
113 | private byte rc_length = 0;
114 |
115 | private OwnerPIN pw3;
116 | private byte pw3_length = 0;
117 |
118 | private byte[] ds_counter = { 0x00, 0x00, 0x00 };
119 |
120 | private PGPKey sig_key;
121 | private PGPKey dec_key;
122 | private PGPKey auth_key;
123 |
124 | private byte[] ca1_fp = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 | 0x00 };
127 | private byte[] ca2_fp = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 | 0x00 };
130 | private byte[] ca3_fp = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 | 0x00 };
133 |
134 | private Cipher cipher;
135 | private RandomData random;
136 |
137 | private byte[] tmp;
138 |
139 | private byte[] buffer;
140 | private short out_left = 0;
141 | private short out_sent = 0;
142 | private short in_received = 0;
143 |
144 | private boolean chain = false;
145 | private byte chain_ins = 0;
146 | private short chain_p1p2 = 0;
147 |
148 | private OpenPGPSecureMessaging sm;
149 | private boolean sm_success = false;
150 |
151 | public static void install(byte[] bArray, short bOffset, byte bLength) {
152 | new OpenPGPApplet().register(bArray, (short) (bOffset + 1),
153 | bArray[bOffset]);
154 | }
155 |
156 | public OpenPGPApplet() {
157 | // Create temporary arrays
158 | tmp = JCSystem.makeTransientByteArray(BUFFER_MAX_LENGTH,
159 | JCSystem.CLEAR_ON_DESELECT);
160 | buffer = JCSystem.makeTransientByteArray(BUFFER_MAX_LENGTH,
161 | JCSystem.CLEAR_ON_DESELECT);
162 | pw1_modes = JCSystem.makeTransientBooleanArray((short) 2,
163 | JCSystem.CLEAR_ON_DESELECT);
164 |
165 | // Initialize PW1 with default password
166 | pw1 = new OwnerPIN((byte) 3, PW1_MAX_LENGTH);
167 | pw1.update(PW1_DEFAULT, _0, (byte) PW1_DEFAULT.length);
168 | pw1_length = (byte) PW1_DEFAULT.length;
169 |
170 | // Initialize RC
171 | rc = new OwnerPIN((byte) 3, RC_MAX_LENGTH);
172 |
173 | // Initialize PW3 with default password
174 | pw3 = new OwnerPIN((byte) 3, PW3_MAX_LENGTH);
175 | pw3.update(PW3_DEFAULT, _0, (byte) PW3_DEFAULT.length);
176 | pw3_length = (byte) PW3_DEFAULT.length;
177 |
178 | // Create empty keys
179 | sig_key = new PGPKey();
180 | dec_key = new PGPKey();
181 | auth_key = new PGPKey();
182 |
183 | //
184 | cipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
185 | random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
186 |
187 | // Initialize Secure Messaging
188 | sm = new OpenPGPSecureMessaging();
189 | }
190 |
191 | public void process(APDU apdu) {
192 | if (selectingApplet()) {
193 | // Reset PW1 modes
194 | pw1_modes[PW1_MODE_NO81] = false;
195 | pw1_modes[PW1_MODE_NO82] = false;
196 |
197 | return;
198 | }
199 |
200 | byte[] buf = apdu.getBuffer();
201 | byte cla= buf[OFFSET_CLA];
202 | byte ins = buf[OFFSET_INS];
203 | byte p1 = buf[OFFSET_P1];
204 | byte p2 = buf[OFFSET_P2];
205 | short p1p2 = Util.makeShort(p1, p2);
206 | short lc = (short) (buf[OFFSET_LC] & 0xFF);
207 |
208 | // Secure messaging
209 | //TODO Force SM if contactless is used
210 | sm_success = false;
211 | if ((byte) (cla & (byte) 0x0C) == (byte) 0x0C) {
212 | // Force initialization of SSC before using SM to prevent replays
213 | if(FORCE_SM_GET_CHALLENGE && !sm.isSetSSC() && (ins != (byte) 0x84)) ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
214 |
215 | lc = sm.unwrapCommandAPDU();
216 | sm_success = true;
217 | }
218 |
219 | short status = SW_NO_ERROR;
220 | short le = 0;
221 |
222 | try {
223 | // Support for command chaining
224 | commandChaining(apdu);
225 |
226 | // Reset buffer for GET RESPONSE
227 | if (ins != (byte) 0xC0) {
228 | out_sent = 0;
229 | out_left = 0;
230 | }
231 |
232 | // Other instructions
233 | switch (ins) {
234 | // GET RESPONSE
235 | case (byte) 0xC0:
236 | // Will be handled in finally clause
237 | break;
238 |
239 | // VERIFY
240 | case (byte) 0x20:
241 | verify(apdu, p2);
242 | break;
243 |
244 | // CHANGE REFERENCE DATA
245 | case (byte) 0x24:
246 | changeReferenceData(apdu, p2);
247 | break;
248 |
249 | // RESET RETRY COUNTER
250 | case (byte) 0x2C:
251 | // Reset only available for PW1
252 | if (p2 != (byte) 0x81)
253 | ISOException.throwIt(SW_INCORRECT_P1P2);
254 |
255 | resetRetryCounter(apdu, p1);
256 | break;
257 |
258 | // PERFORM SECURITY OPERATION
259 | case (byte) 0x2A:
260 | // COMPUTE DIGITAL SIGNATURE
261 | if (p1p2 == (short) 0x9E9A) {
262 | le = computeDigitalSignature(apdu);
263 | }
264 | // DECIPHER
265 | else if (p1p2 == (short) 0x8086) {
266 | le = decipher(apdu);
267 | } else {
268 | ISOException.throwIt(SW_WRONG_P1P2);
269 | }
270 |
271 | break;
272 |
273 | // INTERNAL AUTHENTICATE
274 | case (byte) 0x88:
275 | le = internalAuthenticate(apdu);
276 | break;
277 |
278 | // GENERATE ASYMMETRIC KEY PAIR
279 | case (byte) 0x47:
280 | le = genAsymKey(apdu, p1);
281 | break;
282 |
283 | // GET CHALLENGE
284 | case (byte) 0x84:
285 | le = getChallenge(apdu, lc);
286 | break;
287 |
288 | // GET DATA
289 | case (byte) 0xCA:
290 | le = getData(p1p2);
291 | break;
292 |
293 | // PUT DATA
294 | case (byte) 0xDA:
295 | putData(p1p2);
296 | break;
297 |
298 | // DB - PUT DATA (Odd)
299 | case (byte) 0xDB:
300 | // Odd PUT DATA only supported for importing keys
301 | // 4D - Extended Header list
302 | if (p1p2 == (short) 0x3FFF) {
303 | importKey(apdu);
304 | } else {
305 | ISOException.throwIt(SW_RECORD_NOT_FOUND);
306 | }
307 | break;
308 |
309 | default:
310 | // good practice: If you don't know the INStruction, say so:
311 | ISOException.throwIt(SW_INS_NOT_SUPPORTED);
312 | }
313 | }
314 | catch(ISOException e) {
315 | status = e.getReason();
316 | }
317 | finally {
318 | if(status != (short)0x9000) {
319 | // Send the exception that was thrown
320 | sendException(apdu, status);
321 | }
322 | else {
323 | // GET RESPONSE
324 | if (ins == (byte) 0xC0) {
325 | sendNext(apdu);
326 | }
327 | else {
328 | sendBuffer(apdu, le);
329 | }
330 | }
331 | }
332 | }
333 |
334 | /**
335 | * Provide support for command chaining by storing the received data in
336 | * buffer
337 | *
338 | * @param apdu
339 | */
340 | private void commandChaining(APDU apdu) {
341 | byte[] buf = apdu.getBuffer();
342 | short p1p2 = Util.makeShort(buf[OFFSET_P1],
343 | buf[OFFSET_P2]);
344 | short len = (short) (buf[OFFSET_LC] & 0xFF);
345 |
346 | // Reset chaining if it was not yet initiated
347 | if (!chain)
348 | resetChaining();
349 |
350 | if ((byte) (buf[OFFSET_CLA] & (byte) 0x10) == (byte) 0x10) {
351 | // If chaining was already initiated, INS and P1P2 should match
352 | if (chain
353 | && (buf[OFFSET_INS] != chain_ins && p1p2 != chain_p1p2)) {
354 | resetChaining();
355 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
356 | }
357 |
358 | // Check whether data to be received is larger than size of the
359 | // buffer
360 | if ((short) (in_received + len) > BUFFER_MAX_LENGTH) {
361 | resetChaining();
362 | ISOException.throwIt(SW_WRONG_LENGTH);
363 | }
364 |
365 | // Store received data in buffer
366 | in_received = Util.arrayCopyNonAtomic(buf, OFFSET_CDATA,
367 | buffer, in_received, len);
368 |
369 | chain = true;
370 | chain_ins = buf[OFFSET_INS];
371 | chain_p1p2 = p1p2;
372 |
373 | ISOException.throwIt(SW_NO_ERROR);
374 | }
375 |
376 | if (chain && buf[OFFSET_INS] == chain_ins && p1p2 == chain_p1p2) {
377 | chain = false;
378 |
379 | // Check whether data to be received is larger than size of the
380 | // buffer
381 | if ((short) (in_received + len) > BUFFER_MAX_LENGTH) {
382 | resetChaining();
383 | ISOException.throwIt(SW_WRONG_LENGTH);
384 | }
385 |
386 | // Add received data to the buffer
387 | in_received = Util.arrayCopyNonAtomic(buf, OFFSET_CDATA,
388 | buffer, in_received, len);
389 | } else if (chain) {
390 | // Chained command expected
391 | resetChaining();
392 | ISOException.throwIt(SW_UNKNOWN);
393 | } else {
394 | // No chaining was used, so copy data to buffer
395 | in_received = Util.arrayCopyNonAtomic(buf, OFFSET_CDATA,
396 | buffer, _0, len);
397 | }
398 | }
399 |
400 | private void resetChaining() {
401 | chain = false;
402 | in_received = 0;
403 | }
404 |
405 | /**
406 | * Provide the VERIFY command (INS 20)
407 | *
408 | * Verify one of the passwords depending on mode: - 81: PW1 for a PSO:CDS
409 | * command - 82: PW1 for other commands - 83: PW3
410 | *
411 | * @param apdu
412 | * @param mode
413 | * Password and mode to be verified
414 | */
415 | private void verify(APDU apdu, byte mode) {
416 | if (mode == (byte) 0x81 || mode == (byte) 0x82) {
417 | // Check length of input
418 | if (in_received < PW1_MIN_LENGTH || in_received > PW1_MAX_LENGTH)
419 | ISOException.throwIt(SW_WRONG_LENGTH);
420 |
421 | // Check given PW1 and set requested mode if verified succesfully
422 | if (pw1.check(buffer, _0, (byte) in_received)) {
423 | if (mode == (byte) 0x81)
424 | pw1_modes[PW1_MODE_NO81] = true;
425 | else
426 | pw1_modes[PW1_MODE_NO82] = true;
427 | } else {
428 | ISOException
429 | .throwIt((short) (0x63C0 | pw1.getTriesRemaining()));
430 | }
431 | } else if (mode == (byte) 0x83) {
432 | // Check length of input
433 | if (in_received < PW3_MIN_LENGTH || in_received > PW3_MAX_LENGTH)
434 | ISOException.throwIt(SW_WRONG_LENGTH);
435 |
436 | // Check PW3
437 | if (!pw3.check(buffer, _0, (byte) in_received)) {
438 | ISOException
439 | .throwIt((short) (0x63C0 | pw3.getTriesRemaining()));
440 | }
441 | } else {
442 | ISOException.throwIt(SW_INCORRECT_P1P2);
443 | }
444 | }
445 |
446 | /**
447 | * Provide the CHANGE REFERENCE DATA command (INS 24)
448 | *
449 | * Change the password specified using mode: - 81: PW1 - 82: PW3
450 | *
451 | * @param apdu
452 | * @param mode
453 | * Password to be changed
454 | */
455 | private void changeReferenceData(APDU apdu, byte mode) {
456 | if (mode == (byte) 0x81) {
457 | // Check length of the new password
458 | short new_length = (short) (in_received - pw1_length);
459 | if (new_length < PW1_MIN_LENGTH || new_length > PW1_MAX_LENGTH)
460 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
461 |
462 | if (!pw1.check(buffer, _0, pw1_length))
463 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
464 |
465 | // Change PW1
466 | JCSystem.beginTransaction();
467 | pw1.update(buffer, pw1_length, (byte) new_length);
468 | pw1_length = (byte) new_length;
469 | pw1_modes[PW1_MODE_NO81] = false;
470 | pw1_modes[PW1_MODE_NO82] = false;
471 | JCSystem.commitTransaction();
472 | } else if (mode == (byte) 0x83) {
473 | // Check length of the new password
474 | short new_length = (short) (in_received - pw3_length);
475 | if (new_length < PW3_MIN_LENGTH || new_length > PW3_MAX_LENGTH)
476 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
477 |
478 | if (!pw3.check(buffer, _0, pw3_length))
479 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
480 |
481 | // Change PW3
482 | JCSystem.beginTransaction();
483 | pw3.update(buffer, pw3_length, (byte) new_length);
484 | pw3_length = (byte) new_length;
485 | JCSystem.commitTransaction();
486 | }
487 | }
488 |
489 | /**
490 | * Provide the RESET RETRY COUNTER command (INS 2C)
491 | *
492 | * Reset PW1 either using the Resetting Code (mode = 00) or PW3 (mode = 02)
493 | *
494 | * @param apdu
495 | * @param mode
496 | * Mode used to reset PW1
497 | */
498 | private void resetRetryCounter(APDU apdu, byte mode) {
499 | if (mode == (byte) 0x00) {
500 | // Authentication using RC
501 | if (rc_length == 0)
502 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
503 |
504 | short new_length = (short) (in_received - rc_length);
505 | if (new_length < PW1_MIN_LENGTH || new_length > PW1_MAX_LENGTH)
506 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
507 |
508 | if (!rc.check(buffer, _0, rc_length))
509 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
510 |
511 | // Change PW1
512 | JCSystem.beginTransaction();
513 | pw1.update(buffer, rc_length, (byte) new_length);
514 | pw1_length = (byte) new_length;
515 | JCSystem.commitTransaction();
516 | } else if (mode == (byte) 0x02) {
517 | // Authentication using PW3
518 | if (!pw3.isValidated())
519 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
520 |
521 | if (in_received < PW1_MIN_LENGTH || in_received > PW1_MAX_LENGTH)
522 | ISOException.throwIt(SW_WRONG_LENGTH);
523 |
524 | // Change PW1
525 | JCSystem.beginTransaction();
526 | pw1.update(buffer, _0, (byte) in_received);
527 | pw1_length = (byte) in_received;
528 | JCSystem.commitTransaction();
529 | } else {
530 | ISOException.throwIt(SW_WRONG_P1P2);
531 | }
532 | }
533 |
534 | /**
535 | * Provide the PSO: COMPUTE DIGITAL SIGNATURE command (INS 2A, P1P2 9E9A)
536 | *
537 | * Sign the data provided using the key for digital signatures.
538 | *
539 | * Before using this method PW1 has to be verified with mode No. 81. If the
540 | * first status byte of PW1 is 00, access condition PW1 with No. 81 is
541 | * reset.
542 | *
543 | * @param apdu
544 | * @return Length of data written in buffer
545 | */
546 | private short computeDigitalSignature(APDU apdu) {
547 | if (!(pw1.isValidated() && pw1_modes[PW1_MODE_NO81]))
548 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED);
549 |
550 | if (pw1_status == (byte) 0x00)
551 | pw1_modes[PW1_MODE_NO81] = false;
552 |
553 | if (!sig_key.getPrivate().isInitialized())
554 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
555 |
556 | // Copy data to be signed to tmp
557 | short length = Util
558 | .arrayCopyNonAtomic(buffer, _0, tmp, _0, in_received);
559 |
560 | cipher.init(sig_key.getPrivate(), Cipher.MODE_ENCRYPT);
561 | increaseDSCounter();
562 |
563 | return cipher.doFinal(tmp, _0, length, buffer, _0);
564 | }
565 |
566 | /**
567 | * Provide the PSO: DECIPHER command (INS 2A, P1P2 8086)
568 | *
569 | * Decrypt the data provided using the key for confidentiality.
570 | *
571 | * Before using this method PW1 has to be verified with mode No. 82.
572 | *
573 | * @param apdu
574 | * @return Length of data written in buffer
575 | */
576 | private short decipher(APDU apdu) {
577 | // DECIPHER
578 | if (!(pw1.isValidated() && pw1_modes[PW1_MODE_NO82]))
579 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED);
580 | if (!dec_key.getPrivate().isInitialized())
581 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
582 |
583 | // Copy data to be decrypted to tmp, omit padding indicator
584 | short length = Util.arrayCopyNonAtomic(buffer, (short) 1, tmp, _0,
585 | (short) (in_received - 1));
586 |
587 | cipher.init(dec_key.getPrivate(), Cipher.MODE_DECRYPT);
588 |
589 | return cipher.doFinal(tmp, _0, length, buffer, _0);
590 | }
591 |
592 | /**
593 | * Provide the INTERNAL AUTHENTICATE command (INS 88)
594 | *
595 | * Sign the data provided using the key for authentication. Before using
596 | * this method PW1 has to be verified with mode No. 82.
597 | *
598 | * @param apdu
599 | * @return Length of data written in buffer
600 | */
601 | private short internalAuthenticate(APDU apdu) {
602 | if (!(pw1.isValidated() && pw1_modes[PW1_MODE_NO82]))
603 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED);
604 | Util.arrayCopyNonAtomic(buffer, _0, tmp, _0, in_received);
605 |
606 | if (!auth_key.getPrivate().isInitialized())
607 | ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED);
608 |
609 | cipher.init(auth_key.getPrivate(), Cipher.MODE_ENCRYPT);
610 | return cipher.doFinal(tmp, _0, in_received, buffer, _0);
611 | }
612 |
613 | /**
614 | * Provide the GENERATE ASYMMETRIC KEY PAIR command (INS 47)
615 | *
616 | * For mode 80, generate a new key pair, specified in the first element of
617 | * buffer, and output the public key.
618 | *
619 | * For mode 81, output the public key specified in the first element of
620 | * buffer.
621 | *
622 | * Before using this method PW3 has to be verified.
623 | *
624 | * @param apdu
625 | * @param mode
626 | * Generate key pair (80) or read public key (81)
627 | * @return Length of data written in buffer
628 | */
629 | private short genAsymKey(APDU apdu, byte mode) {
630 | PGPKey key = getKey(buffer[0]);
631 |
632 | if (mode == (byte) 0x80) {
633 | if (!pw3.isValidated())
634 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED);
635 |
636 | // TODO Usage of transaction resultsin SW 6F00 on new cards
637 | JCSystem.beginTransaction();
638 | key.genKeyPair();
639 |
640 | if (buffer[0] == (byte) 0xB6) {
641 | // Reset signature counter
642 | for(short i = 0; i < ds_counter.length; i++) {
643 | ds_counter[i] = (byte) 0;
644 | }
645 | }
646 | JCSystem.commitTransaction();
647 | }
648 |
649 | // Output requested key
650 | return sendPublicKey(key);
651 | }
652 |
653 | /**
654 | * Provide the GET CHALLENGE command (INS 84)
655 | *
656 | * Generate a random number of the length given in len.
657 | *
658 | * @param apdu
659 | * @param len
660 | * Length of the requested challenge
661 | * @return Length of data written in buffer
662 | */
663 | private short getChallenge(APDU apdu, short len) {
664 | if (len > CHALLENGES_MAX_LENGTH)
665 | ISOException.throwIt(SW_WRONG_LENGTH);
666 |
667 | random.generateData(buffer, _0, len);
668 |
669 | // Set the SSC used in Secure Messaging if the size of the requested
670 | // challenge is equal to the size of the SSC
671 | if(len == sm.getSSCSize()) {
672 | sm.setSSC(buffer, _0);
673 | }
674 |
675 | return len;
676 | }
677 |
678 | /**
679 | * Provide the GET DATA command (INS CA)
680 | *
681 | * Output the data specified with tag.
682 | *
683 | * @param apdu
684 | * @param tag
685 | * Tag of the requested data
686 | */
687 | private short getData(short tag) {
688 | short offset = 0;
689 |
690 | switch (tag) {
691 | // 4F - Application identifier (AID)
692 | case (short) 0x004F:
693 | return JCSystem.getAID().getBytes(buffer, _0);
694 |
695 | // 5E - Login data
696 | case (short) 0x005E:
697 | return Util.arrayCopyNonAtomic(loginData, _0, buffer, _0, loginData_length);
698 |
699 | // 5F50 - URL
700 | case (short) 0x5F50:
701 | return Util.arrayCopyNonAtomic(url, _0, buffer, _0, url_length);
702 |
703 | // 5F52 - Historical bytes
704 | case (short) 0x5F52:
705 | return Util.arrayCopyNonAtomic(HISTORICAL, _0, buffer, _0, (short)HISTORICAL.length);
706 |
707 | // 65 - Cardholder Related Data
708 | case (short) 0x0065:
709 | buffer[offset++] = 0x65;
710 | buffer[offset++] = 0x00;
711 |
712 | // 5B - Name
713 | buffer[offset++] = 0x5B;
714 | buffer[offset++] = (byte) name_length;
715 | offset = Util.arrayCopyNonAtomic(name, _0, buffer, offset,
716 | name_length);
717 |
718 | // 5F2D - Language
719 | buffer[offset++] = 0x5F;
720 | buffer[offset++] = 0x2D;
721 | buffer[offset++] = (byte) lang_length;
722 | offset = Util.arrayCopyNonAtomic(lang, _0, buffer, offset,
723 | lang_length);
724 |
725 | // 5F35 - Sex
726 | buffer[offset++] = 0x5F;
727 | buffer[offset++] = 0x35;
728 | buffer[offset++] = 0x01;
729 | buffer[offset++] = sex;
730 |
731 | // Set length for combined data
732 | buffer[1] = (byte) (offset - 2);
733 |
734 | return offset;
735 |
736 | // 6E - Application Related Data
737 | case (short) 0x006E:
738 | buffer[offset++] = 0x6E;
739 | // Total length assumed to be >= 128 and < 256
740 | buffer[offset++] = (byte) 0x81;
741 | buffer[offset++] = 0;
742 |
743 | // 4F - AID
744 | buffer[offset++] = 0x4F;
745 | byte len = JCSystem.getAID().getBytes(buffer, (short)(offset + 1));
746 | buffer[offset++] = len;
747 | offset += len;
748 |
749 | // 5F52 - Historical bytes
750 | buffer[offset++] = 0x5F;
751 | buffer[offset++] = 0x52;
752 | buffer[offset++] = (byte) HISTORICAL.length;
753 | offset = Util.arrayCopyNonAtomic(HISTORICAL, _0, buffer, offset,
754 | (short) HISTORICAL.length);
755 |
756 | // 73 - Discretionary data objects
757 | buffer[offset++] = 0x73;
758 | buffer[offset++] = 0x00;
759 |
760 | // C0 - Extended capabilities
761 | buffer[offset++] = (byte) 0xC0;
762 | buffer[offset++] = (byte) EXTENDED_CAP.length;
763 | offset = Util.arrayCopyNonAtomic(EXTENDED_CAP, _0, buffer, offset,
764 | (short) EXTENDED_CAP.length);
765 |
766 | // C1 - Algorithm attributes signature
767 | buffer[offset++] = (byte) 0xC1;
768 | buffer[offset++] = (byte) 0x06;
769 | offset = sig_key.getAttributes(buffer, offset);
770 |
771 | // C2 - Algorithm attributes decryption
772 | buffer[offset++] = (byte) 0xC2;
773 | buffer[offset++] = (byte) 0x06;
774 | offset = dec_key.getAttributes(buffer, offset);
775 |
776 | // C3 - Algorithm attributes authentication
777 | buffer[offset++] = (byte) 0xC3;
778 | buffer[offset++] = (byte) 0x06;
779 | offset = auth_key.getAttributes(buffer, offset);
780 |
781 | // C4 - PW1 Status bytes
782 | buffer[offset++] = (byte) 0xC4;
783 | buffer[offset++] = 0x07;
784 | buffer[offset++] = pw1_status;
785 | buffer[offset++] = PW1_MAX_LENGTH;
786 | buffer[offset++] = RC_MAX_LENGTH;
787 | buffer[offset++] = PW3_MAX_LENGTH;
788 | buffer[offset++] = pw1.getTriesRemaining();
789 | buffer[offset++] = rc.getTriesRemaining();
790 | buffer[offset++] = pw3.getTriesRemaining();
791 |
792 | // C5 - Fingerprints sign, dec and auth keys
793 | buffer[offset++] = (byte) 0xC5;
794 | buffer[offset++] = (short) 60;
795 | offset = sig_key.getFingerprint(buffer, offset);
796 | offset = dec_key.getFingerprint(buffer, offset);
797 | offset = auth_key.getFingerprint(buffer, offset);
798 |
799 | // C6 - Fingerprints CA 1, 2 and 3
800 | buffer[offset++] = (byte) 0xC6;
801 | buffer[offset++] = (short) 60;
802 | offset = Util.arrayCopyNonAtomic(ca1_fp, _0, buffer, offset,
803 | (short) 20);
804 | offset = Util.arrayCopyNonAtomic(ca2_fp, _0, buffer, offset,
805 | (short) 20);
806 | offset = Util.arrayCopyNonAtomic(ca3_fp, _0, buffer, offset,
807 | (short) 20);
808 |
809 | // CD - Generation times of public key pair
810 | buffer[offset++] = (byte) 0xCD;
811 | buffer[offset++] = (short) 12;
812 | offset = sig_key.getTime(buffer, offset);
813 | offset = dec_key.getTime(buffer, offset);
814 | offset = auth_key.getTime(buffer, offset);
815 |
816 | // Set length of combined data
817 | buffer[2] = (byte) (offset - 3);
818 |
819 | return offset;
820 |
821 | // 7A - Security support template
822 | case (short) 0x007A:
823 | buffer[offset++] = 0x7A;
824 | buffer[offset++] = (byte) 0x05;
825 |
826 | // 93 - Digital signature counter
827 | buffer[offset++] = (byte) 0x93;
828 | buffer[offset++] = 0x03;
829 | offset = Util.arrayCopyNonAtomic(ds_counter, _0, buffer, offset,
830 | (short) 3);
831 |
832 | return offset;
833 |
834 | // 7F21 - Cardholder Certificate
835 | case (short) 0x7F21:
836 | // Use buffer since certificate may be longer than
837 | // RESPONSE_MAX_LENGTH
838 | buffer[offset++] = 0x7F;
839 | buffer[offset++] = 0x21;
840 |
841 | if (cert_length < 128) {
842 | buffer[offset++] = (byte) cert_length;
843 | } else if (cert_length < 256) {
844 | buffer[offset++] = (byte) 0x81;
845 | buffer[offset++] = (byte) cert_length;
846 | } else {
847 | buffer[offset++] = (byte) 0x82;
848 | Util.setShort(buffer, offset, cert_length);
849 | offset += 2;
850 | }
851 |
852 | offset = Util.arrayCopyNonAtomic(cert, _0, buffer, offset,
853 | cert_length);
854 |
855 | return offset;
856 |
857 | // C4 - PW Status Bytes
858 | case (short) 0x00C4:
859 | buffer[offset++] = pw1_status;
860 | buffer[offset++] = PW1_MAX_LENGTH;
861 | buffer[offset++] = RC_MAX_LENGTH;
862 | buffer[offset++] = PW3_MAX_LENGTH;
863 | buffer[offset++] = pw1.getTriesRemaining();
864 | buffer[offset++] = rc.getTriesRemaining();
865 | buffer[offset++] = pw3.getTriesRemaining();
866 |
867 | return offset;
868 |
869 | default:
870 | ISOException.throwIt(SW_RECORD_NOT_FOUND);
871 | }
872 |
873 | return offset;
874 | }
875 |
876 | /**
877 | * Provide the PUT DATA command (INS DA)
878 | *
879 | * Write the data specified using tag.
880 | *
881 | * Before using this method PW3 has to be verified.
882 | *
883 | * @param apdu
884 | * @param tag
885 | * Tag of the requested data
886 | */
887 | private void putData(short tag) {
888 | if (!pw3.isValidated())
889 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED);
890 |
891 | switch (tag) {
892 | // 5B - Name
893 | case (short) 0x005B:
894 | if (in_received > name.length)
895 | ISOException.throwIt(SW_WRONG_LENGTH);
896 |
897 | name_length = Util.arrayCopy(buffer, _0, name, _0, in_received);
898 | break;
899 |
900 | // 5E - Login data
901 | case (short) 0x005E:
902 | if (in_received > loginData.length)
903 | ISOException.throwIt(SW_WRONG_LENGTH);
904 |
905 | loginData_length = Util.arrayCopy(buffer, _0, loginData, _0,
906 | in_received);
907 | break;
908 |
909 | // 5F2D - Language preferences
910 | case (short) 0x5F2D:
911 | if (in_received > lang.length)
912 | ISOException.throwIt(SW_WRONG_LENGTH);
913 |
914 | lang_length = Util.arrayCopy(buffer, _0, lang, _0, in_received);
915 | break;
916 |
917 | // 5F35 - Sex
918 | case (short) 0x5F35:
919 | if (in_received != 1)
920 | ISOException.throwIt(SW_WRONG_LENGTH);
921 |
922 | // Check for valid values
923 | if (buffer[0] != (byte) 0x31 && buffer[0] != (byte) 0x32
924 | && buffer[0] != (byte) 0x39)
925 | ISOException.throwIt(SW_WRONG_DATA);
926 |
927 | sex = buffer[0];
928 | break;
929 |
930 | // 5F50 - URL
931 | case (short) 0x5F50:
932 | if (in_received > url.length)
933 | ISOException.throwIt(SW_WRONG_LENGTH);
934 |
935 | url_length = Util.arrayCopy(buffer, _0, url, _0, in_received);
936 | break;
937 |
938 | // 7F21 - Cardholder certificate
939 | case (short) 0x7F21:
940 | if (in_received > cert.length)
941 | ISOException.throwIt(SW_WRONG_LENGTH);
942 |
943 | cert_length = Util.arrayCopy(buffer, _0, cert, _0, in_received);
944 | break;
945 |
946 | // C4 - PW Status Bytes
947 | case (short) 0x00C4:
948 | if (in_received != 1)
949 | ISOException.throwIt(SW_WRONG_LENGTH);
950 |
951 | // Check for valid values
952 | if (buffer[0] != (byte) 0x00 && buffer[0] != (byte) 0x01)
953 | ISOException.throwIt(SW_WRONG_DATA);
954 |
955 | pw1_status = buffer[0];
956 | break;
957 |
958 | // C7 - Fingerprint signature key
959 | case (short) 0x00C7:
960 | if (in_received != PGPKey.FP_SIZE)
961 | ISOException.throwIt(SW_WRONG_LENGTH);
962 |
963 | sig_key.setFingerprint(buffer, _0);
964 | break;
965 |
966 | // C8 - Fingerprint decryption key
967 | case (short) 0x00C8:
968 | if (in_received != PGPKey.FP_SIZE)
969 | ISOException.throwIt(SW_WRONG_LENGTH);
970 |
971 | dec_key.setFingerprint(buffer, _0);
972 | break;
973 |
974 | // C9 - Fingerprint authentication key
975 | case (short) 0x00C9:
976 | if (in_received != PGPKey.FP_SIZE)
977 | ISOException.throwIt(SW_WRONG_LENGTH);
978 |
979 | auth_key.setFingerprint(buffer, _0);
980 | break;
981 |
982 | // CA - Fingerprint Certification Authority 1
983 | case (short) 0x00CA:
984 | if (in_received != ca1_fp.length)
985 | ISOException.throwIt(SW_WRONG_LENGTH);
986 |
987 | Util.arrayCopy(buffer, _0, ca1_fp, _0, in_received);
988 | break;
989 |
990 | // CB - Fingerprint Certification Authority 2
991 | case (short) 0x00CB:
992 | if (in_received != ca2_fp.length)
993 | ISOException.throwIt(SW_WRONG_LENGTH);
994 |
995 | Util.arrayCopy(buffer, _0, ca2_fp, _0, in_received);
996 | break;
997 |
998 | // CC - Fingerprint Certification Authority 3
999 | case (short) 0x00CC:
1000 | if (in_received != ca3_fp.length)
1001 | ISOException.throwIt(SW_WRONG_LENGTH);
1002 |
1003 | Util.arrayCopy(buffer, _0, ca3_fp, _0, in_received);
1004 | break;
1005 |
1006 | // CE - Signature key generation date/time
1007 | case (short) 0x00CE:
1008 | if (in_received != 4)
1009 | ISOException.throwIt(SW_WRONG_LENGTH);
1010 |
1011 | sig_key.setTime(buffer, _0);
1012 | break;
1013 |
1014 | // CF - Decryption key generation date/time
1015 | case (short) 0x00CF:
1016 | if (in_received != 4)
1017 | ISOException.throwIt(SW_WRONG_LENGTH);
1018 |
1019 | dec_key.setTime(buffer, _0);
1020 | break;
1021 |
1022 | // D0 - Authentication key generation date/time
1023 | case (short) 0x00D0:
1024 | if (in_received != 4)
1025 | ISOException.throwIt(SW_WRONG_LENGTH);
1026 |
1027 | auth_key.setTime(buffer, _0);
1028 | break;
1029 |
1030 | // D3 - Resetting Code
1031 | case (short) 0x00D3:
1032 | if (in_received == 0) {
1033 | rc_length = 0;
1034 | } else if (in_received >= RC_MIN_LENGTH
1035 | && in_received <= RC_MAX_LENGTH) {
1036 | JCSystem.beginTransaction();
1037 | rc.update(buffer, _0, (byte) in_received);
1038 | rc_length = (byte) in_received;
1039 | JCSystem.commitTransaction();
1040 | } else {
1041 | ISOException.throwIt(SW_WRONG_LENGTH);
1042 | }
1043 | break;
1044 |
1045 | // D1 - SM-Key-ENC
1046 | case (short) 0x00D1:
1047 | sm.setSessionKeyEncryption(buffer, _0);
1048 | break;
1049 |
1050 | // D2 - SM-Key-MAC
1051 | case (short) 0x00D2:
1052 | sm.setSessionKeyMAC(buffer, _0);
1053 | break;
1054 |
1055 | // F4 - SM-Key-Container
1056 | case (short) 0x00F4:
1057 | short offset = 0;
1058 | short key_len = 0;
1059 | // Set encryption key
1060 | if(buffer[offset++] == (byte)0xD1) {
1061 | key_len = (short)(buffer[offset++] & 0x7F);
1062 | sm.setSessionKeyEncryption(buffer, offset);
1063 | offset += key_len;
1064 | }
1065 |
1066 | // Set MAC key
1067 | if(buffer[offset++] == (byte)0xD2) {
1068 | key_len = (short)(buffer[offset++] & 0x7F);
1069 | sm.setSessionKeyMAC(buffer, offset);
1070 | offset += key_len;
1071 | }
1072 | break;
1073 |
1074 | default:
1075 | ISOException.throwIt(SW_RECORD_NOT_FOUND);
1076 | break;
1077 | }
1078 | }
1079 |
1080 | /**
1081 | * EXPERIMENTAL: Provide functionality for importing keys.
1082 | *
1083 | * @param apdu
1084 | */
1085 | private void importKey(APDU apdu) {
1086 | // TODO The following code still has to be tested
1087 | if (!pw3.isValidated())
1088 | ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED);
1089 |
1090 | short offset = 0;
1091 |
1092 | // Check for tag 4D
1093 | if (buffer[offset++] != 0x4D)
1094 | ISOException.throwIt(SW_DATA_INVALID);
1095 |
1096 | // Length of 4D
1097 | offset += getLengthBytes(getLength(buffer, offset));
1098 |
1099 | // Get key for Control Reference Template
1100 | PGPKey key = getKey(buffer[offset++]);
1101 |
1102 | // Skip empty length of CRT
1103 | offset++;
1104 |
1105 | // Check for tag 7F48
1106 | if (!(buffer[offset++] == 0x7F && buffer[offset++] == 0x48))
1107 | ISOException.throwIt(SW_DATA_INVALID);
1108 | short len_template = getLength(buffer, offset);
1109 | offset += getLengthBytes(len_template);
1110 |
1111 | short offset_data = (short) (offset + len_template);
1112 |
1113 | if (buffer[offset++] != (byte) 0x91)
1114 | ISOException.throwIt(SW_DATA_INVALID);
1115 | short len_e = getLength(buffer, offset);
1116 | offset += getLengthBytes(len_e);
1117 |
1118 | if (buffer[offset++] != (byte) 0x92)
1119 | ISOException.throwIt(SW_DATA_INVALID);
1120 | short len_p = getLength(buffer, offset);
1121 | offset += getLengthBytes(len_p);
1122 |
1123 | if (buffer[offset++] != (byte) 0x93)
1124 | ISOException.throwIt(SW_DATA_INVALID);
1125 | short len_q = getLength(buffer, offset);
1126 | offset += getLengthBytes(len_q);
1127 |
1128 | if (buffer[offset++] != (byte) 0x94)
1129 | ISOException.throwIt(SW_DATA_INVALID);
1130 | short len_pq = getLength(buffer, offset);
1131 | offset += getLengthBytes(len_pq);
1132 |
1133 | if (buffer[offset++] != (byte) 0x95)
1134 | ISOException.throwIt(SW_DATA_INVALID);
1135 | short len_dp1 = getLength(buffer, offset);
1136 | offset += getLengthBytes(len_dp1);
1137 |
1138 | if (buffer[offset++] != (byte) 0x96)
1139 | ISOException.throwIt(SW_DATA_INVALID);
1140 | short len_dq1 = getLength(buffer, offset);
1141 | offset += getLengthBytes(len_dq1);
1142 |
1143 | if (!(buffer[offset_data++] == 0x5F && buffer[offset_data++] == 0x48))
1144 | ISOException.throwIt(SW_DATA_INVALID);
1145 | offset_data += getLengthBytes(getLength(buffer, offset_data));
1146 |
1147 | // TODO Check value of e
1148 | offset_data += len_e;
1149 |
1150 | key.setP(buffer, offset_data, len_p);
1151 | offset_data += len_p;
1152 |
1153 | key.setQ(buffer, offset_data, len_q);
1154 | offset_data += len_q;
1155 |
1156 | key.setPQ(buffer, offset_data, len_pq);
1157 | offset_data += len_pq;
1158 |
1159 | key.setDP1(buffer, offset_data, len_dp1);
1160 | offset_data += len_dp1;
1161 |
1162 | key.setDQ1(buffer, offset_data, len_dq1);
1163 | offset_data += len_dq1;
1164 | }
1165 |
1166 | /**
1167 | * Output the public key of the given key pair.
1168 | *
1169 | * @param apdu
1170 | * @param key
1171 | * Key pair containing public key to be output
1172 | */
1173 | private short sendPublicKey(PGPKey key) {
1174 | RSAPublicKey pubkey = key.getPublic();
1175 |
1176 | // Build message in tmp
1177 | short offset = 0;
1178 |
1179 | // 81 - Modulus
1180 | tmp[offset++] = (byte) 0x81;
1181 |
1182 | // Length of modulus is always greater than 128 bytes
1183 | if (key.getModulusLength() < 256) {
1184 | tmp[offset++] = (byte) 0x81;
1185 | tmp[offset++] = (byte) key.getModulusLength();
1186 | } else {
1187 | tmp[offset++] = (byte) 0x82;
1188 | offset = Util.setShort(tmp, offset, key.getModulusLength());
1189 | }
1190 | pubkey.getModulus(tmp, offset);
1191 | offset += key.getModulusLength();
1192 |
1193 | // 82 - Exponent
1194 | tmp[offset++] = (byte) 0x82;
1195 | tmp[offset++] = (byte) key.getExponentLength();
1196 | pubkey.getExponent(tmp, offset);
1197 | offset += key.getExponentLength();
1198 |
1199 | short len = offset;
1200 |
1201 | offset = 0;
1202 |
1203 | buffer[offset++] = 0x7F;
1204 | buffer[offset++] = 0x49;
1205 |
1206 | if (len < 256) {
1207 | buffer[offset++] = (byte) 0x81;
1208 | buffer[offset++] = (byte) len;
1209 | } else {
1210 | buffer[offset++] = (byte) 0x82;
1211 | offset = Util.setShort(buffer, offset, len);
1212 | }
1213 |
1214 | offset = Util.arrayCopyNonAtomic(tmp, _0, buffer, offset, len);
1215 |
1216 | return offset;
1217 | }
1218 |
1219 | /**
1220 | * Send len bytes from buffer. If len is greater than RESPONSE_MAX_LENGTH,
1221 | * remaining data can be retrieved using GET RESPONSE.
1222 | *
1223 | * @param apdu
1224 | * @param len
1225 | * The byte length of the data to send
1226 | */
1227 | private void sendBuffer(APDU apdu, short len) {
1228 | out_sent = 0;
1229 | out_left = len;
1230 | sendNext(apdu);
1231 | }
1232 |
1233 | /**
1234 | * Send provided status
1235 | *
1236 | * @param apdu
1237 | * @param status Status to send
1238 | */
1239 | private void sendException(APDU apdu, short status) {
1240 | out_sent = 0;
1241 | out_left = 0;
1242 | sendNext(apdu, status);
1243 | }
1244 |
1245 | /**
1246 | * Send next block of data in buffer. Used for sending data in
1247 | *
1248 | * @param apdu
1249 | */
1250 | private void sendNext(APDU apdu) {
1251 | sendNext(apdu, SW_NO_ERROR);
1252 | }
1253 |
1254 | /**
1255 | * Send next block of data in buffer. Used for sending data in
1256 | *
1257 | * @param apdu
1258 | * @param status Status to send
1259 | */
1260 | private void sendNext(APDU apdu, short status) {
1261 | byte[] buf = APDU.getCurrentAPDUBuffer();
1262 | apdu.setOutgoing();
1263 |
1264 | // Determine maximum size of the messages
1265 | short max_length;
1266 | if(sm_success) {
1267 | max_length = RESPONSE_SM_MAX_LENGTH;
1268 | }
1269 | else {
1270 | max_length = RESPONSE_MAX_LENGTH;
1271 | }
1272 |
1273 | Util.arrayCopyNonAtomic(buffer, out_sent, buf, _0, max_length);
1274 |
1275 | short len = 0;
1276 |
1277 | if (out_left > max_length) {
1278 | len = max_length;
1279 |
1280 | // Compute byte left and sent
1281 | out_left -= max_length;
1282 | out_sent += max_length;
1283 |
1284 | // Determine new status word
1285 | if (out_left > max_length) {
1286 | status = (short) (SW_BYTES_REMAINING_00 | max_length);
1287 | } else {
1288 | status = (short) (SW_BYTES_REMAINING_00 | out_left);
1289 | }
1290 | }
1291 | else {
1292 | len = out_left;
1293 |
1294 | // Reset buffer
1295 | out_sent = 0;
1296 | out_left = 0;
1297 | }
1298 |
1299 | // If SM is used, wrap response
1300 | if(sm_success) {
1301 | len = sm.wrapResponseAPDU(buf, _0, len, status);
1302 | }
1303 |
1304 | // Send data in buffer
1305 | apdu.setOutgoingLength(len);
1306 | apdu.sendBytes(_0, len);
1307 |
1308 | // Send status word
1309 | if(status != SW_NO_ERROR)
1310 | ISOException.throwIt(status);
1311 | }
1312 |
1313 | /**
1314 | * Get length of TLV element.
1315 | *
1316 | * @param data
1317 | * Byte array
1318 | * @param offset
1319 | * Offset within byte array containing first byte
1320 | * @return Length of value
1321 | */
1322 | private short getLength(byte[] data, short offset) {
1323 | short len = 0;
1324 |
1325 | if ((data[offset] & (byte) 0x80) == (byte) 0x00) {
1326 | len = data[offset];
1327 | } else if ((data[offset] & (byte) 0x7F) == (byte) 0x01) {
1328 | len = (short)(0xFF & data[(short) (offset + 1)]);
1329 | } else if ((data[offset] & (byte) 0x7F) == (byte) 0x02) {
1330 | len = Util.makeShort(data[(short) (offset + 1)], data[(short) (offset + 2)]);
1331 | } else {
1332 | ISOException.throwIt(SW_UNKNOWN);
1333 | }
1334 |
1335 | return len;
1336 | }
1337 |
1338 | /**
1339 | * Get number of bytes needed to represent length for TLV element.
1340 | *
1341 | * @param length
1342 | * Length of value
1343 | * @return Number of bytes needed to represent length
1344 | */
1345 | private short getLengthBytes(short length) {
1346 | if (length <= 127)
1347 | return 1;
1348 | else if (length <= 255)
1349 | return 2;
1350 | else
1351 | return 3;
1352 | }
1353 |
1354 | /**
1355 | * Return the key of the type requested: - B6: Digital signatures - B8:
1356 | * Confidentiality - A4: Authentication
1357 | *
1358 | * @param type
1359 | * Type of key to be returned
1360 | * @return Key of requested type
1361 | */
1362 | private PGPKey getKey(byte type) {
1363 | PGPKey key = sig_key;
1364 |
1365 | if (type == (byte) 0xB6)
1366 | key = sig_key;
1367 | else if (type == (byte) 0xB8)
1368 | key = dec_key;
1369 | else if (type == (byte) 0xA4)
1370 | key = auth_key;
1371 | else
1372 | ISOException.throwIt(SW_UNKNOWN);
1373 |
1374 | return key;
1375 | }
1376 |
1377 | /**
1378 | * Increase the digital signature counter by one. In case of overflow
1379 | * SW_WARNING_STATE_UNCHANGED will be thrown and nothing will
1380 | * change.
1381 | */
1382 | private void increaseDSCounter() {
1383 | for (short i = (short) (ds_counter.length - 1); i >= 0; i--) {
1384 | if ((short) (ds_counter[i] & 0xFF) >= 0xFF) {
1385 | if (i == 0) {
1386 | // Overflow
1387 | ISOException.throwIt(SW_WARNING_STATE_UNCHANGED);
1388 | } else {
1389 | ds_counter[i] = 0;
1390 | }
1391 | } else {
1392 | ds_counter[i]++;
1393 | break;
1394 | }
1395 | }
1396 | }
1397 | }
1398 |
--------------------------------------------------------------------------------