1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//--------------------------------------------------------------------------------------------------
18//
19//   Module Name: GeneratePassword.cc
20//
21//   Based on an IMEI and serverId input, the algorithm for generating the OMADM client password
22//   and server password actually has following three steps,
23//
24//   Step 1 Generate the client password key and server password key:
25//   char[] clientPasswordDict = new char[] { 0x0e, 0x06, 0x10,0x0c, 0x0a, 0x0e, 0x05, 0x0c,
26//                                            0x12, 0x0a, 0x0b, 0x06, 0x0d, 0x0e, 0x05 };
27//   char[] serverPasswordDict = new char[] { 0x0a, 0x06, 0x0e,0x0e, 0x0a, 0x0b, 0x06, 0x0e,
28//                                            0x0b, 0x04, 0x04, 0x07, 0x11, 0x0c, 0x0c };
29//
30//   It defines a client password dictionary and a server password dictionary which contain
31//   15 numbers, we use client password dictionary to generate client password key and server
32//   password dictionary to generate server password key.  Suppose IMEI string length is n, for
33//   each character in IMEI string {imei[i], 0<= i <n-3}, we generate two serial numbers using
34//   following calculation,
35//
36//   Serial1 += imei[i + 3] * dict[i];
37//   Serial2 += imei[i + 3] * imei[i + 2]*dict[i];
38//
39//   Note: Serial numbers are in decimal.
40//   Hence we get a password KEY which is Serial1+"-"+Serial2.
41//
42//   Step 2 Generate Temporary passwords:
43//   We generate a MD5 digest from IMEI+KEY+serverId, then we pick No. 2,7,8,12,25,30 characters
44//   from MD5 DigestStr , let is be md5key. Convert IMEI to 36 radix number, let it be newImei
45//   which is always 10 characters, then we get a temporary password which is md5key+newImei.
46//
47//   Step 3 Shuffle the temporary password:
48//   The last step is to shuffle the temporary password got from Step 2.
49//   Since the password length is 16 and n is the length which is equal to 16,
50//   let P1,P2,P3,P4,...,P[n/2]-1, P[n/2], P[n/2]+1 , ... , Pn are the characters in the
51//   temporary password string, we do following shuffle,
52//
53//   move P[n/2]+1 between P[n/2]-1 and P[n/2],
54//   ....
55//   move Pn before P1.
56//   Then we get Pn,P1,Pn-1,P2,....,P[n/2]-1,P[n/2]+1,P[n/2].
57//   Do the same shuffle three times, then after third time shuffle we get the password which
58//   is 16 characters string.
59//
60//   Usage: GeneratePassword [IMEI] [SERVER_ID]
61//
62//   Example: GeneratePassword 000000011234564 motorola
63//
64//   Default: IMEI = 123456789012345
65//       SERVER_ID = openwave.com
66//
67
68#include <stdio.h>          // printf()
69#include <stdlib.h>         // exit() malloc()
70#include <string.h>         // strcpy() strlen()
71#include "md5.h"
72
73#include "GeneratePassword.H"
74
75/**
76 *  Initialize all the client/server password dictionaries and other values.
77 */
78GeneratePassword::GeneratePassword() {
79
80  clientPasswordDict[0] = 0x0e;
81  clientPasswordDict[1] = 0x06;
82  clientPasswordDict[2] = 0x10;
83  clientPasswordDict[3] = 0x0c;
84  clientPasswordDict[4] = 0x0a;
85  clientPasswordDict[5] = 0x0e;
86  clientPasswordDict[6] = 0x05;
87  clientPasswordDict[7] = 0x0c;
88  clientPasswordDict[8] = 0x12;
89  clientPasswordDict[9] = 0x0a;
90  clientPasswordDict[10] = 0x0b;
91  clientPasswordDict[11] = 0x06;
92  clientPasswordDict[12] = 0x0d;
93  clientPasswordDict[13] = 0x0e;
94  clientPasswordDict[14] = 0x05;
95
96  serverPasswordDict[0] = 0x0a;
97  serverPasswordDict[1] = 0x06;
98  serverPasswordDict[2] = 0x0e;
99  serverPasswordDict[3] = 0x0e;
100  serverPasswordDict[4] = 0x0a;
101  serverPasswordDict[5] = 0x0b;
102  serverPasswordDict[6] = 0x06;
103  serverPasswordDict[7] = 0x0e;
104  serverPasswordDict[8] = 0x0b;
105  serverPasswordDict[9] = 0x04;
106  serverPasswordDict[10] = 0x04;
107  serverPasswordDict[11] = 0x07;
108  serverPasswordDict[12] = 0x11;
109  serverPasswordDict[13] = 0x0c;
110  serverPasswordDict[14] = 0x0c;
111
112  hexTable[0] = '0';
113  hexTable[1] = '1';
114  hexTable[2] = '2';
115  hexTable[3] = '3';
116  hexTable[4] = '4';
117  hexTable[5] = '5';
118  hexTable[6] = '6';
119  hexTable[7] = '7';
120  hexTable[8] = '8';
121  hexTable[9] = '9';
122  hexTable[10] = 'a';
123  hexTable[11] = 'b';
124  hexTable[12] = 'c';
125  hexTable[13] = 'd';
126  hexTable[14] = 'e';
127  hexTable[15] = 'f';
128
129  imei = "123456789012345";
130  serverId = "openwave.com";
131
132  char * tmpIMEI = "123456789012345";
133  char * tmpServerId = "openwave.com";
134
135  int len = sizeof(char) * strlen(tmpIMEI) + 1;
136  imei = (char *) malloc(len);
137  memset(imei, '\0', (len));
138  strcpy(imei, tmpIMEI);
139
140  len = sizeof(char) * strlen(tmpServerId) + 1;
141  serverId = (char *) malloc(len);
142  memset(serverId, '\0', (len));
143  strcpy(serverId, tmpServerId);
144
145  MD5_HASH_LENGTH = 16;
146}
147
148/**
149 *  Free all dynamic allocated memories.
150 */
151GeneratePassword::~GeneratePassword() {
152
153  if (imei != NULL) {
154    free(imei);
155    imei = NULL;
156  }
157
158  if (serverId != NULL) {
159    free(serverId);
160    serverId = NULL;
161  }
162}
163
164/**
165 *  Sets the sever ID for password generation
166 *
167 *  @param serverId the serverId that represent a DM server
168 */
169void GeneratePassword::setServerId(const char * sid) {
170  if (serverId != NULL) {
171    free(serverId);
172    serverId = NULL;
173  }
174
175  int len = sizeof(char) * strlen(sid) + 1;
176  serverId = (char *) malloc(len);
177  memset(serverId, '\0', len);
178  strcpy(serverId, sid);
179}
180
181/**
182 *  Sets the IMEI for password generation
183 *
184 *  @param aIMEI a phone identification number
185 */
186void GeneratePassword::setIMEI(const char * aIMEI) {
187
188  if (imei != NULL) {
189    free(imei);
190    imei = NULL;
191  }
192
193  int len = sizeof(char) * strlen(aIMEI) + 1;
194  imei = (char *) malloc(len);
195  memset(imei, '\0', len);
196  strcpy(imei, aIMEI);
197}
198
199/**
200 *  Returns the IMEI number.
201 *
202 *  @return a phone identification number
203 */
204char * GeneratePassword::getIMEI() {
205  return imei;
206}
207
208/**
209 *  Returns the server ID
210 *
211 *  @return serverId the serverId that represent a DM server
212 */
213char * GeneratePassword::getServerId() {
214  return serverId;
215}
216
217/**
218 *  Generate a client password key with a predefined client password dictionary
219 *  based on the IMEI.
220 *
221 *  @param imei the imei use to generate the key
222 *  @return the client password key
223 */
224char * GeneratePassword::generateClientPasswordKey(char * imei) {
225  return generateKeyFromDict(imei, clientPasswordDict);
226}
227
228/**
229 *  Generate a server password key with a predefined server password dictionary
230 *  based on the IMEI.
231 *
232 *  @param imei the imei use to generate the key
233 *  @return the server password key
234 */
235char * GeneratePassword::generateServerPasswordKey(char * imei) {
236  return generateKeyFromDict(imei, serverPasswordDict);
237}
238
239/**
240 *  Generate a client password using a generated client password key, the IMEI, and
241 *  the server ID.
242 *
243 *  @return the client password
244 */
245char * GeneratePassword::generateClientPassword() {
246  char * key = generateClientPasswordKey(imei);
247  return generatePassword(imei, serverId, key);
248}
249
250/**
251 *  Generate a server password using a generated server password key, the IMEI, and
252 *  the server ID.
253 *
254 *  @return the server password
255 */
256char * GeneratePassword::generateServerPassword() {
257  char * key = generateServerPasswordKey(imei);
258  return generatePassword(imei, serverId, key);
259}
260
261/**
262 *  Generate a client password using a generated client password key, the IMEI, and
263 *  the server ID.
264 *
265 *  @param imei a phone identification number
266 *  @param serverId the serverId that represent a DM server
267 *  @return the client password
268 */
269char * GeneratePassword::generateClientPassword(char * imei, char * serverId) {
270  char * key = generateClientPasswordKey(imei);
271  return generatePassword(imei, serverId, key);
272}
273
274/**
275 *  Generate a server password using a generated server password key, the IMEI, and
276 *  the server ID.
277 *
278 *  @param imei a phone identification number
279 *  @param serverId the serverId that represent a DM server
280 *  @return the server password
281 */
282char * GeneratePassword::generateServerPassword(char * imei, char * serverId) {
283  char * key = generateServerPasswordKey(imei);
284  return generatePassword(imei, serverId, key);
285}
286
287/**
288 *  Generate a key with given IMEI and password dictionary.
289 *  Suppose IMEI string length is n, for each character in IMEI string {imei[i], 0<= i <n-3},
290 *  we generate two serial numbers using following calculation,
291 *
292 *  Serial1 += imei[i + 3] * dict[i];
293 *  Serial2 += imei[i + 3] * imei[i + 2]*dict[i];
294 *
295 *  Note: Serial numbers are in decimal.
296 *  Hence we get a password KEY which is Serial1+"-"+Serial2.
297 *
298 *  @param imei a phone indentification number
299 *  @param dict[] a password dictionary
300 *  @return a password key
301 */
302char * GeneratePassword::generateKeyFromDict(char * imei, char dict[]) {
303  int i;
304  int length;
305  long serial1 = 0;
306  long serial2 = 0;
307  char * serial1_str;
308  char * serial2_str;
309  char * key;
310
311  length = strlen(imei);
312
313  for (i = 0; i < length - 3; i++) {
314    serial1 += imei[i + 3] * dict[i];
315    serial2 += imei[i + 3] * imei[i + 2] * dict[i];
316  }
317
318  serial1_str = (char *) malloc(sizeof(char) * (24));
319  serial2_str = (char *) malloc(sizeof(char) * (24));
320  sprintf(serial1_str, "%d", serial1);
321  sprintf(serial2_str, "%d", serial2);
322
323  key = (char *) malloc(sizeof(char) * (strlen(serial1_str) + strlen(serial2_str) + 2));
324  memset(key, '\0', (sizeof(char) * (strlen(serial1_str) + strlen(serial2_str) + 2)));
325
326  strcat(key, (const char *)serial1_str );
327  strcat(key, "-");
328  strcat(key, (const char *)serial2_str );
329
330  free(serial1_str);
331  serial1_str = NULL;
332  free(serial2_str);
333  serial2_str = NULL;
334
335  return key;
336}
337
338/**
339 * Convert an array of characters that represents a large number to a decimal number type
340 *
341 * @param input the array of characters thar represents a large number
342 * @return the decimal number
343 */
344long long GeneratePassword::convertChar2Long(char * input)
345{
346  char ch[2];
347  int i;
348  long long tmp;
349  tmp = 0;
350
351  for (i=0 ; i < strlen((const char *)input) ; i++ ) {
352    ch[0]=*(input+i);
353    ch[1]='\0';
354    tmp = (long long) ( ( (tmp) * 10) + atol((const char *)ch) );
355  }
356  return tmp;
357}
358
359/**
360 * Convert an array of characters that represents a number in decimal based to 36 based.
361 *
362 * @param target_imei the array of characters thar represents a number in decimal based
363 * @param the 36 based number represented by array of characters.
364 */
365char * GeneratePassword::get36BasedIMEI(char * target_imei) {
366
367  char NumericBaseData[]= "0123456789abcdefghijklmnopqrstuvwxyz";
368  char tmp_IMEI[11];
369  long long Quotient;
370  long Remainder;
371  int i;
372  char tmpchar;
373
374  char * IMEI36 = (char *) malloc(sizeof(char) * ( 10 + 1 ));
375  memset(IMEI36, '\0', (sizeof(char) * (10 + 1)));
376
377  long long IMEI = 0;
378  IMEI = convertChar2Long(target_imei);
379
380  i=0;
381  while ( IMEI > 0 ) {
382    Quotient = (long long)(IMEI/36);
383    Remainder = (long)(IMEI%36);
384    tmp_IMEI[i++] = NumericBaseData[Remainder];
385    IMEI = Quotient;
386  }
387  tmp_IMEI[i]='\0';
388
389  //If the length is <10 pad the remaining chracter to '0' to make the length 10
390  if( strlen(tmp_IMEI) < 10 ) {
391    for (i=strlen(tmp_IMEI); i<10 ; i++) {
392      tmp_IMEI[i]='0';
393    }
394    tmp_IMEI[i]='\0';
395  }
396
397  for( i=0 ; i < strlen(tmp_IMEI)/2 ; i++ ) {
398    tmpchar = tmp_IMEI[i];
399    tmp_IMEI[i] = tmp_IMEI[ strlen(tmp_IMEI)-i-1];
400    tmp_IMEI[ strlen(tmp_IMEI)-i-1] = tmpchar;
401  }
402
403  memcpy(IMEI36,tmp_IMEI,strlen(tmp_IMEI));
404  return IMEI36;
405}
406
407/**
408 *  Shuffle an array of characters.
409 *
410 *  let P1,P2,P3,P4,...,P[n/2]-1, P[n/2], P[n/2]+1 , ... , Pn
411 *  are the characters in the string, we do following shuffle,
412 *
413 *  move P[n/2]+1 between P[n/2]-1 and P[n/2],
414 *  ....
415 *  move Pn before P1.
416 *  Then we get
417 *  Pn,P1,Pn-1,P2,....,P[n/2]-1,P[n/2]+1,P[n/2].
418 *
419 *  @param buffer the string to be shuffle
420 */
421void GeneratePassword::shuffle(char & buffer) {
422
423  int length;
424  int secondHalfPos;
425  int insertPos;
426  char * buf;
427  char tmpchar;
428  int i;
429  int j;
430
431  insertPos = 0;
432  i=0;
433  j=0;
434  buf = & buffer;
435  length = strlen((char *)buf);
436  secondHalfPos = (length / 2);
437
438  for ( i= secondHalfPos ; i < length ; i++ ) {
439    tmpchar = (char)buf[i];
440    insertPos = (length - i - 1);
441    for ( j = i ; j > insertPos ; j-- ) {
442      buf[j] = buf[j-1];
443    }
444    buf[j] = tmpchar;
445  }
446}
447
448/**
449 * Convert the input data from a decimal based number to Heximal based number.
450 *
451 * @param data the decimal based number to be covert
452 * @return the Heximal based number
453 */
454char * GeneratePassword::encodeHex(char data[]) {
455
456  int i;
457  char tmpChar;
458  int len = MD5_HASH_LENGTH;
459  printf("LEN: %d\n", len);
460  int size = len * 2 + 1;
461  char * output = (char *) malloc(sizeof(char) * size);
462  memset(output, '\0', (sizeof(char) * size));
463
464  for ( i = 0; i < len ; i++) {
465    tmpChar = data[i];
466    output[2*i] = hexTable[ (tmpChar & 0x0F) ];		// Get low 4 bits
467    output[(2*i)+1] = hexTable[ ((tmpChar >> 4) & 0x0F ) ];	// Get high 4 bits
468  }
469  output[2*i] = '\0';
470
471  return output;
472}
473
474
475/**
476 *  Generate a password with given IMEI, serverID, and key.
477 *  We generate a MD5 digest from IMEI+KEY+serverId, then we pick No. 2,7,8,12,25,30 characters
478 *  from MD5 DigestStr , let is be md5key. Convert IMEI to 36 radix number, let it be newImei
479 *  which is always 10 characters, then we get a temporary password which is md5key+newImei.
480 *  Finally, we shuffle the temporary password three time.
481 *
482 *  @param imei the phone identification number
483 *  @param serverId the server ID of a DM server.
484 *  @param key the key needs to generate password
485 *  @return a password
486 */
487char * GeneratePassword::generatePassword(char * imei, char * serverId, char * key) {
488
489  char * MD5DigestStr = (char *) malloc(sizeof(char) * (strlen ((const char *)imei) + strlen(key) + strlen(serverId) +1));
490  memset( MD5DigestStr , '\0', (sizeof(char) * ( strlen ((const char *)imei) + strlen(key) + strlen(serverId) +1 ) ) );
491
492  strcpy( MD5DigestStr , (const char *)imei );
493  strcat( MD5DigestStr , key );
494  strcat( MD5DigestStr , serverId );
495
496  printf("Before MD5: %s\n", MD5DigestStr);
497
498  MD5_CTX md5_context;
499
500  char md5hash[MD5_HASH_LENGTH + 1];  /* Add 1 character for NULL */
501  memset(md5hash, '\0', (sizeof(char) * (MD5_HASH_LENGTH + 1)));
502
503  smlMD5Init(&md5_context);
504  smlMD5Update(&md5_context, (unsigned char *)MD5DigestStr,strlen(MD5DigestStr));
505  smlMD5Final((unsigned char*)md5hash, &md5_context);
506  md5hash[MD5_HASH_LENGTH] = 0;
507
508  free(MD5DigestStr );
509  MD5DigestStr =NULL;
510
511  printf("MD5 HASH: %s\n", md5hash);
512
513  char * MD5DigestStr32 = encodeHex(md5hash);
514
515  printf("Digest Str: %s\n", MD5DigestStr32);
516
517  // Pick only no. 2,7,8,12,25,30 characters from MD5 Digest String
518
519  MD5DigestStr = (char *) malloc(sizeof(char) * ( 6 + 1 ));
520  memset(MD5DigestStr, '\0', (sizeof(char) * ( 6 + 1)));
521
522  MD5DigestStr[0] = MD5DigestStr32[2];
523  MD5DigestStr[1] = MD5DigestStr32[7];
524  MD5DigestStr[2] = MD5DigestStr32[8];
525  MD5DigestStr[3] = MD5DigestStr32[12];
526  MD5DigestStr[4] = MD5DigestStr32[25];
527  MD5DigestStr[5] = MD5DigestStr32[30];
528
529  free(MD5DigestStr32 );
530  MD5DigestStr32 =NULL;
531
532  //Convert IMEI to 36(base) radixnumber to generate NewIMEI
533
534  char * IMEI36 = (char *) malloc(sizeof(char) * ( 10 + 1 ));
535  memset(IMEI36, '\0', (sizeof(char) * (10 + 1)));
536
537  IMEI36 = get36BasedIMEI(imei);
538
539  //Create Password
540
541  int PWLength = strlen (MD5DigestStr) + strlen((const char *)IMEI36);  //Length will be 16
542
543  char * password = (char *) malloc(sizeof(char) * ( PWLength + 1));
544  memset( password , '\0', (sizeof(char) * ( PWLength + 1 ) ) ); //this will be of 16 + 1
545
546  strcpy( password , MD5DigestStr );
547  strcat( password , (const char *)IMEI36 );
548
549  free(MD5DigestStr );
550  MD5DigestStr =NULL;
551
552  free(IMEI36);
553  IMEI36 =NULL;
554
555  free(MD5DigestStr32 );
556  MD5DigestStr32 =NULL;
557
558  printf("BUFFER: %s\n", password);
559
560  shuffle(*password);
561  shuffle(*password);
562  shuffle(*password);
563
564  free( key );
565  key=NULL;
566
567  return password;
568}
569