/* * sm-common.c: Common cryptographic procedures related to * Secure Messaging * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #ifndef ENABLE_OPENSSL #error "Need OpenSSL" #endif #include #include #include #include "libopensc/opensc.h" #include "libopensc/asn1.h" #include "libopensc/log.h" #include "sm-common.h" #if OPENSSL_VERSION_NUMBER < 0x30000000L /* * From crypto/des/des_locl.h of OpenSSL . */ #define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \ l|=((DES_LONG)(*((c)++)))<< 8L, \ l|=((DES_LONG)(*((c)++)))<<16L, \ l|=((DES_LONG)(*((c)++)))<<24L) #define c2ln(c,l1,l2,n) { \ c+=n; \ l1=l2=0; \ switch (n) { \ case 8: l2 =((DES_LONG)(*(--(c))))<<24L; \ /* fall through */ \ case 7: l2|=((DES_LONG)(*(--(c))))<<16L; \ /* fall through */ \ case 6: l2|=((DES_LONG)(*(--(c))))<< 8L; \ /* fall through */ \ case 5: l2|=((DES_LONG)(*(--(c)))); \ /* fall through */ \ case 4: l1 =((DES_LONG)(*(--(c))))<<24L; \ /* fall through */ \ case 3: l1|=((DES_LONG)(*(--(c))))<<16L; \ /* fall through */ \ case 2: l1|=((DES_LONG)(*(--(c))))<< 8L; \ /* fall through */ \ case 1: l1|=((DES_LONG)(*(--(c)))); \ } \ } #define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ *((c)++)=(unsigned char)(((l)>>24L)&0xff)) /* * Inspired by or taken from OpenSSL crypto/des/cbc3_enc.c */ static void DES_3cbc_encrypt(DES_cblock *input, DES_cblock *output, long length, DES_key_schedule *ks1, DES_key_schedule *ks2, DES_cblock *iv, int enc) { int off=((int)length-1)/8; long l8=((length+7)/8)*8; DES_cblock icv_out; memset(&icv_out, 0, sizeof(icv_out)); if (enc == DES_ENCRYPT) { DES_cbc_encrypt((unsigned char*)input, (unsigned char*)output,length,ks1,iv,enc); DES_cbc_encrypt((unsigned char*)output, (unsigned char*)output,l8,ks2,iv,!enc); DES_cbc_encrypt((unsigned char*)output, (unsigned char*)output,l8,ks1,iv,enc); if ((unsigned)length >= sizeof(DES_cblock)) memcpy(icv_out,output[off],sizeof(DES_cblock)); } else { if ((unsigned)length >= sizeof(DES_cblock)) memcpy(icv_out,input[off],sizeof(DES_cblock)); DES_cbc_encrypt((unsigned char*)input, (unsigned char*)output,l8,ks1,iv,enc); DES_cbc_encrypt((unsigned char*)output, (unsigned char*)output,l8,ks2,iv,!enc); DES_cbc_encrypt((unsigned char*)output, (unsigned char*)output,length,ks1,iv,enc); } memcpy(*iv,icv_out,sizeof(DES_cblock)); } #else #include /* The single-DES algorithm is not available in the default provider anymore * so we need to load the legacy provider. This is not done on the application * start, but only as needed */ OSSL_PROVIDER *legacy_provider = NULL; #endif DES_LONG DES_cbc_cksum_3des_emv96(const unsigned char *in, DES_cblock *output, long length, unsigned char *key, const_DES_cblock *ivec) { register long l=length; unsigned char *out = &(*output)[0]; const unsigned char *iv = &(*ivec)[0]; #if OPENSSL_VERSION_NUMBER < 0x30000000L register DES_LONG tout0,tout1,tin0,tin1; DES_LONG tin[2]; DES_cblock kk, k2; DES_key_schedule ks,ks2; memcpy(&kk, key, 8); memcpy(&k2, key + 8, 8); DES_set_key_unchecked(&kk,&ks); DES_set_key_unchecked(&k2,&ks2); c2l(iv,tout0); c2l(iv,tout1); for (; l>8; l-=8) { if (l >= 16) { c2l(in,tin0); c2l(in,tin1); } else c2ln(in,tin0,tin1,l); tin0^=tout0; tin[0]=tin0; tin1^=tout1; tin[1]=tin1; DES_encrypt1((DES_LONG *)tin, &ks, DES_ENCRYPT); tout0=tin[0]; tout1=tin[1]; } if (l == 8) { c2l(in,tin0); c2l(in,tin1); } else c2ln(in,tin0,tin1,l); tin0^=tout0; tin[0]=tin0; tin1^=tout1; tin[1]=tin1; DES_encrypt3((DES_LONG *)tin, &ks, &ks2, &ks); tout1=tin[1]; if (out != NULL) { l2c(tout0,out); l2c(tout1,out); } /* Transform the data in tout1 so that it will match the return value that the MIT Kerberos mit_des_cbc_cksum API returns. */ tout1 = ((tout1 >> 24L) & 0x000000FF) | ((tout1 >> 8L) & 0x0000FF00) | ((tout1 << 8L) & 0x00FF0000) | ((tout1 << 24L) & 0xFF000000); return(tout1); #else EVP_CIPHER_CTX *cctx = NULL; unsigned char outv[8], tmpout[4]; int tmplen; /* Prepare IV */ memcpy(outv, iv, sizeof outv); cctx = EVP_CIPHER_CTX_new(); if (l > 8) { if (legacy_provider == NULL) { legacy_provider = OSSL_PROVIDER_load(NULL, "legacy"); } if (!EVP_EncryptInit_ex2(cctx, EVP_des_cbc(), key, iv, NULL)) { EVP_CIPHER_CTX_free(cctx); return SC_ERROR_INTERNAL; } /* Disable padding, otherwise it will fail to decrypt non-padded inputs */ EVP_CIPHER_CTX_set_padding(cctx, 0); for (; l > 8; l -= 8, in += 8) { if (!EVP_EncryptUpdate(cctx, outv, &tmplen, in, 8)) { EVP_CIPHER_CTX_free(cctx); return SC_ERROR_INTERNAL; } } if (!EVP_EncryptFinal_ex(cctx, outv + tmplen, &tmplen)) { EVP_CIPHER_CTX_free(cctx); return SC_ERROR_INTERNAL; } } /* We need to return first 4 bytes from here */ memcpy(tmpout, outv, 4); if (!EVP_EncryptInit_ex2(cctx, EVP_des_ede_cbc(), key, outv, NULL)) { EVP_CIPHER_CTX_free(cctx); return SC_ERROR_INTERNAL; } /* Disable padding, otherwise it will fail to decrypt non-padded inputs */ EVP_CIPHER_CTX_set_padding(cctx, 0); if (!EVP_EncryptUpdate(cctx, outv, &tmplen, in, l)) { EVP_CIPHER_CTX_free(cctx); return SC_ERROR_INTERNAL; } if (!EVP_EncryptFinal_ex(cctx, outv + tmplen, &tmplen)) { EVP_CIPHER_CTX_free(cctx); return SC_ERROR_INTERNAL; } if (out != NULL) { memcpy(out, tmpout, 4); memcpy(out+4, outv+4, 4); } EVP_CIPHER_CTX_free(cctx); return ((outv[7] << 0L) & 0x000000FF) | ((outv[6] << 8L) & 0x0000FF00) | ((outv[5] << 16L) & 0x00FF0000) | ((outv[4] << 24L) & 0xFF000000); #endif } DES_LONG DES_cbc_cksum_3des(const unsigned char *in, DES_cblock *output, long length, unsigned char *key, const_DES_cblock *ivec) { register long l=length; unsigned char *out = &(*output)[0]; const unsigned char *iv = &(*ivec)[0]; #if OPENSSL_VERSION_NUMBER < 0x30000000L register DES_LONG tout0,tout1,tin0,tin1; DES_LONG tin[2]; DES_cblock kk, k2; DES_key_schedule ks,ks2; memcpy(&kk, key, 8); memcpy(&k2, key + 8, 8); DES_set_key_unchecked(&kk,&ks); DES_set_key_unchecked(&k2,&ks2); c2l(iv, tout0); c2l(iv, tout1); for (; l>0; l-=8) { if (l >= 8) { c2l(in,tin0); c2l(in,tin1); } else c2ln(in,tin0,tin1,l); tin0^=tout0; tin[0]=tin0; tin1^=tout1; tin[1]=tin1; DES_encrypt3((DES_LONG *)tin, &ks, &ks2, &ks); /* fix 15/10/91 eay - thanks to keithr@sco.COM */ tout0=tin[0]; tout1=tin[1]; } if (out != NULL) { l2c(tout0,out); l2c(tout1,out); } /* Transform the data in tout1 so that it will match the return value that the MIT Kerberos mit_des_cbc_cksum API returns. */ tout1 = ((tout1 >> 24L) & 0x000000FF) | ((tout1 >> 8L) & 0x0000FF00) | ((tout1 << 8L) & 0x00FF0000) | ((tout1 << 24L) & 0xFF000000); return(tout1); #else EVP_CIPHER_CTX *cctx = NULL; unsigned char outv[8]; int tmplen; /* Prepare IV */ memcpy(outv, iv, sizeof outv); cctx = EVP_CIPHER_CTX_new(); if (!EVP_EncryptInit_ex2(cctx, EVP_des_ede_cbc(), key, iv, NULL)) { EVP_CIPHER_CTX_free(cctx); return SC_ERROR_INTERNAL; } /* Disable padding, otherwise it will fail to decrypt non-padded inputs */ EVP_CIPHER_CTX_set_padding(cctx, 0); for (; l > 0; l -= 8, in += 8) { if (!EVP_EncryptUpdate(cctx, outv, &tmplen, in, 8)) { EVP_CIPHER_CTX_free(cctx); return SC_ERROR_INTERNAL; } } if (!EVP_EncryptFinal_ex(cctx, outv + tmplen, &tmplen)) { EVP_CIPHER_CTX_free(cctx); return SC_ERROR_INTERNAL; } if (out != NULL) { memcpy(out, outv, sizeof outv); } EVP_CIPHER_CTX_free(cctx); return ((outv[7] << 0L) & 0x000000FF) | ((outv[6] << 8L) & 0x0000FF00) | ((outv[5] << 16L) & 0x00FF0000) | ((outv[4] << 24L) & 0xFF000000); #endif } int sm_encrypt_des_ecb3(unsigned char *key, unsigned char *data, int data_len, unsigned char **out, int *out_len) { #if OPENSSL_VERSION_NUMBER < 0x30000000L int ii; DES_cblock kk,k2; DES_key_schedule ks,ks2; #else EVP_CIPHER_CTX *cctx = NULL; int tmplen; #endif if (!out || !out_len) return -1; *out_len = data_len + 7; *out_len -= *out_len % 8; *out = malloc(*out_len); if (!(*out)) return -1; #if OPENSSL_VERSION_NUMBER < 0x30000000L memcpy(&kk, key, 8); memcpy(&k2, key + 8, 8); DES_set_key_unchecked(&kk,&ks); DES_set_key_unchecked(&k2,&ks2); for (ii=0; ii= 0; ii--) { *(ssc + ii) += 1; if (*(ssc + ii) != 0) break; } }