Support BLISS signatures with SHA-3 hash
[strongswan.git] / src / pki / commands / acert.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Copyright (C) 2015 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include <time.h>
18 #include <errno.h>
19
20 #include "pki.h"
21
22 #include <utils/debug.h>
23 #include <asn1/asn1.h>
24 #include <collections/linked_list.h>
25 #include <credentials/certificates/certificate.h>
26 #include <credentials/certificates/x509.h>
27 #include <credentials/certificates/ac.h>
28
29 /**
30 * Issue an attribute certificate
31 */
32 static int acert()
33 {
34 cred_encoding_type_t form = CERT_ASN1_DER;
35 hash_algorithm_t digest = HASH_UNKNOWN;
36 certificate_t *ac = NULL, *cert = NULL, *issuer =NULL;
37 private_key_t *private = NULL;
38 public_key_t *public = NULL;
39 char *file = NULL, *hex = NULL, *issuercert = NULL, *issuerkey = NULL;
40 char *error = NULL, *keyid = NULL;
41 linked_list_t *groups;
42 chunk_t serial = chunk_empty, encoding = chunk_empty;
43 time_t not_before, not_after, lifetime = 24 * 60 * 60;
44 char *datenb = NULL, *datena = NULL, *dateform = NULL;
45 rng_t *rng;
46 char *arg;
47
48 groups = linked_list_create();
49
50 while (TRUE)
51 {
52 switch (command_getopt(&arg))
53 {
54 case 'h':
55 goto usage;
56 case 'g':
57 if (!enum_from_name(hash_algorithm_short_names, arg, &digest))
58 {
59 error = "invalid --digest type";
60 goto usage;
61 }
62 continue;
63 case 'i':
64 file = arg;
65 continue;
66 case 'm':
67 groups->insert_last(groups, arg);
68 continue;
69 case 'c':
70 issuercert = arg;
71 continue;
72 case 'k':
73 issuerkey = arg;
74 continue;
75 case 'x':
76 keyid = arg;
77 continue;
78 case 'l':
79 lifetime = atoi(arg) * 60 * 60;
80 if (!lifetime)
81 {
82 error = "invalid --lifetime value";
83 goto usage;
84 }
85 continue;
86 case 'D':
87 dateform = arg;
88 continue;
89 case 'F':
90 datenb = arg;
91 continue;
92 case 'T':
93 datena = arg;
94 continue;
95 case 's':
96 hex = arg;
97 continue;
98 case 'f':
99 if (!get_form(arg, &form, CRED_CERTIFICATE))
100 {
101 error = "invalid output format";
102 goto usage;
103 }
104 continue;
105 case EOF:
106 break;
107 default:
108 error = "invalid --acert option";
109 goto usage;
110 }
111 break;
112 }
113
114 if (!calculate_lifetime(dateform, datenb, datena, lifetime,
115 &not_before, &not_after))
116 {
117 error = "invalid --not-before/after datetime";
118 goto usage;
119 }
120
121 if (!issuercert)
122 {
123 error = "--issuercert is required";
124 goto usage;
125 }
126 if (!issuerkey && !keyid)
127 {
128 error = "--issuerkey or --issuerkeyid is required";
129 goto usage;
130 }
131
132 issuer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
133 BUILD_FROM_FILE, issuercert, BUILD_END);
134 if (!issuer)
135 {
136 error = "parsing issuer certificate failed";
137 goto end;
138 }
139 public = issuer->get_public_key(issuer);
140 if (!public)
141 {
142 error = "extracting issuer certificate public key failed";
143 goto end;
144 }
145 if (issuerkey)
146 {
147 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
148 public->get_type(public),
149 BUILD_FROM_FILE, issuerkey, BUILD_END);
150 }
151 else
152 {
153 chunk_t chunk;
154
155 chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
156 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
157 BUILD_PKCS11_KEYID, chunk, BUILD_END);
158 free(chunk.ptr);
159 }
160 if (!private)
161 {
162 error = "loading issuer private key failed";
163 goto end;
164 }
165 if (digest == HASH_UNKNOWN)
166 {
167 digest = get_default_digest(private);
168 }
169 if (!private->belongs_to(private, public))
170 {
171 error = "issuer private key does not match issuer certificate";
172 goto end;
173 }
174
175 if (hex)
176 {
177 serial = chunk_from_hex(chunk_create(hex, strlen(hex)), NULL);
178 }
179 else
180 {
181 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
182 if (!rng)
183 {
184 error = "no random number generator found";
185 goto end;
186 }
187 if (!rng_allocate_bytes_not_zero(rng, 8, &serial, FALSE))
188 {
189 error = "failed to generate serial number";
190 rng->destroy(rng);
191 goto end;
192 }
193 serial.ptr[0] &= 0x7F;
194 rng->destroy(rng);
195 }
196
197 if (file)
198 {
199 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
200 BUILD_FROM_FILE, file, BUILD_END);
201 }
202 else
203 {
204 set_file_mode(stdin, CERT_ASN1_DER);
205 if (!chunk_from_fd(0, &encoding))
206 {
207 fprintf(stderr, "%s: ", strerror(errno));
208 error = "reading public key failed";
209 goto end;
210 }
211 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
212 BUILD_BLOB, encoding, BUILD_END);
213 chunk_free(&encoding);
214 }
215 if (!cert)
216 {
217 error = "parsing user certificate failed";
218 goto end;
219 }
220
221 ac = lib->creds->create(lib->creds,
222 CRED_CERTIFICATE, CERT_X509_AC,
223 BUILD_CERT, cert,
224 BUILD_NOT_BEFORE_TIME, not_before,
225 BUILD_NOT_AFTER_TIME, not_after,
226 BUILD_SERIAL, serial,
227 BUILD_AC_GROUP_STRINGS, groups,
228 BUILD_SIGNING_CERT, issuer,
229 BUILD_SIGNING_KEY, private,
230 BUILD_END);
231 if (!ac)
232 {
233 error = "generating attribute certificate failed";
234 goto end;
235 }
236 if (!ac->get_encoding(ac, form, &encoding))
237 {
238 error = "encoding attribute certificate failed";
239 goto end;
240 }
241 set_file_mode(stdout, form);
242 if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
243 {
244 error = "writing attribute certificate key failed";
245 goto end;
246 }
247
248 end:
249 DESTROY_IF(ac);
250 DESTROY_IF(cert);
251 DESTROY_IF(issuer);
252 DESTROY_IF(public);
253 DESTROY_IF(private);
254 groups->destroy(groups);
255 free(encoding.ptr);
256 free(serial.ptr);
257
258 if (error)
259 {
260 fprintf(stderr, "%s\n", error);
261 return 1;
262 }
263 return 0;
264
265 usage:
266 groups->destroy(groups);
267 return command_usage(error);
268 }
269
270 /**
271 * Register the command.
272 */
273 static void __attribute__ ((constructor))reg()
274 {
275 command_register((command_t) {
276 acert, 'z', "acert",
277 "issue an attribute certificate",
278 {"[--in file] [--group name]* --issuerkey file|--issuerkeyid hex",
279 " --issuercert file [--serial hex] [--lifetime hours]",
280 " [--not-before datetime] [--not-after datetime] [--dateform form]",
281 "[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
282 "[--outform der|pem]"},
283 {
284 {"help", 'h', 0, "show usage information"},
285 {"in", 'i', 1, "holder certificate, default: stdin"},
286 {"group", 'm', 1, "group membership string to include"},
287 {"issuercert", 'c', 1, "issuer certificate file"},
288 {"issuerkey", 'k', 1, "issuer private key file"},
289 {"issuerkeyid", 'x', 1, "keyid on smartcard of issuer private key"},
290 {"serial", 's', 1, "serial number in hex, default: random"},
291 {"lifetime", 'l', 1, "hours the acert is valid, default: 24"},
292 {"not-before", 'F', 1, "date/time the validity of the AC starts"},
293 {"not-after", 'T', 1, "date/time the validity of the AC ends"},
294 {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
295 {"digest", 'g', 1, "digest for signature creation, default: key-specific"},
296 {"outform", 'f', 1, "encoding of generated cert, default: der"},
297 }
298 });
299 }