2 * Copyright (C) 2005 Jan Hutter, Martin Willi
3 * Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 #include <asn1/asn1.h>
24 #include <asn1/asn1_parser.h>
26 #include <crypto/rngs/rng.h>
27 #include <crypto/hashers/hasher.h>
29 #include "../pluto/constants.h"
30 #include "../pluto/defs.h"
31 #include "../pluto/fetch.h"
35 static const char *pkiStatus_values
[] = { "0", "2", "3" };
37 static const char *pkiStatus_names
[] = {
44 static const char *msgType_values
[] = { "3", "19", "20", "21", "22" };
46 static const char *msgType_names
[] = {
55 static const char *failInfo_reasons
[] = {
56 "badAlg - unrecognized or unsupported algorithm identifier",
57 "badMessageCheck - integrity check failed",
58 "badRequest - transaction not permitted or supported",
59 "badTime - Message time field was not sufficiently close to the system time",
60 "badCertId - No certificate could be identified matching the provided criteria"
63 const scep_attributes_t empty_scep_attributes
= {
64 SCEP_Unknown_MSG
, /* msgType */
65 SCEP_UNKNOWN
, /* pkiStatus */
66 SCEP_unknown_REASON
, /* failInfo */
67 { NULL
, 0 } , /* transID */
68 { NULL
, 0 } , /* senderNonce */
69 { NULL
, 0 } , /* recipientNonce */
72 /* ASN.1 definition of the X.501 atttribute type */
74 static const asn1Object_t attributesObjects
[] = {
75 { 0, "attributes", ASN1_SET
, ASN1_LOOP
}, /* 0 */
76 { 1, "attribute", ASN1_SEQUENCE
, ASN1_NONE
}, /* 1 */
77 { 2, "type", ASN1_OID
, ASN1_BODY
}, /* 2 */
78 { 2, "values", ASN1_SET
, ASN1_LOOP
}, /* 3 */
79 { 3, "value", ASN1_EOC
, ASN1_RAW
}, /* 4 */
80 { 2, "end loop", ASN1_EOC
, ASN1_END
}, /* 5 */
81 { 0, "end loop", ASN1_EOC
, ASN1_END
}, /* 6 */
82 { 0, "exit", ASN1_EOC
, ASN1_EXIT
}
84 #define ATTRIBUTE_OBJ_TYPE 2
85 #define ATTRIBUTE_OBJ_VALUE 4
88 * Extract and store an attribute
90 static bool extract_attribute(int oid
, chunk_t object
, u_int level
,
91 scep_attributes_t
*attrs
)
93 asn1_t type
= ASN1_EOC
;
94 const char *name
= "none";
98 case OID_PKCS9_CONTENT_TYPE
:
100 name
= "contentType";
102 case OID_PKCS9_SIGNING_TIME
:
104 name
= "signingTime";
106 case OID_PKCS9_MESSAGE_DIGEST
:
107 type
= ASN1_OCTET_STRING
;
108 name
= "messageDigest";
110 case OID_PKI_MESSAGE_TYPE
:
111 type
= ASN1_PRINTABLESTRING
;
112 name
= "messageType";
115 type
= ASN1_PRINTABLESTRING
;
118 case OID_PKI_FAIL_INFO
:
119 type
= ASN1_PRINTABLESTRING
;
122 case OID_PKI_SENDER_NONCE
:
123 type
= ASN1_OCTET_STRING
;
124 name
= "senderNonce";
126 case OID_PKI_RECIPIENT_NONCE
:
127 type
= ASN1_OCTET_STRING
;
128 name
= "recipientNonce";
130 case OID_PKI_TRANS_ID
:
131 type
= ASN1_PRINTABLESTRING
;
138 if (type
== ASN1_EOC
)
143 if (!asn1_parse_simple_object(&object
, type
, level
+1, name
))
150 case OID_PKCS9_CONTENT_TYPE
:
152 case OID_PKCS9_SIGNING_TIME
:
154 case OID_PKCS9_MESSAGE_DIGEST
:
156 case OID_PKI_MESSAGE_TYPE
:
160 for (m
= SCEP_CertRep_MSG
; m
< SCEP_Unknown_MSG
; m
++)
162 if (strncmp(msgType_values
[m
], object
.ptr
, object
.len
) == 0)
165 DBG2(DBG_APP
, "messageType: %s", msgType_names
[attrs
->msgType
]);
172 for (s
= SCEP_SUCCESS
; s
< SCEP_UNKNOWN
; s
++)
174 if (strncmp(pkiStatus_values
[s
], object
.ptr
, object
.len
) == 0)
176 attrs
->pkiStatus
= s
;
179 DBG2(DBG_APP
, "pkiStatus: %s", pkiStatus_names
[attrs
->pkiStatus
]);
182 case OID_PKI_FAIL_INFO
:
184 if (object
.len
== 1 &&
185 *object
.ptr
>= '0' && *object
.ptr
<= '4')
187 attrs
->failInfo
= (failInfo_t
)(*object
.ptr
- '0');
189 if (attrs
->failInfo
!= SCEP_unknown_REASON
)
191 DBG1(DBG_APP
, "failInfo: %s", failInfo_reasons
[attrs
->failInfo
]);
195 case OID_PKI_SENDER_NONCE
:
196 attrs
->senderNonce
= object
;
198 case OID_PKI_RECIPIENT_NONCE
:
199 attrs
->recipientNonce
= object
;
201 case OID_PKI_TRANS_ID
:
202 attrs
->transID
= object
;
209 * Parse X.501 attributes
211 bool parse_attributes(chunk_t blob
, scep_attributes_t
*attrs
)
213 asn1_parser_t
*parser
;
215 int oid
= OID_UNKNOWN
;
217 bool success
= FALSE
;
219 parser
= asn1_parser_create(attributesObjects
, blob
);
220 DBG3(DBG_APP
, "parsing attributes");
222 while (parser
->iterate(parser
, &objectID
, &object
))
226 case ATTRIBUTE_OBJ_TYPE
:
227 oid
= asn1_known_oid(object
);
229 case ATTRIBUTE_OBJ_VALUE
:
231 if (!extract_attribute(oid
, object
, parser
->get_level(parser
), attrs
))
239 success
= parser
->success(parser
);
242 parser
->destroy(parser
);
247 * Generates a unique fingerprint of the pkcs10 request
248 * by computing an MD5 hash over it
250 chunk_t
scep_generate_pkcs10_fingerprint(chunk_t pkcs10
)
252 chunk_t digest
= chunk_alloca(HASH_SIZE_MD5
);
255 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD5
);
256 hasher
->get_hash(hasher
, pkcs10
, digest
.ptr
);
257 hasher
->destroy(hasher
);
259 return chunk_to_hex(digest
, NULL
, FALSE
);
263 * Generate a transaction id as the MD5 hash of an public key
264 * the transaction id is also used as a unique serial number
266 void scep_generate_transaction_id(public_key_t
*key
, chunk_t
*transID
,
267 chunk_t
*serialNumber
)
269 chunk_t digest
= chunk_alloca(HASH_SIZE_MD5
);
270 chunk_t keyEncoding
= chunk_empty
, keyInfo
;
275 key
->get_encoding(key
, PUBKEY_ASN1_DER
, &keyEncoding
);
277 keyInfo
= asn1_wrap(ASN1_SEQUENCE
, "mm",
278 asn1_algorithmIdentifier(OID_RSA_ENCRYPTION
),
279 asn1_bitstring("m", keyEncoding
));
281 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD5
);
282 hasher
->get_hash(hasher
, keyInfo
, digest
.ptr
);
283 hasher
->destroy(hasher
);
286 /* is the most significant bit of the digest set? */
287 msb_set
= (*digest
.ptr
& 0x80) == 0x80;
289 /* allocate space for the serialNumber */
290 serialNumber
->len
= msb_set
+ digest
.len
;
291 serialNumber
->ptr
= malloc(serialNumber
->len
);
293 /* the serial number as the two's complement of the digest */
294 pos
= serialNumber
->ptr
;
299 memcpy(pos
, digest
.ptr
, digest
.len
);
301 /* the transaction id is the serial number in hex format */
302 transID
->len
= 2*digest
.len
;
303 transID
->ptr
= malloc(transID
->len
+ 1);
304 datatot(digest
.ptr
, digest
.len
, 16, transID
->ptr
, transID
->len
+ 1);
308 * Builds a transId attribute
310 chunk_t
scep_transId_attribute(chunk_t transID
)
312 return asn1_wrap(ASN1_SEQUENCE
, "cm",
313 asn1_build_known_oid(OID_PKI_TRANS_ID
),
314 asn1_wrap(ASN1_SET
, "m",
315 asn1_simple_object(ASN1_PRINTABLESTRING
, transID
)));
319 * Builds a messageType attribute
321 chunk_t
scep_messageType_attribute(scep_msg_t m
)
324 (u_char
*)msgType_values
[m
],
325 strlen(msgType_values
[m
])
328 return asn1_wrap(ASN1_SEQUENCE
, "mm",
329 asn1_build_known_oid(OID_PKI_MESSAGE_TYPE
),
330 asn1_wrap(ASN1_SET
, "m",
331 asn1_simple_object(ASN1_PRINTABLESTRING
, msgType
)));
335 * Builds a senderNonce attribute
337 chunk_t
scep_senderNonce_attribute(void)
339 const size_t nonce_len
= 16;
340 u_char nonce_buf
[nonce_len
];
341 chunk_t senderNonce
= { nonce_buf
, nonce_len
};
344 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
345 rng
->get_bytes(rng
, nonce_len
, nonce_buf
);
348 return asn1_wrap(ASN1_SEQUENCE
, "cm",
349 asn1_build_known_oid(OID_PKI_SENDER_NONCE
),
350 asn1_wrap(ASN1_SET
, "m",
351 asn1_simple_object(ASN1_OCTET_STRING
, senderNonce
)));
355 * Builds a pkcs7 enveloped and signed scep request
357 chunk_t
scep_build_request(chunk_t data
, chunk_t transID
, scep_msg_t msg
,
358 certificate_t
*enc_cert
, int enc_alg
,
359 certificate_t
*signer_cert
, int digest_alg
,
360 private_key_t
*private_key
)
362 chunk_t envelopedData
, attributes
, request
;
364 envelopedData
= pkcs7_build_envelopedData(data
, enc_cert
, enc_alg
);
366 attributes
= asn1_wrap(ASN1_SET
, "mmmmm",
367 pkcs7_contentType_attribute(),
368 pkcs7_messageDigest_attribute(envelopedData
, digest_alg
),
369 scep_transId_attribute(transID
),
370 scep_messageType_attribute(msg
),
371 scep_senderNonce_attribute());
373 request
= pkcs7_build_signedData(envelopedData
, attributes
,
374 signer_cert
, digest_alg
, private_key
);
375 free(envelopedData
.ptr
);
376 free(attributes
.ptr
);
381 * Converts a binary request to base64 with 64 characters per line
382 * newline and '+' characters are escaped by %0A and %2B, respectively
384 static char* escape_http_request(chunk_t req
)
386 char *escaped_req
= NULL
;
392 /* compute and allocate the size of the base64-encoded request */
393 int len
= 1 + 4*((req
.len
+ 2)/3);
394 char *encoded_req
= malloc(len
);
396 /* do the base64 conversion */
397 len
= datatot(req
.ptr
, req
.len
, 64, encoded_req
, len
);
399 /* compute newline characters to be inserted every 64 characters */
400 lines
= (len
- 2) / 64;
402 /* count number of + characters to be escaped */
412 escaped_req
= malloc(len
+ 3*(lines
+ plus
));
414 /* escape special characters in the request */
421 memcpy(p2
, "%0A", 3);
427 memcpy(p2
, "%2B", 3);
443 * Send a SCEP request via HTTP and wait for a response
445 bool scep_http_request(const char *url
, chunk_t pkcs7
, scep_op_t op
,
446 bool http_get_request
, chunk_t
*response
)
450 char *complete_url
= NULL
;
452 /* initialize response */
453 *response
= chunk_empty
;
455 DBG2(DBG_APP
, "sending scep request to '%s'", url
);
457 if (op
== SCEP_PKI_OPERATION
)
459 const char operation
[] = "PKIOperation";
461 if (http_get_request
)
463 char *escaped_req
= escape_http_request(pkcs7
);
465 /* form complete url */
466 len
= strlen(url
) + 20 + strlen(operation
) + strlen(escaped_req
) + 1;
467 complete_url
= malloc(len
);
468 snprintf(complete_url
, len
, "%s?operation=%s&message=%s"
469 , url
, operation
, escaped_req
);
472 status
= lib
->fetcher
->fetch(lib
->fetcher
, complete_url
, response
,
473 FETCH_HTTP_VERSION_1_0
,
474 FETCH_REQUEST_HEADER
, "Pragma:",
475 FETCH_REQUEST_HEADER
, "Host:",
476 FETCH_REQUEST_HEADER
, "Accept:",
481 /* form complete url */
482 len
= strlen(url
) + 11 + strlen(operation
) + 1;
483 complete_url
= malloc(len
);
484 snprintf(complete_url
, len
, "%s?operation=%s", url
, operation
);
486 status
= lib
->fetcher
->fetch(lib
->fetcher
, complete_url
, response
,
487 FETCH_REQUEST_DATA
, pkcs7
,
488 FETCH_REQUEST_TYPE
, "",
489 FETCH_REQUEST_HEADER
, "Expect:",
493 else /* SCEP_GET_CA_CERT */
495 const char operation
[] = "GetCACert";
497 /* form complete url */
498 len
= strlen(url
) + 32 + strlen(operation
) + 1;
499 complete_url
= malloc(len
);
500 snprintf(complete_url
, len
, "%s?operation=%s&message=CAIdentifier",
503 status
= lib
->fetcher
->fetch(lib
->fetcher
, complete_url
, response
,
508 return (status
== SUCCESS
);
511 err_t
scep_parse_response(chunk_t response
, chunk_t transID
, contentInfo_t
*data
,
512 scep_attributes_t
*attrs
, certificate_t
*signer_cert
)
516 if (!pkcs7_parse_signedData(response
, data
, NULL
, &attributes
, signer_cert
))
518 return "error parsing the scep response";
520 if (!parse_attributes(attributes
, attrs
))
522 return "error parsing the scep response attributes";
524 if (!chunk_equals(transID
, attrs
->transID
))
526 return "transaction ID of scep response does not match";