/**
* @file sipe-crypt-nss.c
*
* pidgin-sipe
*
* Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
* Copyright (C) 2010 pier11 <pier11@operamail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* Cypher routines implementation based on NSS.
* Includes: RC4, DES
*/
#include "glib.h"
#include "nss.h"
/*
* Work around a compiler error in NSS 3.13.x. Let's hope they fix it for
* 3.14.x. See also: https://bugzilla.mozilla.org/show_bug.cgi?id=702090
*/
#if (NSS_VMAJOR == 3) && (NSS_VMINOR == 13)
#define __GNUC_MINOR __GNUC_MINOR__
#endif
#include "pk11pub.h"
#include "sipe-common.h"
#include "sipe-backend.h"
#include "sipe-crypt.h"
/* NSS specific initialization/shutdown */
void sipe_crypto_init(SIPE_UNUSED_PARAMETER gboolean production_mode)
{
if (!NSS_IsInitialized()) {
/*
* I have a bad feeling about this: according to the NSS
* documentation, NSS can only be initialized once.
* Unfortunately there seems to be no way to initialize a
* "NSS context" that could then be used by the SIPE code
* to avoid colliding with other NSS users.
*
* This seems to work, so it'll have to do for now.
*
* It might also be required to move this to the backend
* so that the backend code can decide when it is OK to
* initialize NSS.
*/
NSS_NoDB_Init(".");
SIPE_DEBUG_INFO_NOFORMAT("NSS initialised");
}
}
void sipe_crypto_shutdown(void)
{
/* do nothing for NSS.
* We don't want accedently switch off NSS possibly used by other plugin -
* ssl-nss in Pidgin for example.
*/
}
/* PRIVATE methods */
static PK11Context*
sipe_crypt_ctx_create(CK_MECHANISM_TYPE cipherMech,
const guchar *key, gsize key_length,
const guchar *iv, gsize iv_length)
{
PK11SlotInfo* slot;
SECItem keyItem;
SECItem ivItem;
PK11SymKey* SymKey;
SECItem *SecParam;
PK11Context* EncContext;
/* For key */
slot = PK11_GetBestSlot(cipherMech, NULL);
keyItem.type = siBuffer;
keyItem.data = (unsigned char *)key;
keyItem.len = key_length;
SymKey = PK11_ImportSymKey(slot, cipherMech, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, NULL);
/* Parameter for crypto context */
ivItem.type = siBuffer;
ivItem.data = (unsigned char *)iv;
ivItem.len = iv_length;
SecParam = PK11_ParamFromIV(cipherMech, &ivItem);
EncContext = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, SymKey, SecParam);
PK11_FreeSymKey(SymKey);
SECITEM_FreeItem(SecParam, PR_TRUE);
PK11_FreeSlot(slot);
return EncContext;
}
static void
sipe_crypt_ctx_encrypt(PK11Context* EncContext, const guchar *in, gsize length, guchar *out)
{
int tmp1_outlen;
PK11_CipherOp(EncContext, out, &tmp1_outlen, length, (unsigned char *)in, length);
}
static void
sipe_crypt_ctx_destroy(PK11Context* EncContext)
{
PK11_DestroyContext(EncContext, PR_TRUE);
}
static void
sipe_crypt(CK_MECHANISM_TYPE cipherMech,
const guchar *key, gsize key_length,
const guchar *plaintext, gsize plaintext_length,
guchar *encrypted_text)
{
void *EncContext;
EncContext = sipe_crypt_ctx_create(cipherMech, key, key_length, NULL, 0);
sipe_crypt_ctx_encrypt(EncContext, plaintext, plaintext_length, encrypted_text);
sipe_crypt_ctx_destroy(EncContext);
}
/* PUBLIC methods */
void
sipe_crypt_des(const guchar *key,
const guchar *plaintext, gsize plaintext_length,
guchar *encrypted_text)
{
sipe_crypt(CKM_DES_ECB, key, 8, plaintext, plaintext_length, encrypted_text);
}
void
sipe_crypt_rc4(const guchar *key, gsize key_length,
const guchar *plaintext, gsize plaintext_length,
guchar *encrypted_text)
{
sipe_crypt(CKM_RC4, key, key_length, plaintext, plaintext_length, encrypted_text);
}
gboolean
sipe_crypt_rsa_encrypt(gpointer public, gsize modulus_length,
const guchar *plaintext,
guchar *encrypted_text)
{
SECStatus result = PK11_PubEncryptRaw(public,
encrypted_text, (guchar *) plaintext,
modulus_length, NULL);
return(result == SECSuccess);
}
gboolean
sipe_crypt_rsa_decrypt(gpointer private, gsize modulus_length,
const guchar *encrypted_text,
guchar *plaintext)
{
unsigned int length;
SECStatus result = PK11_PubDecryptRaw(private,
(guchar *) encrypted_text, &length, modulus_length,
plaintext, modulus_length);
return((result == SECSuccess) && (length == modulus_length));
}
guchar *sipe_crypt_rsa_sign(gpointer private,
const guchar *digest, gsize digest_length,
gsize *signature_length)
{
SECItem digItem;
SECItem sigItem;
SECStatus length;
length = PK11_SignatureLen(private);
if (length < 0) return(NULL);
/* digest to sign (= encrypt) with private key */
digItem.data = (guchar *) digest;
digItem.len = digest_length;
/* signature */
sigItem.data = g_malloc(length);
sigItem.len = length;
length = PK11_Sign(private, &sigItem, &digItem);
if (length != SECSuccess) {
g_free(sigItem.data);
return(NULL);
}
*signature_length = sigItem.len;
return(sigItem.data);
}
gboolean sipe_crypt_verify_rsa(gpointer public,
const guchar *digest, gsize digest_length,
const guchar *signature, gsize signature_length)
{
SECItem digItem;
SECItem sigItem;
/* digest to verify against */
digItem.data = (guchar *) digest;
digItem.len = digest_length;
/* signature to decrypt with public key -> digest to compare */
sigItem.data = (guchar *) signature;
sigItem.len = signature_length;
return(PK11_Verify(public, &sigItem, &digItem, NULL) == SECSuccess);
}
/* Stream RC4 cipher for file transfer */
gpointer
sipe_crypt_ft_start(const guchar *key)
{
return sipe_crypt_ctx_create(CKM_RC4, key, 16, NULL, 0);
}
void
sipe_crypt_ft_stream(gpointer context,
const guchar *in, gsize length,
guchar *out)
{
sipe_crypt_ctx_encrypt(context, in, length, out);
}
void
sipe_crypt_ft_destroy(gpointer context)
{
sipe_crypt_ctx_destroy(context);
}
/*
* Stream RC4 cipher for TLS
*
* basically the same as for FT, but with variable key length
*/
gpointer sipe_crypt_tls_start(const guchar *key, gsize key_length)
{
return sipe_crypt_ctx_create(CKM_RC4, key, key_length, NULL, 0);
}
void sipe_crypt_tls_stream(gpointer context,
const guchar *in, gsize length,
guchar *out)
{
sipe_crypt_ctx_encrypt(context, in, length, out);
}
void sipe_crypt_tls_destroy(gpointer context)
{
sipe_crypt_ctx_destroy(context);
}
/* Block AES-CBC cipher for TLS */
void sipe_crypt_tls_block(const guchar *key, gsize key_length,
const guchar *iv, gsize iv_length,
const guchar *in, gsize length,
guchar *out)
{
PK11Context* context = sipe_crypt_ctx_create(CKM_AES_CBC,
key, key_length,
iv, iv_length);
if (context) {
sipe_crypt_ctx_encrypt(context, in, length, out);
sipe_crypt_ctx_destroy(context);
}
}
/*
Local Variables:
mode: c
c-file-style: "bsd"
indent-tabs-mode: t
tab-width: 8
End:
*/