blob: a43337c6526ff9b129d225b5b30990f67c3dac0c [file] [log] [blame]
Alexandre Lisionddd731e2014-01-31 11:50:08 -05001// Copyright (C) 2010 David Sugar, Tycho Softworks.
2//
3// This file is part of GNU uCommon C++.
4//
5// GNU uCommon C++ is free software: you can redistribute it and/or modify
6// it under the terms of the GNU Lesser General Public License as published
7// by the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// GNU uCommon C++ is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU Lesser General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public License
16// along with GNU uCommon C++. If not, see <http://www.gnu.org/licenses/>.
17
18#include "local.h"
19
20static const unsigned char *_salt = NULL;
21static unsigned _rounds = 1;
22
23void Cipher::Key::assign(const char *text, size_t size)
24{
25 assign(text, size, _salt, _rounds);
26}
27
28void Cipher::Key::options(const unsigned char *salt, unsigned rounds)
29{
30 _salt = salt;
31 _rounds = rounds;
32}
33
34void Cipher::Key::assign(const char *text, size_t size, const unsigned char *salt, unsigned rounds)
35{
36 if(!algotype || !hashtype)
37 return;
38
39 if(!size)
40 size = strlen((const char *)text);
41
42 if(!rounds)
43 rounds = _rounds;
44
45 if(!salt)
46 salt = _salt;
47
48 if(EVP_BytesToKey((const EVP_CIPHER*)algotype, (const EVP_MD*)hashtype, salt, (const unsigned char *)text, size, rounds, keybuf, ivbuf) < (int)keysize)
49 keysize = 0;
50}
51
52void Cipher::Key::set(const char *cipher, const char *digest)
53{
54 set(cipher);
55
56 // never use sha0...
57 if(eq_case(digest, "sha"))
58 digest = "sha1";
59
60 hashtype = EVP_get_digestbyname(digest);
61}
62
63void Cipher::Key::set(const char *cipher)
64{
65 char algoname[64];
66
67 clear();
68 String::set(algoname, sizeof(algoname), cipher);
69 char *fpart = strchr(algoname, '-');
70 char *lpart = strrchr(algoname, '-');
71
72 if(fpart && fpart == lpart)
73 strcpy(fpart, fpart + 1);
74
75 algotype = EVP_get_cipherbyname(algoname);
76
77 if(!algotype)
78 return;
79
80 keysize = EVP_CIPHER_key_length((const EVP_CIPHER*)algotype);
81 blksize = EVP_CIPHER_block_size((const EVP_CIPHER*)algotype);
82}
83
84
85bool Cipher::has(const char *id)
86{
87 // make sure cipher-bitsize forms without -mode do not fail...
88 char algoname[64];
89 String::set(algoname, sizeof(algoname), id);
90 char *fpart = strchr(algoname, '-');
91 char *lpart = strrchr(algoname, '-');
92
93 if(fpart && fpart == lpart)
94 strcpy(fpart, fpart + 1);
95
96 return (EVP_get_cipherbyname(algoname) != NULL);
97}
98
99void Cipher::push(unsigned char *address, size_t size)
100{
101}
102
103void Cipher::release(void)
104{
105 keys.clear();
106 if(context) {
107 EVP_CIPHER_CTX_cleanup((EVP_CIPHER_CTX*)context);
108 delete (EVP_CIPHER_CTX*)context;
109 context = NULL;
110 }
111}
112
113void Cipher::set(key_t key, mode_t mode, unsigned char *address, size_t size)
114{
115 release();
116
117 bufsize = size;
118 bufmode = mode;
119 bufaddr = address;
120
121 memcpy(&keys, key, sizeof(keys));
122 if(!keys.keysize)
123 return;
124
125 context = new EVP_CIPHER_CTX;
126 EVP_CIPHER_CTX_init((EVP_CIPHER_CTX *)context);
127 EVP_CipherInit_ex((EVP_CIPHER_CTX *)context, (EVP_CIPHER *)keys.algotype, NULL, keys.keybuf, keys.ivbuf, (int)mode);
128 EVP_CIPHER_CTX_set_padding((EVP_CIPHER_CTX *)context, 0);
129}
130
131size_t Cipher::put(const unsigned char *data, size_t size)
132{
133 int outlen;
134 size_t count = 0;
135
136 if(!bufaddr)
137 return 0;
138
139 if(size % keys.iosize())
140 return 0;
141
142 while(bufsize && size + bufpos > bufsize) {
143 size_t diff = bufsize - bufpos;
144 count += put(data, diff);
145 data += diff;
146 size -= diff;
147 }
148
149 if(!EVP_CipherUpdate((EVP_CIPHER_CTX *)context, bufaddr + bufpos, &outlen, data, size)) {
150 release();
151 return count;
152 }
153 bufpos += outlen;
154 count += outlen;
155 if(bufsize && bufpos >= bufsize) {
156 push(bufaddr, bufsize);
157 bufpos = 0;
158 }
159 return count;
160}
161
162size_t Cipher::pad(const unsigned char *data, size_t size)
163{
164 size_t padsize = 0;
165 unsigned char padbuf[64];
166 const unsigned char *ep;
167
168 if(!bufaddr)
169 return 0;
170
171 switch(bufmode) {
172 case DECRYPT:
173 if(size % keys.iosize())
174 return 0;
175 put(data, size);
176 ep = data + size - 1;
177 bufpos -= *ep;
178 size -= *ep;
179 break;
180 case ENCRYPT:
181 padsize = size % keys.iosize();
182 put(data, size - padsize);
183 if(padsize) {
184 memcpy(padbuf, data + size - padsize, padsize);
185 memset(padbuf + padsize, keys.iosize() - padsize, keys.iosize() - padsize);
186 size = (size - padsize) + keys.iosize();
187 }
188 else {
189 size += keys.iosize();
190 memset(padbuf, keys.iosize(), keys.iosize());
191 }
192
193 put((const unsigned char *)padbuf, keys.iosize());
194 zerofill(padbuf, sizeof(padbuf));
195 }
196
197 flush();
198 return size;
199}
200