1 #define _XOPEN_SOURCE 500 /* strdup from string.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"));
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"),
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
) {
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
;
76 /* Compute the hash */
77 gcry_md_hash_buffer(g_algorithm
, hash
->value
, (length
)?input
:"", length
);
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
);
91 isds_log(ILF_SEC
, ILL_CRIT
, _("GPGME initialization failed\n"));
95 isds_log(ILF_SEC
, ILL_INFO
, _("GPGME version in use: %s\n"),
97 /* Needed to propagate locale to remote processes like pinentry */
98 gpgme_set_locale (NULL
, LC_CTYPE
, setlocale(LC_CTYPE
, NULL
));
100 gpgme_set_locale (NULL
, LC_MESSAGES
, setlocale(LC_MESSAGES
, NULL
));
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"));
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
) {
119 if (buffer
) gpgme_free(buffer
);
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
;
142 ksba_cms_t cms_handler
= NULL
;
143 ksba_reader_t cms_reader
= NULL
;
144 ksba_writer_t cms_writer
= NULL
;
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"));
154 if (ksba_reader_new(&cms_reader
)) {
155 isds_log_message(context
, _("Could not allocate CMS reader"));
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"));
165 if (ksba_writer_new(&cms_writer
)) {
166 isds_log_message(context
, _("Could not allocate CMS writer"));
170 if (ksba_writer_set_mem(cms_writer
, 0)) {
171 isds_log_message(context
,
172 _("Could not bind CMS reader to PKCS#7 structure"));
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"));
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. */
190 gerr
= ksba_cms_parse(cms_handler
, &stop_reason
);
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"),
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
) {
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
);
224 isds_log_message(context
, _("Getting CMS writer buffer failed"));
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
;
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) \
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; \
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
);
275 GET_GPGME_ERROR_STRING
;
276 isds_printf_message(context
,
277 _("CMS verification failed: %s"),
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
;
289 /* No data or error occured */
290 isds_printf_message(context
,
291 _("Could not get plain data from GPGME "
292 "after verifying CMS structure"));
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 */