OBS: sync with latest versions
[siplcs.git] / src / core / sipe-cert-crypto-nss.c
blob255b23b478a0501586a8b695377a531aaa33b685
1 /**
2 * @file sipe-cert-crypto-nss.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2012 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 /**
24 * Certificate routines implementation based on NSS.
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
31 #include <glib.h>
33 #ifdef HAVE_VALGRIND
34 #include <valgrind.h>
35 #endif
38 * Work around a compiler error in NSS 3.13.x. Let's hope they fix it for
39 * 3.14.x. See also: https://bugzilla.mozilla.org/show_bug.cgi?id=702090
41 #include "nss.h"
42 #if (NSS_VMAJOR == 3) && (NSS_VMINOR == 13)
43 #define __GNUC_MINOR __GNUC_MINOR__
44 #endif
46 #include "cert.h"
47 #include "cryptohi.h"
48 #include "keyhi.h"
49 #include "pk11pub.h"
51 #include "sipe-backend.h"
52 #include "sipe-cert-crypto.h"
54 struct sipe_cert_crypto {
55 SECKEYPrivateKey *private;
56 SECKEYPublicKey *public;
60 * This data structure is used in two different modes
62 * a) certificate generated by the server from our Certificate Request
64 * key_pair.private - reference to client private key, don't free!
65 * key_pair.public - reference to client public key, don't free!
66 * decoded - certificate as NSS data structure, must be freed
67 * raw - certificate as DER encoded binary, must be freed
68 * length - length of DER binary
70 * b) server certificate
72 * key_pair.private - NULL
73 * key_pair.public - reference to server public key, must be freed!
74 * decoded - certificate as NSS data structure, must be freed
75 * raw - NULL
76 * length - modulus length of server public key
78 struct certificate_nss {
79 struct sipe_cert_crypto key_pair;
80 CERTCertificate *decoded;
81 guchar *raw;
82 gsize length;
85 struct sipe_cert_crypto *sipe_cert_crypto_init(void)
87 PK11SlotInfo *slot = PK11_GetInternalKeySlot();
89 if (slot) {
90 PK11RSAGenParams rsaParams;
91 struct sipe_cert_crypto *scc = g_new0(struct sipe_cert_crypto, 1);
93 /* RSA parameters - should those be configurable? */
94 #ifdef HAVE_VALGRIND
96 * valgrind makes key pair generation extremely slow. At least
97 * on my system it takes longer for the default key size than
98 * the SIP server timeout and our next message will fail with
100 * Read error: Connection reset by peer (104)
102 * Let's reduce the key size when we detect valgrind.
104 if (RUNNING_ON_VALGRIND) {
105 rsaParams.keySizeInBits = 1024;
106 SIPE_DEBUG_INFO("sipe_cert_crypto_init: running on valgrind, reducing RSA key size to %d bits",
107 rsaParams.keySizeInBits);
108 } else
109 #endif
110 rsaParams.keySizeInBits = 2048;
111 rsaParams.pe = 65537;
113 SIPE_DEBUG_INFO_NOFORMAT("sipe_cert_crypto_init: generate key pair, this might take a while...");
114 scc->private = PK11_GenerateKeyPair(slot,
115 CKM_RSA_PKCS_KEY_PAIR_GEN,
116 &rsaParams,
117 &scc->public,
118 PR_FALSE, /* not permanent */
119 PR_TRUE, /* sensitive */
120 NULL);
121 if (scc->private) {
122 SIPE_DEBUG_INFO_NOFORMAT("sipe_cert_crypto_init: key pair generated");
123 PK11_FreeSlot(slot);
124 return(scc);
127 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_init: key generation failed");
128 g_free(scc);
129 PK11_FreeSlot(slot);
132 return(NULL);
135 void sipe_cert_crypto_free(struct sipe_cert_crypto *scc)
137 if (scc) {
138 if (scc->public)
139 SECKEY_DestroyPublicKey(scc->public);
140 if (scc->private)
141 SECKEY_DestroyPrivateKey(scc->private);
142 g_free(scc);
146 static gchar *sign_cert_or_certreq(CERTCertificate *cert,
147 CERTCertificateRequest *certreq,
148 SECKEYPrivateKey *private)
150 PRArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
151 gchar *base64 = NULL;
153 if (arena) {
154 SECItem *encoding = SEC_ASN1EncodeItem(arena,
155 NULL,
156 cert ?
157 (void *) cert :
158 (void *) certreq,
159 cert ?
160 SEC_ASN1_GET(CERT_CertificateTemplate) :
161 SEC_ASN1_GET(CERT_CertificateRequestTemplate));
163 if (encoding) {
164 SECOidTag signtag = SEC_GetSignatureAlgorithmOidTag(private->keyType,
165 SEC_OID_UNKNOWN);
167 if (signtag != SEC_OID_UNKNOWN) {
168 SECItem raw;
170 if (!SEC_DerSignData(arena,
171 &raw,
172 encoding->data,
173 encoding->len,
174 private,
175 signtag)) {
177 SIPE_DEBUG_INFO_NOFORMAT("sign_cert_or_certreq: successfully signed");
178 base64 = g_base64_encode(raw.data, raw.len);
180 } else {
181 SIPE_DEBUG_ERROR_NOFORMAT("sign_cert_or_certreq: signing failed");
183 } else {
184 SIPE_DEBUG_ERROR_NOFORMAT("sign_cert_or_certreq: can't find signature algorithm");
187 /* all memory allocated from "arena"
188 SECITEM_FreeItem(encoding, PR_TRUE); */
189 } else {
190 SIPE_DEBUG_ERROR_NOFORMAT("sign_cert_or_certreq: can't ASN.1 encode data");
193 PORT_FreeArena(arena, PR_TRUE);
194 } else {
195 SIPE_DEBUG_ERROR_NOFORMAT("sign_cert_or_certreq: can't allocate memory");
198 return(base64);
201 static CERTCertificateRequest *generate_request(struct sipe_cert_crypto *scc,
202 const gchar *subject)
204 SECItem *pkd;
205 CERTCertificateRequest *certreq = NULL;
207 if (!scc || !subject)
208 return(NULL);
210 pkd = SECKEY_EncodeDERSubjectPublicKeyInfo(scc->public);
211 if (pkd) {
212 CERTSubjectPublicKeyInfo *spki = SECKEY_DecodeDERSubjectPublicKeyInfo(pkd);
214 if (spki) {
215 gchar *cn = g_strdup_printf("CN=%s", subject);
216 CERTName *name = CERT_AsciiToName(cn);
217 g_free(cn);
219 if (name) {
220 certreq = CERT_CreateCertificateRequest(name,
221 spki,
222 NULL);
223 if (!certreq) {
224 SIPE_DEBUG_ERROR_NOFORMAT("generate_request: certreq creation failed");
227 CERT_DestroyName(name);
228 } else {
229 SIPE_DEBUG_ERROR_NOFORMAT("generate_request: subject name creation failed");
232 SECKEY_DestroySubjectPublicKeyInfo(spki);
233 } else {
234 SIPE_DEBUG_ERROR_NOFORMAT("generate_request: DER decode public key info failed");
237 SECITEM_FreeItem(pkd, PR_TRUE);
238 } else {
239 SIPE_DEBUG_ERROR_NOFORMAT("generate_request: DER encode failed");
242 return(certreq);
245 gchar *sipe_cert_crypto_request(struct sipe_cert_crypto *scc,
246 const gchar *subject)
248 gchar *base64 = NULL;
249 CERTCertificateRequest *certreq = generate_request(scc, subject);
251 if (certreq) {
252 base64 = sign_cert_or_certreq(NULL, certreq, scc->private);
253 CERT_DestroyCertificateRequest(certreq);
256 return(base64);
259 void sipe_cert_crypto_destroy(gpointer certificate)
261 struct certificate_nss *cn = certificate;
263 if (cn) {
264 /* imported server certificate - mode (b) */
265 if (!cn->raw && cn->key_pair.public)
266 SECKEY_DestroyPublicKey(cn->key_pair.public);
267 if (cn->decoded)
268 CERT_DestroyCertificate(cn->decoded);
269 g_free(cn->raw);
270 g_free(cn);
274 /* generates certificate_nss in mode (a) */
275 gpointer sipe_cert_crypto_decode(struct sipe_cert_crypto *scc,
276 const gchar *base64)
278 struct certificate_nss *cn = g_new0(struct certificate_nss, 1);
280 cn->raw = g_base64_decode(base64, &cn->length);
281 cn->decoded = CERT_DecodeCertFromPackage((char *) cn->raw, cn->length);
283 if (!cn->decoded) {
284 sipe_cert_crypto_destroy(cn);
285 return(NULL);
288 cn->key_pair = *scc;
290 return(cn);
293 /* generates certificate_nss in mode (b) */
294 gpointer sipe_cert_crypto_import(const guchar *raw,
295 gsize length)
297 struct certificate_nss *cn = g_new0(struct certificate_nss, 1);
299 /* cn->raw not needed as this is a server certificate */
300 cn->decoded = CERT_DecodeCertFromPackage((char *) raw, length);
302 if (!cn->decoded) {
303 sipe_cert_crypto_destroy(cn);
304 return(NULL);
307 cn->key_pair.public = CERT_ExtractPublicKey(cn->decoded);
309 if (!cn->key_pair.public) {
310 sipe_cert_crypto_destroy(cn);
311 return(NULL);
314 cn->length = SECKEY_PublicKeyStrength(cn->key_pair.public);
316 return(cn);
319 gboolean sipe_cert_crypto_valid(gpointer certificate,
320 guint offset)
322 struct certificate_nss *cn = certificate;
323 SECCertTimeValidity validity;
325 if (!cn)
326 return(FALSE);
328 validity = CERT_CheckCertValidTimes(cn->decoded,
329 /* PRTime unit is microseconds */
330 PR_Now() + offset * PR_USEC_PER_SEC,
331 PR_FALSE);
333 return((validity == secCertTimeValid) ||
335 * From certt.h: "validity could not be decoded from the
336 * cert, most likely because it was NULL"
338 * Let's assume if the server sends us such a certificate
339 * that it must be valid then...
341 (validity == secCertTimeUndetermined));
344 guint sipe_cert_crypto_expires(gpointer certificate)
346 struct certificate_nss *cn = certificate;
347 PRTime now, notAfter;
349 if (!cn ||
350 (CERT_GetCertTimes(cn->decoded,
351 &now, /* can't be NULL */
352 &notAfter) != SECSuccess))
353 return(0);
355 /* Sanity check */
356 now = PR_Now();
357 if (notAfter < now)
358 return(0);
360 /* PRTime unit is microseconds */
361 return((notAfter - now) / PR_USEC_PER_SEC);
364 gsize sipe_cert_crypto_raw_length(gpointer certificate)
366 return(((struct certificate_nss *) certificate)->length);
369 const guchar *sipe_cert_crypto_raw(gpointer certificate)
371 return(((struct certificate_nss *) certificate)->raw);
374 gpointer sipe_cert_crypto_public_key(gpointer certificate)
376 return(((struct certificate_nss *) certificate)->key_pair.public);
379 gsize sipe_cert_crypto_modulus_length(gpointer certificate)
381 return(((struct certificate_nss *) certificate)->length);
384 gpointer sipe_cert_crypto_private_key(gpointer certificate)
386 return(((struct certificate_nss *) certificate)->key_pair.private);
389 /* Create test certificate for internal key pair (ONLY USE FOR TEST CODE!!!) */
390 gpointer sipe_cert_crypto_test_certificate(struct sipe_cert_crypto *scc)
392 CERTCertificateRequest *certreq = generate_request(scc, "test@test.com");
393 struct certificate_nss *cn = NULL;
395 if (certreq) {
396 /* self-signed */
397 CERTName *issuer = CERT_AsciiToName("CN=test@test.com");
399 if (issuer) {
400 /* we really don't need this certificate for long... */
401 CERTValidity *validity = CERT_CreateValidity(PR_Now(),
402 PR_Now() + 600 * PR_USEC_PER_SEC);
404 if (validity) {
405 CERTCertificate *certificate = CERT_CreateCertificate(1,
406 issuer,
407 validity,
408 certreq);
410 if (certificate) {
411 SECOidTag signtag = SEC_GetSignatureAlgorithmOidTag(scc->private->keyType,
412 SEC_OID_UNKNOWN);
414 if ((signtag != SEC_OID_UNKNOWN) &&
415 (SECOID_SetAlgorithmID(certificate->arena,
416 &certificate->signature,
417 signtag, 0) == SECSuccess)) {
418 gchar *base64 = sign_cert_or_certreq(certificate,
419 NULL,
420 scc->private);
422 if (base64) {
423 cn = sipe_cert_crypto_decode(scc,
424 base64);
425 if (!cn) {
426 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: certificate decode failed");
429 g_free(base64);
430 } else {
431 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: certificate signing failed");
433 } else {
434 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: setting certificate signature algorithm ID failed");
437 CERT_DestroyCertificate(certificate);
438 } else {
439 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: certificate creation failed");
442 CERT_DestroyValidity(validity);
443 } else {
444 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: validity creation failed");
447 CERT_DestroyName(issuer);
448 } else {
449 SIPE_DEBUG_ERROR_NOFORMAT("sipe_cert_crypto_test_certificate: issuer name creation failed");
452 CERT_DestroyCertificateRequest(certreq);
455 return(cn);
459 Local Variables:
460 mode: c
461 c-file-style: "bsd"
462 indent-tabs-mode: t
463 tab-width: 8
464 End: