1 /*
2 Copyright (C) 2011-2014 Yubico AB.  All rights reserved.
3 
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are
6 met:
7 
8    1. Redistributions of source code must retain the above copyright
9       notice, this list of conditions and the following disclaimer.
10 
11    2. Redistributions in binary form must reproduce the above
12       copyright notice, this list of conditions and the following
13       disclaimer in the documentation and/or other materials provided
14       with the distribution.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "yubikeyutil.h"
30 #include <yubikey.h>
31 #include <QDebug>
32 #include <QRegExp>
33 #ifdef Q_OS_WIN
34 #include "crandom.h"
35 #endif
36 
~YubiKeyUtil()37 YubiKeyUtil::~YubiKeyUtil() {
38 }
39 
hexModhexDecode(unsigned char * result,size_t * resultLen,const char * str,size_t strLen,size_t minSize,size_t maxSize,bool modhex)40 int YubiKeyUtil::hexModhexDecode(unsigned char *result, size_t *resultLen,
41                                  const char *str, size_t strLen,
42                                  size_t minSize, size_t maxSize,
43                                  bool modhex)
44 {
45     if ((strLen % 2 != 0) || (strLen < minSize) || (strLen > maxSize)) {
46         *resultLen = 0;
47         return -1;
48     }
49 
50     *resultLen = strLen / 2;
51     if (modhex) {
52         if (yubikey_modhex_p(str)) {
53             yubikey_modhex_decode((char *)result, str, *resultLen);
54             return 1;
55         }
56     } else {
57         if (yubikey_hex_p(str)) {
58             yubikey_hex_decode((char *)result, str, *resultLen);
59             return 1;
60         }
61     }
62 
63     return 0;
64 }
65 
hexModhexEncode(char * result,size_t * resultLen,const unsigned char * str,size_t strLen,bool modhex)66 int YubiKeyUtil::hexModhexEncode(char *result, size_t *resultLen,
67                                  const unsigned char *str, size_t strLen,
68                                  bool modhex)
69 {
70     *resultLen = strLen * 2;
71     if (modhex) {
72         yubikey_modhex_encode((char *)result, (char *)str, strLen);
73         return 1;
74     } else {
75         yubikey_hex_encode((char *)result, (char *)str, strLen);
76         return 1;
77     }
78 
79     return 0;
80 }
81 
qstrHexEncode(const unsigned char * str,size_t strLen)82 QString YubiKeyUtil::qstrHexEncode(const unsigned char *str, size_t strLen) {
83     char result[strLen * 2 + 1];
84     size_t resultLen;
85     memset(&result, 0, sizeof(result));
86 
87     int rc = hexModhexEncode(result, &resultLen, str, strLen, false);
88 
89     if(rc > 0) {
90         qDebug() << "hex encoded string: " << QString(result) << sizeof(result);
91         return QString::fromLocal8Bit(result);
92     }
93 
94     return QString("");
95 }
96 
qstrHexDecode(unsigned char * result,size_t * resultLen,const QString & str)97 void YubiKeyUtil::qstrHexDecode(unsigned char *result, size_t *resultLen,
98                                 const QString &str) {
99 
100     if(str.size() % 2 != 0) {
101         return;
102     }
103 
104     char hex[MAX_SIZE];
105     YubiKeyUtil::qstrToRaw(hex, sizeof(hex), str);
106     size_t hexLen = strlen(hex);
107 
108     //Hex decode
109     hexModhexDecode(result, resultLen,
110                     hex, hexLen,
111                     0, MAX_SIZE,
112                     false);
113 }
114 
qstrModhexEncode(const unsigned char * str,size_t strLen)115 QString YubiKeyUtil::qstrModhexEncode(const unsigned char *str, size_t strLen) {
116     char result[strLen * 2 + 1];
117     size_t resultLen;
118     memset(&result, 0, sizeof(result));
119 
120     int rc = hexModhexEncode(result, &resultLen, str, strLen, true);
121 
122     if(rc > 0) {
123         qDebug() << "modhex encoded string: " << QString(result) << sizeof(result);
124         return QString::fromLocal8Bit(result);
125     }
126 
127     return QString("");
128 }
129 
qstrModhexDecode(unsigned char * result,size_t * resultLen,const QString & str)130 void YubiKeyUtil::qstrModhexDecode(unsigned char *result, size_t *resultLen,
131                                    const QString &str) {
132 
133     if(str.size() % 2 != 0) {
134         *resultLen = 0;
135         return;
136     }
137 
138     char modhex[MAX_SIZE];
139     YubiKeyUtil::qstrToRaw(modhex, sizeof(modhex), str);
140     size_t modhexLen = strlen(modhex);
141 
142     //Hex decode
143     hexModhexDecode(result, resultLen,
144                     modhex, modhexLen,
145                     0, MAX_SIZE,
146                     true);
147 }
148 
qstrDecDecode(unsigned char * result,size_t * resultLen,const QString & str)149 void YubiKeyUtil::qstrDecDecode(unsigned char *result, size_t *resultLen,
150                                 const QString &str) {
151     if(str.size() % 2 != 0) {
152         *resultLen = 0;
153         return;
154     }
155 
156     *resultLen = str.size() / 2;
157 
158     for(size_t i = 0; i < *resultLen; i++) {
159         unsigned char val = str.mid(i * 2, 2).toInt();
160         result[i] = ((val / 10) << 4) | (val % 10);
161     }
162 }
163 
qstrToRaw(char * result,size_t resultLen,const QString & str)164 void YubiKeyUtil::qstrToRaw(char *result, size_t resultLen,
165                             const QString &str) {
166     QByteArray strByteArr = str.toLocal8Bit();
167 
168     size_t strLen = strByteArr.size() + 1;
169     strLen = (resultLen < strLen)? resultLen : strLen;
170 
171     memset(result, 0, strLen);
172     strncpy(result, (char *) strByteArr.data(), strLen);
173 }
174 
qstrClean(QString * str,size_t maxSize,bool reverse)175 void YubiKeyUtil::qstrClean(QString *str, size_t maxSize, bool reverse) {
176     *str = str->toLower();
177 
178     QRegExp rx("[^0-9a-f]");
179     *str = str->replace(rx, QString(""));
180 
181     if(maxSize > 0) {
182         if(reverse) {
183             *str = str->rightJustified(maxSize, '0', true);
184         } else {
185             *str = str->leftJustified(maxSize, '0', true);
186         }
187     }
188 }
189 
qstrModhexClean(QString * str,size_t maxSize,bool reverse)190 void YubiKeyUtil::qstrModhexClean(QString *str, size_t maxSize, bool reverse) {
191     *str = str->toLower();
192 
193     QRegExp rx("[^b-lnrt-v]");
194     *str = str->replace(rx, QString(""));
195 
196     if(maxSize > 0) {
197         if(reverse) {
198             *str = str->rightJustified(maxSize, 'c', true);
199         } else {
200             *str = str->leftJustified(maxSize, 'c', true);
201         }
202     }
203 }
204 
generateRandom(unsigned char * result,size_t resultLen)205 int YubiKeyUtil::generateRandom(unsigned char *result, size_t resultLen) {
206     size_t bufSize = resultLen;
207 
208     unsigned char buf[bufSize];
209     memset(&buf, 0, sizeof(buf));
210 
211     size_t bufLen = 0;
212 
213 #ifdef Q_OS_WIN
214     CRandom random;
215     random.getRand(buf, bufSize);
216 
217     bufLen = sizeof(buf);
218 #else
219     const char *random_places[] = {
220         "/dev/srandom",
221         "/dev/urandom",
222         "/dev/random",
223         0
224     };
225 
226     const char **random_place;
227 
228     for (random_place = random_places; *random_place; random_place++) {
229         FILE *random_file = fopen(*random_place, "r");
230         if (random_file) {
231             size_t read_bytes = 0;
232 
233             while (read_bytes < bufSize) {
234                 size_t n = fread(&buf[read_bytes],
235                                  1, bufSize - read_bytes,
236                                  random_file);
237                 read_bytes += n;
238             }
239 
240             fclose(random_file);
241 
242             bufLen = sizeof(buf);
243 
244             break; /* from for loop */
245         }
246     }
247 #endif
248 
249     if(bufLen > 0) {
250         memcpy(result, buf, bufLen);
251         return 1;
252     }
253 
254     return 0;
255 }
256 
generateRandomHex(size_t resultLen)257 QString YubiKeyUtil::generateRandomHex(size_t resultLen) {
258     QString result("");
259 
260     if (resultLen % 2 != 0) {
261         return result;
262     }
263 
264     size_t bufSize = resultLen / 2;
265     unsigned char buf[bufSize];
266     memset(&buf, 0, sizeof(buf));
267 
268     if(generateRandom(buf, bufSize) > 0) {
269         result = qstrHexEncode(buf, bufSize);
270     }
271 
272     return result;
273 }
274 
generateRandomModhex(size_t resultLen)275 QString YubiKeyUtil::generateRandomModhex(size_t resultLen) {
276     QString result("");
277 
278     if (resultLen % 2 != 0) {
279         return result;
280     }
281 
282     size_t bufSize = resultLen / 2;
283     unsigned char buf[bufSize];
284     memset(&buf, 0, sizeof(buf));
285 
286     if(generateRandom(buf, bufSize) > 0) {
287         result = qstrModhexEncode(buf, bufSize);
288     }
289 
290     return result;
291 }
292 
getNextHex(size_t resultLen,const QString & str,int scheme)293 QString YubiKeyUtil::getNextHex(size_t resultLen,
294                                 const QString &str, int scheme) {
295     QString result("");
296 
297     qDebug() << "str = " << str
298             << " len = " << str.length();
299 
300     switch(scheme) {
301     case GEN_SCHEME_FIXED:
302         result = str;
303         break;
304 
305     case GEN_SCHEME_INCR:
306         {
307             //Hex clean
308             QString hexStr(str);
309             qstrClean(&hexStr, resultLen);
310 
311             //Hex decode
312             unsigned char hexDecoded[MAX_SIZE];
313             size_t hexDecodedLen = 0;
314             memset(&hexDecoded, 0, sizeof(hexDecoded));
315 
316             qstrHexDecode(hexDecoded, &hexDecodedLen, hexStr);
317             if(hexDecodedLen <= 0) {
318                 break;
319             }
320 
321             qDebug() << "hexDecoded = " << QString((char*)hexDecoded)
322                     << " len = " << hexDecodedLen;
323 
324             //Increment
325             for (int i = hexDecodedLen; i--; ) {
326                 if (++hexDecoded[i]) {
327                     break;
328                 }
329             }
330 
331             //Hex encode
332             result = qstrHexEncode(hexDecoded, hexDecodedLen);
333 
334             qDebug() << "hexEncoded = " << result
335                     << " len = " << result.size();
336         }
337         break;
338 
339     case GEN_SCHEME_RAND:
340         result = generateRandomHex(resultLen);
341         break;
342     }
343 
344     return result;
345 }
346 
getNextModhex(size_t resultLen,const QString & str,int scheme)347 QString YubiKeyUtil::getNextModhex(size_t resultLen,
348                                    const QString &str, int scheme) {
349     QString tmpStr(str);
350     qstrModhexClean(&tmpStr, resultLen);
351     unsigned char result[resultLen];
352     size_t len;
353     QString hex;
354     qstrModhexDecode(result, &len, tmpStr);
355     if(len == 0) {
356         return "";
357     }
358     hex = qstrHexEncode(result, len);
359     hex = getNextHex(resultLen, hex, scheme);
360     qstrHexDecode(result, &len, hex);
361     return qstrModhexEncode(result, len);
362 }
363