Implement DisableOwnDataBox and EnableOwnDataBox
[libisds.git] / src / crypto.c
blob37087af80abc28211cf50a4eb44b8341c339c634
1 #define _XOPEN_SOURCE 500 /* strdup from string.h */
2 #include "isds_priv.h"
3 #include "utils.h"
4 #include "gcrypt.h"
6 #ifdef ISDS_USE_KSBA
7 #include <ksba.h>
8 #endif
10 #include <gpgme.h>
11 #include <locale.h>
13 /* Inicialize libgrcypt if not yet done by application or other library.
14 * @return IE_SUCCESS if everything is O.k. */
15 _hidden isds_error init_gcrypt(void) {
16 const char *gcrypt_version;
18 /* Check version and initialize gcrypt */
19 gcrypt_version = gcry_check_version(NULL);
20 if (!gcrypt_version) {
21 isds_log(ILF_SEC, ILL_CRIT, _("Could not check gcrypt version\n"));
22 return IE_ERROR;
25 /* Finalize initialization if not yet done */
26 if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
27 /* Disable secure memory */
28 /* TODO: Allow it when implementing key authentication */
29 gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
30 /* Finish initialization */
31 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
34 isds_log(ILF_SEC, ILL_INFO, _("gcrypt version in use: %s\n"),
35 gcrypt_version);
37 return IE_SUCCESS;
41 /* Computes hash from @input with @length and store it into @hash.
42 * The hash algoritm is defined inside @hash.
43 * @input is input block to hash
44 * @length is @input block length in bytes
45 * @hash input algoritm, output hash value and hash length; hash value will be
46 * reallocated, it's always valid pointer or NULL (before and after call) */
47 _hidden isds_error compute_hash(const void *input, const size_t length,
48 struct isds_hash *hash) {
49 int g_algorithm;
50 void *buffer;
52 if ((length != 0 && !input) || !hash) return IE_INVAL;
54 isds_log(ILF_SEC, ILL_DEBUG,
55 _("Data hash requested, length=%zu, content:\n%*s\n"
56 "End of data to hash\n"), length, length, input);
58 /* Select algorithm */
59 switch (hash->algorithm) {
60 case HASH_ALGORITHM_MD5: g_algorithm = GCRY_MD_MD5; break;
61 case HASH_ALGORITHM_SHA_1: g_algorithm = GCRY_MD_SHA1; break;
62 case HASH_ALGORITHM_SHA_256: g_algorithm = GCRY_MD_SHA256; break;
63 case HASH_ALGORITHM_SHA_512: g_algorithm = GCRY_MD_SHA512; break;
64 default: return IE_NOTSUP;
67 /* Test it's available */
68 if (gcry_md_test_algo(g_algorithm)) return IE_NOTSUP;
70 /* Get known the hash length and allocate buffer for hash value */
71 hash->length = gcry_md_get_algo_dlen(g_algorithm);
72 buffer = realloc(hash->value, hash->length);
73 if (!buffer) return IE_NOMEM;
74 hash->value = buffer;
76 /* Compute the hash */
77 gcry_md_hash_buffer(g_algorithm, hash->value, (length)?input:"", length);
79 return IE_SUCCESS;
83 /* Inicialize GPGME.
84 * @return IE_SUCCESS if everything is O.k. */
85 _hidden isds_error init_gpgme(void) {
86 const char *gpgme_version;
88 /* Check version and initialize GPGME */
89 gpgme_version = gpgme_check_version(NULL);
90 if (!gpgme_version) {
91 isds_log(ILF_SEC, ILL_CRIT, _("GPGME initialization failed\n"));
92 return IE_ERROR;
95 isds_log(ILF_SEC, ILL_INFO, _("GPGME version in use: %s\n"),
96 gpgme_version);
97 /* Needed to propagate locale to remote processes like pinentry */
98 gpgme_set_locale (NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
99 #ifdef LC_MESSAGES
100 gpgme_set_locale (NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL));
101 #endif
103 /* Check for engines */
104 if (gpgme_engine_check_version(GPGME_PROTOCOL_CMS)) {
105 isds_log(ILF_SEC, ILL_CRIT, _("GPGME does not support CMS\n"));
106 return IE_ERROR;
109 return IE_SUCCESS;
112 /* Free CMS data buffer allocated inside extract_cms_data().
113 * This is necesary because GPGME.
114 * @buffer is pointer to memory to free */
115 _hidden void cms_data_free(void *buffer) {
116 #ifdef ISDS_USE_KSBA
117 free(buffer);
118 #else
119 if (buffer) gpgme_free(buffer);
120 #endif
124 /* Extract data from CMS (successor of PKCS#7)
125 * @context is session context
126 * @cms is input block with CMS structure
127 * @cms_length is @cms block length in bytes
128 * @data are automatically reallocated bit stream with data found in @cms
129 * You must free them with cms_data_free().
130 * @data_length is length of @data in bytes */
131 _hidden isds_error extract_cms_data(struct isds_ctx *context,
132 const void *cms, const size_t cms_length,
133 void **data, size_t *data_length) {
134 isds_error err = IE_SUCCESS;
136 if (!cms || !data || !data_length) return IE_INVAL;
138 zfree(*data);
139 *data_length = 0;
141 #ifdef ISDS_USE_KSBA
142 ksba_cms_t cms_handler = NULL;
143 ksba_reader_t cms_reader = NULL;
144 ksba_writer_t cms_writer = NULL;
145 gpg_error_t gerr;
146 char gpg_error_string[128];
147 ksba_stop_reason_t stop_reason;
149 if (ksba_cms_new(&cms_handler)) {
150 isds_log_message(context, _("Could not allocate CMS parser handler"));
151 err = IE_NOMEM;
152 goto leave;
154 if (ksba_reader_new(&cms_reader)) {
155 isds_log_message(context, _("Could not allocate CMS reader"));
156 err = IE_ERROR;
157 goto leave;
159 if (ksba_reader_set_mem(cms_reader, cms, cms_length)) {
160 isds_log_message(context,
161 _("Could not bind CMS reader to PKCS#7 structure"));
162 err = IE_ERROR;
163 goto leave;
165 if (ksba_writer_new(&cms_writer)) {
166 isds_log_message(context, _("Could not allocate CMS writer"));
167 err = IE_ERROR;
168 goto leave;
170 if (ksba_writer_set_mem(cms_writer, 0)) {
171 isds_log_message(context,
172 _("Could not bind CMS reader to PKCS#7 structure"));
173 err = IE_ERROR;
174 goto leave;
176 if (ksba_cms_set_reader_writer(cms_handler, cms_reader, cms_writer)) {
177 isds_log_message(context,
178 _("Could not register CMS reader to CMS handler"));
179 err = IE_ERROR;
180 goto leave;
184 /* FIXME: This cycle stops with: Missing action on KSBA_CT_SIGNED_DATA
185 * I don't know how to program the KSBA cycle.
186 * TODO: Use gpgme's verify call to extract data. The only problem is it
187 * gpgme verifies signature always. We don't need it now and it's slow.
188 * We should find out how to use KSBA. */
189 do {
190 gerr = ksba_cms_parse(cms_handler, &stop_reason);
191 if (gerr) {
192 gpg_strerror_r(gerr, gpg_error_string, sizeof(gpg_error_string));
193 gpg_error_string[sizeof(gpg_error_string)/sizeof(char) - 1] = '\0';
194 isds_printf_message(context,
195 _("Error while parsing PKCS#7 structure: %s"),
196 gpg_error_string);
197 return IE_ERROR;
199 if (stop_reason == KSBA_SR_BEGIN_DATA) {
200 isds_log(ILF_SEC, ILL_DEBUG, _("CMS: Data begining found\n"));
202 if (stop_reason == KSBA_SR_GOT_CONTENT) {
203 char *type;
204 switch (ksba_cms_get_content_type(cms_handler, 0)) {
205 case KSBA_CT_NONE: type = _("uknown data"); break;
206 case KSBA_CT_DATA: type = _("plain data"); break;
207 case KSBA_CT_SIGNED_DATA: type = _("signed data"); break;
208 case KSBA_CT_ENVELOPED_DATA:
209 type = _("encypted data by session key"); break;
210 case KSBA_CT_DIGESTED_DATA: type = _("digest data"); break;
211 case KSBA_CT_ENCRYPTED_DATA: type = _("encryoted data"); break;
212 case KSBA_CT_AUTH_DATA: type = _("auth data"); break;
213 default: type = _("other data");
215 isds_log(ILF_SEC, ILL_DEBUG, _("CMS: Data type: %s\n"), type);
217 if (stop_reason == KSBA_SR_END_DATA) {
218 isds_log(ILF_SEC, ILL_DEBUG, _("CMS: Data end found\n"));
220 } while (stop_reason != KSBA_SR_READY);
222 *data = ksba_writer_snatch_mem(cms_writer, data_length);
223 if (!*data) {
224 isds_log_message(context, _("Getting CMS writer buffer failed"));
225 err = IE_ERROR;
226 goto leave;
229 leave:
230 ksba_cms_release(cms_handler);
231 ksba_writer_release(cms_writer);
232 ksba_reader_release(cms_reader);
233 #else /* ndef ISDS_USE_KSBA */
234 gpgme_ctx_t gctx = NULL;
235 gpgme_error_t gerr;
236 char gpgme_error_string[128];
237 gpgme_data_t cms_handler = NULL, plain_handler = NULL;
239 #define GET_GPGME_ERROR_STRING \
240 gpgme_strerror_r(gerr, gpgme_error_string, sizeof(gpgme_error_string)); \
241 gpgme_error_string[sizeof(gpgme_error_string)/sizeof(char) - 1] = '\0'; \
243 #define FAIL_ON_GPGME_ERROR(code, message) \
244 if (code) { \
245 GET_GPGME_ERROR_STRING; \
246 isds_printf_message(context, message, gpgme_error_string); \
247 if ((code) == GPG_ERR_ENOMEM) err = IE_NOMEM; \
248 else err = IE_ERROR; \
249 goto leave; \
252 /* Create GPGME context */
253 gerr = gpgme_new(&gctx);
254 FAIL_ON_GPGME_ERROR(gerr, _("Could not create GPGME context: %s"));
256 gerr = gpgme_set_protocol(gctx, GPGME_PROTOCOL_CMS);
257 FAIL_ON_GPGME_ERROR(gerr,
258 _("Could not set CMS protocol for GPGME context: %s"));
260 /* Create data handlers */
261 gerr = gpgme_data_new_from_mem(&cms_handler, cms, cms_length, 0);
262 FAIL_ON_GPGME_ERROR(gerr, _("Could not create data handler for "
263 "signed message in CMS structure: %s"));
264 gerr = gpgme_data_set_encoding(cms_handler, GPGME_DATA_ENCODING_BINARY);
265 FAIL_ON_GPGME_ERROR(gerr, _("Could not explain to GPGME "
266 "that CMS structure was packed in DER binary format: %s"));
268 gerr = gpgme_data_new(&plain_handler);
269 FAIL_ON_GPGME_ERROR(gerr, _("Could not create data handler for "
270 "plain message extracted from CMS structure: %s"));
272 /* Verify signature */
273 gerr = gpgme_op_verify(gctx, cms_handler, NULL, plain_handler);
274 if (gerr) {
275 GET_GPGME_ERROR_STRING;
276 isds_printf_message(context,
277 _("CMS verification failed: %s"),
278 gpgme_error_string);
279 err = IE_ERROR;
280 goto leave;
283 /* Get extracted plain message
284 * XXX: One must free *data with gpgme_free() because of clashing
285 * possibly different allocators. */
286 *data = gpgme_data_release_and_get_mem(plain_handler, data_length);
287 plain_handler = NULL;
288 if (!*data) {
289 /* No data or error occured */
290 isds_printf_message(context,
291 _("Could not get plain data from GPGME "
292 "after verifying CMS structure"));
293 err = IE_ERROR;
294 goto leave;
297 leave:
298 if (gctx) gpgme_release(gctx);
299 if (plain_handler) gpgme_data_release(plain_handler);
300 if (cms_handler) gpgme_data_release(cms_handler);
301 #undef FAIL_ON_GPGME_ERROR
302 #undef GET_GPGME_ERROR_STRING
303 #endif /* ndef ISDS_USE_KSBA */
304 return err;