summaryrefslogtreecommitdiffstats
path: root/pki/base/tps/src/apdu/APDU.cpp
blob: 1ae729cc5852c49e55433a09ff9c16f8d68b40b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// --- BEGIN COPYRIGHT BLOCK ---
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation;
// version 2.1 of the License.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor,
// Boston, MA  02110-1301  USA 
// 
// Copyright (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---

#include <stdio.h>
#include "apdu/APDU.h"
#include "engine/RA.h"
#include "main/Util.h"
#include "main/Memory.h"

#ifdef XP_WIN32
#define TPS_PUBLIC __declspec(dllexport)
#else /* !XP_WIN32 */
#define TPS_PUBLIC
#endif /* !XP_WIN32 */

/**
 * Constructs an APDU.
 *
 * ==============
 * APDU:
 * APDU are commands that can be sent from an authorized entity
 * (such as RA) to the token.  It takes the following form:
 * ---------------------------------------------------
 * | CLA | INS | P1  | P2  | lc  | data...
 * ---------------------------------------------------
 *
 * The values for the APDU header: CLA, INS, P1, P2 and lc are defined
 * in each individual APDU class.
 *
 * ==============
 * Status Words (response):
 * When APDUs are sent to the token, a response is returned.  The following
 * is a list of all possible Return Codes (Status Words):
 *
 * <I'm hoping not having to type this out...waiting for Bob to get back
 * to me with an electronic copy of his file...>
 *
 * ==============
 * ObjectID:
 *    byte[0] - an ASCII letter, 
 *          'c' - An object containing PKCS11 attributes for a certificate
 *          'k' - An object containing PKCS11 attributes for a public or private key
 *          'r' - An object containing PKCS11 attributes for a "reader"
 *          <upper case letters signify objects containing raw data 
 *           corresponding to lower cases objects above
 *    byte[1] - an ASCII numeral, in the range '0' - '9'
 *    byte[2] - binary zero
 *    byte[3] - binary zero
 *
 * ==============
 * ACLs:
 *    Each key or object on the card is associated with an ACL.
 *
 * ACL for objects:
 * [2-byte] Read Permissions;
 * [2-byte] Write Permissions;
 * [2-byte] Delete Permissions;
 *
 * Each permission is a 2-byte word.  A 1 in a bit grants permission
 * to it's corresponding identity if pass authentication.
 * permission 2-byte word format:
 * Bit 15 - reserved
 * Bit 14 - Identity #14 (strong - Secure Channel required)
 * Bit 13 - reserved
 * ...
 * Bit  7 - Identity #7 (PIN identity)
 * ...
 * Bit  1 - Identity #1 (PIN identity)
 * Bit  0 - Identity #0 (PIN identity)
 *
 * All 0 means operation never allowed
 */
TPS_PUBLIC APDU::APDU ()
{
	m_data = Buffer(0, (BYTE)0);
	m_mac = Buffer(0, (BYTE)0);
} /* APDU */

/**
 * Destroys an APDU.
 */ 
TPS_PUBLIC APDU::~APDU ()
{
} /* ~APDU */

/**
 * Copy constructor.
 */
TPS_PUBLIC APDU::APDU (const APDU &cpy)
{
    *this = cpy;
} /* APDU */

/**
 * Operator for simple assignment.
 */
TPS_PUBLIC APDU& APDU::operator=(const APDU &cpy)
{
    if (this == &cpy) 
      return *this;
    m_cla = cpy.m_cla;
    m_ins = cpy.m_ins;
    m_p1 = cpy.m_p1;
    m_p2 = cpy.m_p2;
    m_data = cpy.m_data;
    return *this;
} /* operator= */

TPS_PUBLIC APDU_Type APDU::GetType()
{
	return APDU_UNDEFINED;
}

/**
 * Sets APDU's CLA parameter.
 */
TPS_PUBLIC void APDU::SetCLA(BYTE cla)
{
    m_cla = cla;
} /* SetCLA */

/**
 * Sets APDU's INS parameter.
 */
TPS_PUBLIC void APDU::SetINS(BYTE ins)
{
    m_ins = ins;
} /* SetINS */

/**
 * Sets APDU's P1 parameter.
 */
TPS_PUBLIC void APDU::SetP1(BYTE p1)
{
    m_p1 = p1;
} /* SetP1 */

/**
 * Sets APDU's P2 parameter.
 */
TPS_PUBLIC void APDU::SetP2(BYTE p2)
{
    m_p2 = p2;
} /* SetP2 */


TPS_PUBLIC BYTE APDU::GetCLA()
{
	return m_cla;
}

TPS_PUBLIC BYTE APDU::GetINS()
{
	return m_ins;
}

TPS_PUBLIC BYTE APDU::GetP1()
{
	return m_p1;
}

TPS_PUBLIC BYTE APDU::GetP2()
{
	return m_p2;
}

TPS_PUBLIC Buffer &APDU::GetData()
{
	return m_data;
}

TPS_PUBLIC Buffer &APDU::GetMAC()
{
	return m_mac;
}

/**
 * Sets APDU's data parameter.
 */
TPS_PUBLIC void APDU::SetData(Buffer &data)
{
    m_data = data;
} /* SetData */

TPS_PUBLIC void APDU::SetMAC(Buffer &mac)
{
    m_mac = mac;
} /* SetMAC */

/**
 * populates "data" with data that's to be mac'd.
 * note: mac is not handled in here
 *
 * @param data results buffer
 */
TPS_PUBLIC void APDU::GetDataToMAC(Buffer &data)
{
    data += Buffer(1, m_cla);
    data += Buffer(1, m_ins);
    data += Buffer(1, m_p1);
    data += Buffer(1, m_p2);
    data += Buffer(1, (BYTE)m_data.size() + 8);
    data += Buffer(m_data, m_data.size());
}

/*
 * pad the message, if needed, and then
 * encrypt it with the encryption session key
 * and then set data
 *
 */
TPS_PUBLIC PRStatus APDU::SecureMessage(PK11SymKey *encSessionKey)
{
    PRStatus rv = PR_SUCCESS;
    Buffer data_to_enc;
    Buffer padding;
    Buffer data_encrypted;
    int pad_needed = 0;
#ifdef ENC_DEBUG
    m_plainText = m_data;
    // developer debugging only, not for production
//    RA::DebugBuffer("APDU::SecureMessage", "plaintext (pre padding) = ", &m_plainText);
#endif

    if (encSessionKey == NULL) {
 //     RA::Debug("APDU::SecureMessage", "no encryption session key");
      rv = PR_FAILURE;
      goto done;
    }
//    RA::Debug(LL_ALL_DATA_IN_PDU, "APDU::SecureMessage", "plaintext data length = %d", m_data.size());

    data_to_enc +=  (BYTE)m_data.size();
    data_to_enc += m_data;

    if ((data_to_enc.size() % 8) == 0)
      pad_needed = 0;
    else if (data_to_enc.size() < 8) {
      pad_needed = 8 - data_to_enc.size();
    } else { // data size > 8 and not divisible by 8
      pad_needed = 8 - (data_to_enc.size() % 8);
    }
    if (pad_needed) {
//      RA::Debug(LL_ALL_DATA_IN_PDU, "APDU::SecureMessage", "padding needed =%d", pad_needed);
      data_to_enc += Buffer(1, 0x80);
      pad_needed --;

      if (pad_needed) {
//	RA::Debug(LL_ALL_DATA_IN_PDU, "APDU::SecureMessage", "padding needed =%d", pad_needed);
	padding = Buffer(pad_needed, (BYTE)0);
	for (int i = 0; i < pad_needed; i++) {
	    ((BYTE*)padding)[i] = 0x00;
	} /* for */
      } // pad needed

    } else {
 //     RA::Debug(LL_ALL_DATA_IN_PDU, "APDU::SecureMessage", "padding not needed");
    }

    if (padding.size() > 0) {
        data_to_enc += Buffer(padding, padding.size());
    }

#ifdef ENC_DEBUG
//    RA::DebugBuffer("APDU::SecureMessage", "data to encrypt (post padding)= ",&data_to_enc);
#endif

    // now, encrypt "data_to_enc"
    rv = Util::EncryptData(encSessionKey, data_to_enc, data_encrypted);
    if (rv == PR_FAILURE) {
 //     RA::Error("APDU::SecureMessage", "encryption failed");
      goto done;
    } else {
 //     RA::Debug(LL_PER_PDU, "APDU::SecureMessage", "encryption succeeded");
 //      RA::Debug(LL_PER_PDU, "APDU::SecureMessage", "encrypted data length = %d",
//	      data_encrypted.size());
      // set "m_data"
      m_data = data_encrypted;
    }

    // lc should be automatically set correctly when getEncoding is called

 done:
    return rv;

}


/**
 * Retrieves APDU's encoding. 
 * The encoding of APDU is as follows:
 *
 *   CLA            1 byte
 *   INS            1 byte
 *   P1             1 byte
 *   P2             1 byte
 *   <Data Size>    1 byte
 *   <Data>         <Data Size> byte(s)
 *   0              1 byte
 * 
 * @param data the result buffer which will contain the actual data
 *        including the APDU header, data, and pre-calculated mac.
 */
TPS_PUBLIC void APDU::GetEncoding(Buffer &data)
{
    data += Buffer(1, m_cla);
    data += Buffer(1, m_ins);
    data += Buffer(1, m_p1);
    data += Buffer(1, m_p2);
    data += Buffer(1, (BYTE)m_data.size() + m_mac.size());
    data += Buffer(m_data, m_data.size());
    if (m_mac.size() > 0) {
      data += Buffer(m_mac, m_mac.size());
    }
} /* Encode */