3 * @brief SCEP specific functions
5 * Contains functions to build SCEP request's and to parse SCEP reply's.
9 * Copyright (C) 2005 Jan Hutter, Martin Willi
10 * Hochschule fuer Technik Rapperswil
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
29 #include <asn1/asn1.h>
30 #include <asn1/asn1_parser.h>
32 #include <crypto/rngs/rng.h>
33 #include <crypto/hashers/hasher.h>
35 #include "../pluto/constants.h"
36 #include "../pluto/defs.h"
37 #include "../pluto/fetch.h"
38 #include "../pluto/log.h"
42 static char ASN1_messageType_oid_str
[] = {
43 0x06, 0x0A, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0x02
46 static char ASN1_senderNonce_oid_str
[] = {
47 0x06, 0x0A, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0x05
50 static char ASN1_transId_oid_str
[] = {
51 0x06, 0x0A, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0x07
54 static const chunk_t ASN1_messageType_oid
=
55 chunk_from_buf(ASN1_messageType_oid_str
);
56 static const chunk_t ASN1_senderNonce_oid
=
57 chunk_from_buf(ASN1_senderNonce_oid_str
);
58 static const chunk_t ASN1_transId_oid
=
59 chunk_from_buf(ASN1_transId_oid_str
);
61 static const char *pkiStatus_values
[] = { "0", "2", "3" };
63 static const char *pkiStatus_names
[] = {
70 static const char *msgType_values
[] = { "3", "19", "20", "21", "22" };
72 static const char *msgType_names
[] = {
81 static const char *failInfo_reasons
[] = {
82 "badAlg - unrecognized or unsupported algorithm identifier",
83 "badMessageCheck - integrity check failed",
84 "badRequest - transaction not permitted or supported",
85 "badTime - Message time field was not sufficiently close to the system time",
86 "badCertId - No certificate could be identified matching the provided criteria"
89 const scep_attributes_t empty_scep_attributes
= {
90 SCEP_Unknown_MSG
, /* msgType */
91 SCEP_UNKNOWN
, /* pkiStatus */
92 SCEP_unknown_REASON
, /* failInfo */
93 { NULL
, 0 } , /* transID */
94 { NULL
, 0 } , /* senderNonce */
95 { NULL
, 0 } , /* recipientNonce */
98 /* ASN.1 definition of the X.501 atttribute type */
100 static const asn1Object_t attributesObjects
[] = {
101 { 0, "attributes", ASN1_SET
, ASN1_LOOP
}, /* 0 */
102 { 1, "attribute", ASN1_SEQUENCE
, ASN1_NONE
}, /* 1 */
103 { 2, "type", ASN1_OID
, ASN1_BODY
}, /* 2 */
104 { 2, "values", ASN1_SET
, ASN1_LOOP
}, /* 3 */
105 { 3, "value", ASN1_EOC
, ASN1_RAW
}, /* 4 */
106 { 2, "end loop", ASN1_EOC
, ASN1_END
}, /* 5 */
107 { 0, "end loop", ASN1_EOC
, ASN1_END
}, /* 6 */
108 { 0, "exit", ASN1_EOC
, ASN1_EXIT
}
110 #define ATTRIBUTE_OBJ_TYPE 2
111 #define ATTRIBUTE_OBJ_VALUE 4
114 * Extract and store an attribute
116 static bool extract_attribute(int oid
, chunk_t object
, u_int level
,
117 scep_attributes_t
*attrs
)
119 asn1_t type
= ASN1_EOC
;
120 const char *name
= "none";
124 case OID_PKCS9_CONTENT_TYPE
:
126 name
= "contentType";
128 case OID_PKCS9_SIGNING_TIME
:
130 name
= "signingTime";
132 case OID_PKCS9_MESSAGE_DIGEST
:
133 type
= ASN1_OCTET_STRING
;
134 name
= "messageDigest";
136 case OID_PKI_MESSAGE_TYPE
:
137 type
= ASN1_PRINTABLESTRING
;
138 name
= "messageType";
141 type
= ASN1_PRINTABLESTRING
;
144 case OID_PKI_FAIL_INFO
:
145 type
= ASN1_PRINTABLESTRING
;
148 case OID_PKI_SENDER_NONCE
:
149 type
= ASN1_OCTET_STRING
;
150 name
= "senderNonce";
152 case OID_PKI_RECIPIENT_NONCE
:
153 type
= ASN1_OCTET_STRING
;
154 name
= "recipientNonce";
156 case OID_PKI_TRANS_ID
:
157 type
= ASN1_PRINTABLESTRING
;
164 if (type
== ASN1_EOC
)
167 if (!asn1_parse_simple_object(&object
, type
, level
+1, name
))
172 case OID_PKCS9_CONTENT_TYPE
:
174 case OID_PKCS9_SIGNING_TIME
:
176 case OID_PKCS9_MESSAGE_DIGEST
:
178 case OID_PKI_MESSAGE_TYPE
:
182 for (m
= SCEP_CertRep_MSG
; m
< SCEP_Unknown_MSG
; m
++)
184 if (strncmp(msgType_values
[m
], object
.ptr
, object
.len
) == 0)
188 DBG_log("messageType: %s", msgType_names
[attrs
->msgType
])
196 for (s
= SCEP_SUCCESS
; s
< SCEP_UNKNOWN
; s
++)
198 if (strncmp(pkiStatus_values
[s
], object
.ptr
, object
.len
) == 0)
199 attrs
->pkiStatus
= s
;
202 DBG_log("pkiStatus: %s", pkiStatus_names
[attrs
->pkiStatus
])
206 case OID_PKI_FAIL_INFO
:
208 && *object
.ptr
>= '0' && *object
.ptr
<= '4')
210 attrs
->failInfo
= (failInfo_t
)(*object
.ptr
- '0');
212 if (attrs
->failInfo
!= SCEP_unknown_REASON
)
213 plog("failInfo: %s", failInfo_reasons
[attrs
->failInfo
]);
215 case OID_PKI_SENDER_NONCE
:
216 attrs
->senderNonce
= object
;
218 case OID_PKI_RECIPIENT_NONCE
:
219 attrs
->recipientNonce
= object
;
221 case OID_PKI_TRANS_ID
:
222 attrs
->transID
= object
;
228 * Parse X.501 attributes
230 bool parse_attributes(chunk_t blob
, scep_attributes_t
*attrs
)
232 asn1_parser_t
*parser
;
234 int oid
= OID_UNKNOWN
;
236 bool success
= FALSE
;
238 parser
= asn1_parser_create(attributesObjects
, blob
);
239 DBG(DBG_CONTROL
| DBG_PARSING
,
240 DBG_log("parsing attributes")
243 while (parser
->iterate(parser
, &objectID
, &object
))
247 case ATTRIBUTE_OBJ_TYPE
:
248 oid
= asn1_known_oid(object
);
250 case ATTRIBUTE_OBJ_VALUE
:
251 if (!extract_attribute(oid
, object
, parser
->get_level(parser
), attrs
))
257 success
= parser
->success(parser
);
260 parser
->destroy(parser
);
265 * Generates a unique fingerprint of the pkcs10 request
266 * by computing an MD5 hash over it
268 chunk_t
scep_generate_pkcs10_fingerprint(chunk_t pkcs10
)
270 char digest_buf
[HASH_SIZE_MD5
];
271 chunk_t digest
= chunk_from_buf(digest_buf
);
274 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD5
);
275 hasher
->get_hash(hasher
, pkcs10
, digest_buf
);
276 hasher
->destroy(hasher
);
278 return chunk_to_hex(digest
, NULL
, FALSE
);
282 * Generate a transaction id as the MD5 hash of an public key
283 * the transaction id is also used as a unique serial number
285 void scep_generate_transaction_id(public_key_t
*key
, chunk_t
*transID
,
286 chunk_t
*serialNumber
)
288 char digest_buf
[HASH_SIZE_MD5
];
289 chunk_t digest
= chunk_from_buf(digest_buf
);
290 chunk_t keyEncoding
= chunk_empty
, keyInfo
;
295 key
->get_encoding(key
, KEY_PUB_ASN1_DER
, &keyEncoding
);
297 keyInfo
= asn1_wrap(ASN1_SEQUENCE
, "cm",
298 asn1_algorithmIdentifier(OID_RSA_ENCRYPTION
),
299 asn1_bitstring("m", keyEncoding
));
301 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, HASH_MD5
);
302 hasher
->get_hash(hasher
, keyInfo
, digest_buf
);
303 hasher
->destroy(hasher
);
306 /* is the most significant bit of the digest set? */
307 msb_set
= (*digest
.ptr
& 0x80) == 0x80;
309 /* allocate space for the serialNumber */
310 serialNumber
->len
= msb_set
+ digest
.len
;
311 serialNumber
->ptr
= malloc(serialNumber
->len
);
313 /* the serial number as the two's complement of the digest */
314 pos
= serialNumber
->ptr
;
319 memcpy(pos
, digest
.ptr
, digest
.len
);
321 /* the transaction id is the serial number in hex format */
322 transID
->len
= 2*digest
.len
;
323 transID
->ptr
= malloc(transID
->len
+ 1);
324 datatot(digest
.ptr
, digest
.len
, 16, transID
->ptr
, transID
->len
+ 1);
328 * Builds a transId attribute
330 chunk_t
scep_transId_attribute(chunk_t transID
)
332 return asn1_wrap(ASN1_SEQUENCE
, "cm"
334 , asn1_wrap(ASN1_SET
, "m"
335 , asn1_simple_object(ASN1_PRINTABLESTRING
, transID
)
341 * Builds a messageType attribute
343 chunk_t
scep_messageType_attribute(scep_msg_t m
)
346 (u_char
*)msgType_values
[m
],
347 strlen(msgType_values
[m
])
350 return asn1_wrap(ASN1_SEQUENCE
, "cm"
351 , ASN1_messageType_oid
352 , asn1_wrap(ASN1_SET
, "m"
353 , asn1_simple_object(ASN1_PRINTABLESTRING
, msgType
)
359 * Builds a senderNonce attribute
361 chunk_t
scep_senderNonce_attribute(void)
363 const size_t nonce_len
= 16;
364 u_char nonce_buf
[nonce_len
];
365 chunk_t senderNonce
= { nonce_buf
, nonce_len
};
368 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
369 rng
->get_bytes(rng
, nonce_len
, nonce_buf
);
372 return asn1_wrap(ASN1_SEQUENCE
, "cm"
373 , ASN1_senderNonce_oid
374 , asn1_wrap(ASN1_SET
, "m"
375 , asn1_simple_object(ASN1_OCTET_STRING
, senderNonce
)
381 * Builds a pkcs7 enveloped and signed scep request
383 chunk_t
scep_build_request(chunk_t data
, chunk_t transID
, scep_msg_t msg
,
384 const x509cert_t
*enc_cert
, int enc_alg
,
385 const x509cert_t
*signer_cert
, int digest_alg
,
386 private_key_t
*private_key
)
388 chunk_t envelopedData
, attributes
, request
;
390 envelopedData
= pkcs7_build_envelopedData(data
, enc_cert
, enc_alg
);
392 attributes
= asn1_wrap(ASN1_SET
, "mmmmm"
393 , pkcs7_contentType_attribute()
394 , pkcs7_messageDigest_attribute(envelopedData
396 , scep_transId_attribute(transID
)
397 , scep_messageType_attribute(msg
)
398 , scep_senderNonce_attribute());
400 request
= pkcs7_build_signedData(envelopedData
, attributes
401 , signer_cert
, digest_alg
, private_key
);
402 free(envelopedData
.ptr
);
403 free(attributes
.ptr
);
408 * Converts a binary request to base64 with 64 characters per line
409 * newline and '+' characters are escaped by %0A and %2B, respectively
411 static char* escape_http_request(chunk_t req
)
413 char *escaped_req
= NULL
;
419 /* compute and allocate the size of the base64-encoded request */
420 int len
= 1 + 4*((req
.len
+ 2)/3);
421 char *encoded_req
= malloc(len
);
423 /* do the base64 conversion */
424 len
= datatot(req
.ptr
, req
.len
, 64, encoded_req
, len
);
426 /* compute newline characters to be inserted every 64 characters */
427 lines
= (len
- 2) / 64;
429 /* count number of + characters to be escaped */
437 escaped_req
= malloc(len
+ 3*(lines
+ plus
));
439 /* escape special characters in the request */
446 memcpy(p2
, "%0A", 3);
452 memcpy(p2
, "%2B", 3);
468 * Send a SCEP request via HTTP and wait for a response
470 bool scep_http_request(const char *url
, chunk_t pkcs7
, scep_op_t op
,
471 bool http_get_request
, chunk_t
*response
)
475 char *complete_url
= NULL
;
477 /* initialize response */
478 *response
= chunk_empty
;
481 DBG_log("sending scep request to '%s'", url
)
484 if (op
== SCEP_PKI_OPERATION
)
486 const char operation
[] = "PKIOperation";
488 if (http_get_request
)
490 char *escaped_req
= escape_http_request(pkcs7
);
492 /* form complete url */
493 len
= strlen(url
) + 20 + strlen(operation
) + strlen(escaped_req
) + 1;
494 complete_url
= malloc(len
);
495 snprintf(complete_url
, len
, "%s?operation=%s&message=%s"
496 , url
, operation
, escaped_req
);
499 status
= lib
->fetcher
->fetch(lib
->fetcher
, complete_url
, response
,
500 FETCH_HTTP_VERSION_1_0
,
501 FETCH_REQUEST_HEADER
, "Pragma:",
502 FETCH_REQUEST_HEADER
, "Host:",
503 FETCH_REQUEST_HEADER
, "Accept:",
508 /* form complete url */
509 len
= strlen(url
) + 11 + strlen(operation
) + 1;
510 complete_url
= malloc(len
);
511 snprintf(complete_url
, len
, "%s?operation=%s", url
, operation
);
513 status
= lib
->fetcher
->fetch(lib
->fetcher
, complete_url
, response
,
514 FETCH_REQUEST_DATA
, pkcs7
,
515 FETCH_REQUEST_TYPE
, "",
516 FETCH_REQUEST_HEADER
, "Expect:",
520 else /* SCEP_GET_CA_CERT */
522 const char operation
[] = "GetCACert";
524 /* form complete url */
525 len
= strlen(url
) + 32 + strlen(operation
) + 1;
526 complete_url
= malloc(len
);
527 snprintf(complete_url
, len
, "%s?operation=%s&message=CAIdentifier"
530 status
= lib
->fetcher
->fetch(lib
->fetcher
, complete_url
, response
,
535 return (status
== SUCCESS
);
538 err_t
scep_parse_response(chunk_t response
, chunk_t transID
, contentInfo_t
*data
,
539 scep_attributes_t
*attrs
, x509cert_t
*signer_cert
)
543 if (!pkcs7_parse_signedData(response
, data
, NULL
, &attributes
, signer_cert
))
545 return "error parsing the scep response";
547 if (!parse_attributes(attributes
, attrs
))
549 return "error parsing the scep response attributes";
551 if (!chunk_equals(transID
, attrs
->transID
))
553 return "transaction ID of scep response does not match";