opvault.c
changeset 58 1390348facc7
equal deleted inserted replaced
57:4974e5368a29 58:1390348facc7
       
     1 // https://support.1password.com/opvault-design
       
     2 
       
     3 #include <stdio.h>
       
     4 #include <stdlib.h>
       
     5 #include <string.h>
       
     6 #include <stdint.h>
       
     7 #include <unistd.h>
       
     8 #include <time.h>
       
     9 #include <errno.h>
       
    10 
       
    11 #include <openssl/bio.h>
       
    12 #include <openssl/evp.h>
       
    13 #include <openssl/hmac.h>
       
    14 #include <openssl/err.h>
       
    15 #include <openssl/conf.h>
       
    16 
       
    17 #include "cJSON.h"
       
    18 
       
    19 #define JSONVAR(typ, var, jstyp, json, name) typ var; { \
       
    20     cJSON *item = cJSON_GetObjectItem(json, name); \
       
    21     if (!item) { \
       
    22         fprintf(stderr, "No " name " field in profile.\n"); \
       
    23         return 0; \
       
    24     } \
       
    25     var = (typ) item->value##jstyp; \
       
    26 }
       
    27 
       
    28 static cJSON *load_json(const char *fname)
       
    29 {
       
    30     cJSON *retval = NULL;
       
    31     FILE *io = fopen(fname, "rb");
       
    32 
       
    33     if (io != NULL) {
       
    34         if (fseek(io, 0, SEEK_END) == 0) {
       
    35             long len = ftell(io);
       
    36             if ((len != -1) && (fseek(io, 0, SEEK_SET) == 0)) {
       
    37                 char *buf = (char *) malloc(len + 1);
       
    38                 if ((buf != NULL) && (fread(buf, len, 1, io) == 1)) {
       
    39                     char *json = buf;
       
    40                     json[len] = '\0';
       
    41 
       
    42                     // !!! FIXME: hack.
       
    43                     if (strncmp(json, "ld(", 3) == 0) {
       
    44                         json[len-2] = '\0';  // chop off ");" from end.
       
    45                         json += 3;  // skip past "ld(".
       
    46                         len -= 5;
       
    47                     } else if (strncmp(json, "loadFolders(", 12) == 0) {
       
    48                         json[len-2] = '\0';  // chop off ");" from end.
       
    49                         json += 12;  // skip past "loadFolders(".
       
    50                         len -= 14;
       
    51                     } else if (strncmp(json, "var profile=", 12) == 0) {
       
    52                         json[len-1] = '\0';  // chop off ";" from end.
       
    53                         json += 12;  // skip past "var profile=".
       
    54                         len -= 13;
       
    55                     }
       
    56 
       
    57                     retval = cJSON_Parse(json);
       
    58                 }
       
    59                 free(buf);
       
    60             }
       
    61         }
       
    62         fclose(io);
       
    63     }
       
    64 
       
    65     return retval;
       
    66 }
       
    67 
       
    68 static void dump_json_internal(const cJSON *json, const int indent)
       
    69 {
       
    70     const cJSON *i;
       
    71     int j;
       
    72 
       
    73     if (!json) return;
       
    74 
       
    75     for (j = 0; j < (indent*2); j++) {
       
    76         printf(" ");
       
    77     }
       
    78 
       
    79     if (json->string != NULL) {
       
    80         printf("%s : ", json->string);
       
    81     }
       
    82 
       
    83     switch (json->type) {
       
    84         default: printf("[!unknown type!]"); break;
       
    85         case cJSON_Invalid: printf("[!invalid!]"); break;
       
    86         case cJSON_False: printf("false"); break;
       
    87         case cJSON_True: printf("true"); break;
       
    88         case cJSON_NULL: printf("null"); break;
       
    89         case cJSON_Number: printf("%f", json->valuedouble); break;
       
    90         case cJSON_Raw: printf("!CDATA[\"%s\"]", json->valuestring); break;
       
    91         case cJSON_String: printf("\"%s\"", json->valuestring); break;
       
    92 
       
    93         case cJSON_Array:
       
    94             printf("[\n");
       
    95             for (i = json->child; i != NULL; i = i->next) {
       
    96                 dump_json_internal(i, indent + 1);
       
    97                 if (i->next != NULL) {
       
    98                     printf(", ");
       
    99                 }
       
   100                 printf("\n");
       
   101             }
       
   102             for (j = 0; j < (indent*2); j++) {
       
   103                 printf(" ");
       
   104             }
       
   105             printf("]");
       
   106             break;
       
   107 
       
   108         case cJSON_Object:
       
   109             printf("{\n");
       
   110             for (i = json->child; i != NULL; i = i->next) {
       
   111                 dump_json_internal(i, indent + 1);
       
   112                 if (i->next != NULL) {
       
   113                     printf(", ");
       
   114                 }
       
   115                 printf("\n");
       
   116             }
       
   117             for (j = 0; j < (indent*2); j++) {
       
   118                 printf(" ");
       
   119             }
       
   120             printf("}");
       
   121             break;
       
   122     }
       
   123 }
       
   124 
       
   125 static void dump_json(const cJSON *json)
       
   126 {
       
   127     dump_json_internal(json, 0);
       
   128     printf("\n");
       
   129 }
       
   130 
       
   131 
       
   132 static int base64_decode(const char *in, const int inlen, uint8_t **out)
       
   133 {
       
   134     const int len = (inlen == -1) ? (int) strlen(in) : inlen;
       
   135 
       
   136     *out = NULL;
       
   137 
       
   138     BIO *b64f = BIO_new(BIO_f_base64());
       
   139     BIO *buff = buff = BIO_push(b64f, BIO_new_mem_buf(in, len));
       
   140 
       
   141     uint8_t *decoded = (uint8_t *) malloc(len);
       
   142     if (!decoded) {
       
   143         fprintf(stderr, "Out of memory!\n");
       
   144         return -1;
       
   145     }
       
   146 
       
   147     BIO_set_flags(buff, BIO_FLAGS_BASE64_NO_NL);
       
   148     BIO_set_close(buff, BIO_CLOSE);
       
   149     const int retval = BIO_read(buff, decoded, len);
       
   150     if (retval < 0) {
       
   151         free(decoded);
       
   152         return -1;
       
   153     }
       
   154     decoded[retval] = '\0';
       
   155 
       
   156     BIO_free_all(buff);
       
   157 
       
   158     *out = decoded;
       
   159     return retval;
       
   160 }
       
   161 
       
   162 static int decrypt_opdata(const char *name, const uint8_t *opdata, const int opdatalen, const uint8_t *encryptionkey, const uint8_t *mackey, uint8_t **out, int *outlen)
       
   163 {
       
   164     *out = NULL;
       
   165     if (outlen) {
       
   166         *outlen = 0;
       
   167     }
       
   168 
       
   169     if ((opdatalen < 64) || (memcmp(opdata, "opdata01", 8) != 0)) {
       
   170         fprintf(stderr, "opdata(%s) isn't actually in opdata01 format.\n", name);
       
   171         return 0;
       
   172     }
       
   173 
       
   174     // !!! FIXME: byteswap
       
   175     const int plaintextlen = (int) (*((uint64_t *) (opdata + 8)));
       
   176     const int paddedplaintextlen = plaintextlen + (16 - (plaintextlen % 16));
       
   177     if (paddedplaintextlen > (opdatalen - (8 + 8 + 16 + 32))) {  // minus magic, len, iv, hmac
       
   178         fprintf(stderr, "opdata(%s) plaintext length is bogus.\n", name);
       
   179         return 0;
       
   180     }
       
   181 
       
   182     uint8_t digest[32];
       
   183     unsigned int digestlen = sizeof (digest);
       
   184     if (!HMAC(EVP_sha256(), mackey, 32, opdata, opdatalen-32, (unsigned char *) digest, &digestlen)) {
       
   185         fprintf(stderr, "opdata(%s) HMAC failed.\n", name);
       
   186         return 0;
       
   187     } else if (digestlen != sizeof (digest)) {
       
   188         fprintf(stderr, "opdata(%s) HMAC is wrong size (got=%u expected=%u).\n", name, digestlen, (unsigned int) sizeof (digest));
       
   189         return 0;
       
   190     } else if (memcmp(digest, opdata + (opdatalen-sizeof (digest)), sizeof (digest)) != 0) {
       
   191         fprintf(stderr, "opdata(%s) HMAC verification failed.\n", name);
       
   192         return 0;
       
   193     }
       
   194 
       
   195     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
       
   196     if (!ctx) {
       
   197         fprintf(stderr, "opdata(%s) EVP_CIPHER_CTX_new() failed\n", name);
       
   198         return 0;
       
   199     }
       
   200 
       
   201     const unsigned char *iv = (unsigned char *) (opdata + 16);
       
   202     if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, (const unsigned char *) encryptionkey, iv)) {
       
   203         fprintf(stderr, "opdata(%s) EVP_DecryptInit_ex() failed\n", name);
       
   204         EVP_CIPHER_CTX_cleanup(ctx);
       
   205         return 0;
       
   206     }
       
   207 
       
   208     EVP_CIPHER_CTX_set_padding(ctx, 0);
       
   209 
       
   210     uint8_t *plaintext = (uint8_t *) malloc(paddedplaintextlen);
       
   211     if (!plaintext) {
       
   212         fprintf(stderr, "opdata(%s) Out of memory.\n", name);
       
   213         EVP_CIPHER_CTX_cleanup(ctx);
       
   214         return 0;
       
   215     }
       
   216 
       
   217     // opdata+32 == first byte past magic, len, and iv.
       
   218     int decryptedlen = 0;
       
   219     if (!EVP_DecryptUpdate(ctx, plaintext, &decryptedlen, opdata + 32, paddedplaintextlen)) {
       
   220         fprintf(stderr, "opdata(%s) EVP_DecryptUpdate() failed\n", name);
       
   221         free(plaintext);
       
   222         EVP_CIPHER_CTX_cleanup(ctx);
       
   223         return 0;
       
   224     }
       
   225 
       
   226     int totaldecryptedlen = decryptedlen;
       
   227     if (!EVP_DecryptFinal_ex(ctx, plaintext + decryptedlen, &decryptedlen)) {
       
   228         fprintf(stderr, "opdata(%s) EVP_DecryptFinal_ex() failed\n", name);
       
   229         free(plaintext);
       
   230         EVP_CIPHER_CTX_cleanup(ctx);
       
   231         return 0;
       
   232     }
       
   233     totaldecryptedlen += decryptedlen;
       
   234 
       
   235     EVP_CIPHER_CTX_cleanup(ctx);
       
   236 
       
   237     if (totaldecryptedlen != paddedplaintextlen) {
       
   238         fprintf(stderr, "opdata(%s) decrypted to wrong size (got=%d expected=%u).\n", name, totaldecryptedlen, (unsigned int) paddedplaintextlen);
       
   239         free(plaintext);
       
   240         return 0;
       
   241     }
       
   242 
       
   243     // random padding bytes are prepended. Drop them.
       
   244     const int paddinglen = paddedplaintextlen - plaintextlen;
       
   245     memmove(plaintext, plaintext + paddinglen, plaintextlen);
       
   246 
       
   247     *out = plaintext;
       
   248     if (outlen) {
       
   249         *outlen = plaintextlen;
       
   250     }
       
   251 
       
   252     return 1;
       
   253 }
       
   254 
       
   255 static int decrypt_opdata_base64(const char *name, const char *base64data, const uint8_t *encryptionkey, const uint8_t *mackey, uint8_t **out, int *outlen)
       
   256 {
       
   257     uint8_t *opdata = NULL;
       
   258     const int opdatalen = base64_decode(base64data, -1, &opdata);
       
   259     if (opdatalen == -1) {
       
   260         fprintf(stderr, "opdata(%s) wasn't a valid base64 string\n", name);
       
   261         return 0;
       
   262     }
       
   263 
       
   264     const int retval = decrypt_opdata(name, opdata, opdatalen, encryptionkey, mackey, out, outlen);
       
   265     free(opdata);
       
   266     return retval;
       
   267 }
       
   268 
       
   269 static int decrypt_key(const char *name, const char *base64data, const uint8_t *encryptionkey, const uint8_t *mackey, uint8_t *finalkey, uint8_t *finalhmac)
       
   270 {
       
   271     uint8_t *decryptedkey = NULL;
       
   272     int decryptedkeylen = 0;
       
   273     if (!decrypt_opdata_base64(name, base64data, encryptionkey, mackey, &decryptedkey, &decryptedkeylen)) {
       
   274         return 0;
       
   275     }
       
   276 
       
   277     uint8_t digest[64];
       
   278     unsigned int digestlen = sizeof (digest);
       
   279     const int rc = EVP_Digest(decryptedkey, decryptedkeylen, (unsigned char *) digest, &digestlen, EVP_sha512(), NULL);
       
   280     free(decryptedkey);
       
   281     if (!rc) {
       
   282         fprintf(stderr, "Hashing %s failed.\n", name);
       
   283         return 0;
       
   284     } else if (digestlen != sizeof (digest)) {
       
   285         fprintf(stderr, "Hash for %s is wrong size (got=%u expected=%u).\n", name, digestlen, (unsigned int) sizeof (digest));
       
   286         return 0;
       
   287     }
       
   288 
       
   289     memcpy(finalkey, digest, 32);
       
   290     memcpy(finalhmac, digest + 32, 32);
       
   291     return 1;
       
   292 }
       
   293 
       
   294 static int derive_keys_from_password(const char *password, const char *base64salt, const int iterations, uint8_t *encryptionkey, uint8_t *mackey)
       
   295 {
       
   296     uint8_t salt[16];
       
   297     uint8_t *buf = NULL;
       
   298     int saltlen = base64_decode(base64salt, -1, &buf);
       
   299     if (saltlen == -1) {
       
   300         fprintf(stderr, "Salt wasn't a valid base64 string.\n");
       
   301         return 0;
       
   302     } else if (saltlen != 16) {
       
   303         fprintf(stderr, "Expected salt to base64-decode to 16 bytes (it was %lu).\n", (unsigned long) saltlen);
       
   304         free(buf);
       
   305         return 0;
       
   306     }
       
   307     memcpy(salt, buf, saltlen);
       
   308     free(buf);
       
   309 
       
   310     uint8_t derived[64];
       
   311     if (!PKCS5_PBKDF2_HMAC(password, -1,
       
   312             (const unsigned char *) salt, saltlen, iterations,
       
   313             EVP_sha512(), sizeof (derived), (unsigned char *) derived)) {
       
   314         fprintf(stderr, "Key derivation failed.\n");
       
   315         return 0;
       
   316     }
       
   317 
       
   318     // first half of the derived key is the encryption key, second half is MAC key.
       
   319     memcpy(encryptionkey, derived, 32);
       
   320     memcpy(mackey, derived + 32, 32);
       
   321     return 1;
       
   322 }
       
   323 
       
   324 static int prepare_keys(cJSON *profile, const char *password,
       
   325                         uint8_t *masterkey, uint8_t *masterhmac,
       
   326                         uint8_t *overviewkey, uint8_t *overviewhmac)
       
   327 {
       
   328     JSONVAR(const char *, base64salt, string, profile, "salt");
       
   329     JSONVAR(const char *, base64masterkey, string, profile, "masterKey");
       
   330     JSONVAR(const char *, base64overviewkey, string, profile, "overviewKey");
       
   331     JSONVAR(int, iterations, double, profile, "iterations");
       
   332 
       
   333     uint8_t encryptionkey[32];
       
   334     uint8_t mackey[32];
       
   335     if (!derive_keys_from_password(password, base64salt, iterations, encryptionkey, mackey)) {
       
   336         return 0;
       
   337     } else if (!decrypt_key("master key", base64masterkey, encryptionkey, mackey, masterkey, masterhmac)) {
       
   338         return 0;
       
   339     } else if (!decrypt_key("overview key", base64overviewkey, encryptionkey, mackey, overviewkey, overviewhmac)) {
       
   340         return 0;
       
   341     }
       
   342 
       
   343     return 1;
       
   344 }
       
   345 
       
   346 static void dump_folders(const uint8_t *overviewkey, const uint8_t *overviewhmac)
       
   347 {
       
   348     cJSON *folders = load_json("folders.js");
       
   349     cJSON *i;
       
   350 
       
   351     if (!folders || !folders->child) {
       
   352         printf("(no folders.)\n");
       
   353         return;
       
   354     }
       
   355 
       
   356     printf("\nFolders...\n");
       
   357     for (i = folders->child; i != NULL; i = i->next) {
       
   358         char *encrypted = NULL;
       
   359         uint8_t *decrypted = NULL;
       
   360         cJSON *overview = cJSON_GetObjectItem(i, "overview");
       
   361         if (overview) {
       
   362             encrypted = overview->valuestring;
       
   363             int decryptedlen = 0;
       
   364             if (decrypt_opdata_base64("overview", encrypted, overviewkey, overviewhmac, &decrypted, &decryptedlen)) {
       
   365                 decrypted[decryptedlen] = 0;
       
   366                 overview->valuestring = (char *) decrypted;
       
   367             }
       
   368         }
       
   369 
       
   370         dump_json(i);
       
   371         printf("\n");
       
   372 
       
   373         if (overview) {
       
   374             overview->valuestring = encrypted; // put this back for cleanup.
       
   375         }
       
   376         free(decrypted);
       
   377     }
       
   378 
       
   379     printf("\n");
       
   380 
       
   381     cJSON_Delete(folders);
       
   382 }
       
   383 
       
   384 typedef struct CategoryMap
       
   385 {
       
   386     const char *name;
       
   387     const char *idstr;
       
   388 } CategoryMap;
       
   389 
       
   390 static const CategoryMap category_map[] = {
       
   391     { "Login", "001" },
       
   392     { "Credit Card", "002" },
       
   393     { "Secure Note", "003" },
       
   394     { "Identity", "004" },
       
   395     { "Password", "005" },
       
   396     { "Tombstone", "099" },
       
   397     { "Software License", "100" },
       
   398     { "Bank Account", "101" },
       
   399     { "Database", "102" },
       
   400     { "Driver License", "103" },
       
   401     { "Outdoor License", "104" },
       
   402     { "Membership", "105" },
       
   403     { "Passport", "106" },
       
   404     { "Rewards", "107" },
       
   405     { "SSN", "108" },
       
   406     { "Router", "109" },
       
   407     { "Server", "110" },
       
   408     { "Email", "111" }
       
   409 };
       
   410 
       
   411 static int compare_cjson_by_fieldname(const void *a, const void *b)
       
   412 {
       
   413     return strcmp( (*(const cJSON **) a)->string, (*(const cJSON **) b)->string );
       
   414 }
       
   415 
       
   416 static int test_item_hmac(cJSON *item, const char *base64hmac, const uint8_t *overviewhmac)
       
   417 {
       
   418     // sort all the fields into alphabetic order.
       
   419     int total = 0;
       
   420     for (cJSON *i = item->child; i != NULL; i = i->next) {
       
   421         total++;
       
   422     }
       
   423 
       
   424     total--;  // don't include "hmac"
       
   425 
       
   426     cJSON **items = (cJSON **) calloc(total, sizeof (cJSON *));
       
   427     if (!items) {
       
   428         return 0;  // oh well.
       
   429     }
       
   430 
       
   431     total = 0;
       
   432     for (cJSON *i = item->child; i != NULL; i = i->next) {
       
   433         if (strcmp(i->string, "hmac") == 0) {
       
   434             continue;
       
   435         }
       
   436         items[total++] = i;
       
   437     }
       
   438 
       
   439     qsort(items, total, sizeof (cJSON *), compare_cjson_by_fieldname);
       
   440 
       
   441     int retval = 0;
       
   442     uint8_t digest[32];
       
   443     HMAC_CTX ctx;
       
   444     HMAC_CTX_init(&ctx);
       
   445     if (HMAC_Init(&ctx, overviewhmac, 32, EVP_sha256())) {
       
   446         int i;
       
   447         for (i = 0; i < total; i++) {
       
   448             cJSON *it = items[i];
       
   449             if (!HMAC_Update(&ctx, (const unsigned char *) it->string, strlen(it->string))) {
       
   450                 break;
       
   451             }
       
   452 
       
   453             char strbuf[64];
       
   454             const char *str = NULL;
       
   455             switch (it->type) {
       
   456                 case cJSON_False: str = "0"; break;
       
   457                 case cJSON_True: str = "1"; break;
       
   458                 case cJSON_Number: str = strbuf; snprintf(strbuf, sizeof (strbuf), "%d", (int) it->valuedouble); break;  // !!! FIXME: might be wrong.
       
   459                 case cJSON_String: str = it->valuestring; break;
       
   460                 default: fprintf(stderr, "uhoh, can't HMAC this field ('%s')!\n", it->string); break;
       
   461             }
       
   462 
       
   463             if (!HMAC_Update(&ctx, (const unsigned char *) str, strlen(str))) {
       
   464                 break;
       
   465             }
       
   466         }
       
   467 
       
   468         unsigned int digestlen = sizeof (digest);
       
   469         if ((i == total) && (HMAC_Final(&ctx, digest, &digestlen)) && (digestlen == sizeof (digest))) {
       
   470             uint8_t *expected = NULL;
       
   471             if (base64_decode(base64hmac, -1, &expected) == sizeof (digest)) {
       
   472                 retval = (memcmp(digest, expected, sizeof (digest)) == 0) ? 1 : 0;
       
   473             }
       
   474             free(expected);
       
   475         }
       
   476     }
       
   477 
       
   478     HMAC_CTX_cleanup(&ctx);
       
   479     free(items);
       
   480 
       
   481     return retval;
       
   482 }
       
   483 
       
   484 static void dump_band(cJSON *band, const uint8_t *masterkey, const uint8_t *masterhmac, const uint8_t *overviewkey, const uint8_t *overviewhmac)
       
   485 {
       
   486     for (cJSON *i = band->child; i != NULL; i = i->next) {
       
   487         //dump_json(i);
       
   488         cJSON *json;
       
   489         uint8_t itemkey[32];
       
   490         uint8_t itemhmac[32];
       
   491         int itemkeysokay = 0;
       
   492 
       
   493         printf("uuid %s:\n", i->string);
       
   494 
       
   495         if ((json = cJSON_GetObjectItem(i, "category")) != NULL) {
       
   496             const char *category = json->valuestring;
       
   497             for (int i = 0; i < sizeof (category_map) / sizeof (category_map[0]); i++) {
       
   498                 if (strcmp(category_map[i].idstr, category) == 0) {
       
   499                     category = category_map[i].name;
       
   500                     break;
       
   501                 }
       
   502             }
       
   503             printf(" category: %s\n", category);
       
   504         }
       
   505 
       
   506         if ((json = cJSON_GetObjectItem(i, "created")) != NULL) {
       
   507             time_t t = (time_t) json->valuedouble;
       
   508             printf(" created: %s", ctime(&t));
       
   509         }
       
   510 
       
   511         if ((json = cJSON_GetObjectItem(i, "updated")) != NULL) {
       
   512             time_t t = (time_t) json->valuedouble;
       
   513             printf(" updated: %s", ctime(&t));
       
   514         }
       
   515 
       
   516         if ((json = cJSON_GetObjectItem(i, "tx")) != NULL) {
       
   517             time_t t = (time_t) json->valuedouble;
       
   518             printf(" last tx: %s", ctime(&t));
       
   519         }
       
   520 
       
   521         printf(" trashed: %s\n", cJSON_IsTrue(cJSON_GetObjectItem(i, "trashed")) ? "true" : "false");
       
   522 
       
   523         json = cJSON_GetObjectItem(i, "folder");
       
   524         printf(" folder uuid: %s\n", json ? json->valuestring : "[none]");
       
   525 
       
   526         if ((json = cJSON_GetObjectItem(i, "fave")) != NULL) {
       
   527             printf(" fave: %lu\n", (unsigned long) json->valuedouble);
       
   528         } else {
       
   529             printf(" fave: [no]\n");
       
   530         }
       
   531 
       
   532         if ((json = cJSON_GetObjectItem(i, "hmac")) != NULL) {
       
   533             const char *base64hmac = json->valuestring;
       
   534             const int valid = test_item_hmac(i, base64hmac, overviewhmac);
       
   535             printf(" hmac: %s [%svalid]\n", base64hmac, valid ? "" : "in");
       
   536         } else {
       
   537             printf(" hmac: [none]\n");
       
   538         }
       
   539 
       
   540         // !!! FIXME: lots of code dupe with master key decrypt.
       
   541         if ((json = cJSON_GetObjectItem(i, "k")) != NULL) {
       
   542             const char *base64key = json->valuestring;
       
   543             uint8_t *decoded = NULL;
       
   544             const int decodedlen = base64_decode(base64key, -1, &decoded);
       
   545             if ((decodedlen != -1) && (decodedlen > 32)) {
       
   546                 uint8_t digest[32];
       
   547                 unsigned int digestlen = sizeof (digest);
       
   548                 if (!HMAC(EVP_sha256(), masterhmac, 32, (const unsigned char *) decoded, decodedlen-digestlen, (unsigned char *) digest, &digestlen)) {
       
   549                     fprintf(stderr, " [item key HMAC failed.]\n");
       
   550                 } else if (digestlen != sizeof (digest)) {
       
   551                     fprintf(stderr, "[item key HMAC is wrong size (got=%u expected=%u)].\n", digestlen, (unsigned int) sizeof (digest));
       
   552                 } else if (memcmp(digest, decoded + (decodedlen-sizeof (digest)), sizeof (digest)) != 0) {
       
   553                     fprintf(stderr, "[item key HMAC verification failed.]\n");
       
   554                 } else {  // HMAC cleared.
       
   555                     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
       
   556                     const unsigned char *iv = (unsigned char *) (decoded);
       
   557                     uint8_t *plaintext = NULL;
       
   558                     int decryptedlen = 0;
       
   559                     int decryptedlen2 = 0;
       
   560 
       
   561                     if (!ctx) {
       
   562                         fprintf(stderr, "[item key EVP_CIPHER_CTX_new() failed.]\n");
       
   563                     } else if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, (const unsigned char *) masterkey, iv)) {
       
   564                         fprintf(stderr, "[item key EVP_DecryptInit_ex() failed.]\n");
       
   565                     } else if (!EVP_CIPHER_CTX_set_padding(ctx, 0)) {
       
   566                         fprintf(stderr, "[item key EVP_CIPHER_CTX_set_padding() failed.]\n");
       
   567                     } else if ((plaintext = (uint8_t *) malloc(decodedlen)) == NULL) {
       
   568                         fprintf(stderr, "[item key Out of memory.]\n");
       
   569                     } else if (!EVP_DecryptUpdate(ctx, plaintext, &decryptedlen, decoded + 16, decodedlen - 48)) {
       
   570                         fprintf(stderr, "[item key EVP_DecryptUpdate() failed.]\n");
       
   571                     } else if (!EVP_DecryptFinal_ex(ctx, plaintext + decryptedlen, &decryptedlen2)) {
       
   572                         fprintf(stderr, "[item key EVP_DecryptFinal_ex() failed.]\n");
       
   573                     } else if ((decryptedlen + decryptedlen2) != 64) {
       
   574                         fprintf(stderr, "[item key is wrong size.]\n");
       
   575                     } else {
       
   576                         memcpy(itemkey, plaintext, 32);
       
   577                         memcpy(itemhmac, plaintext + 32, 32);
       
   578                         itemkeysokay = 1;
       
   579                     }
       
   580 
       
   581                     free(plaintext);
       
   582 
       
   583                     if (ctx) {
       
   584                         EVP_CIPHER_CTX_cleanup(ctx);
       
   585                     }
       
   586                 }
       
   587             }
       
   588             free(decoded);
       
   589         }
       
   590 
       
   591         if ((json = cJSON_GetObjectItem(i, "o")) != NULL) {
       
   592             uint8_t *decrypted = NULL;
       
   593             int decryptedlen = 0;
       
   594             if (decrypt_opdata_base64("o", json->valuestring, overviewkey, overviewhmac, &decrypted, &decryptedlen)) {
       
   595                 decrypted[decryptedlen] = 0;
       
   596                 printf(" o: %s\n", decrypted);
       
   597                 free(decrypted);
       
   598             } else {
       
   599                 printf(" o: [failed to decrypt]\n");
       
   600             }
       
   601         }
       
   602 
       
   603         if ((json = cJSON_GetObjectItem(i, "d")) != NULL) {
       
   604             uint8_t *decrypted = NULL;
       
   605             int decryptedlen = 0;
       
   606             if (itemkeysokay && decrypt_opdata_base64("d", json->valuestring, itemkey, itemhmac, &decrypted, &decryptedlen)) {
       
   607                 decrypted[decryptedlen] = 0;
       
   608                 printf(" d: %s\n", decrypted);
       
   609                 free(decrypted);
       
   610             } else {
       
   611                 printf(" d: [failed to decrypt]\n");
       
   612             }
       
   613         }
       
   614 
       
   615         printf("\n");
       
   616     }
       
   617 
       
   618     printf("\n");
       
   619 }
       
   620 
       
   621 static void dump_bands(const uint8_t *masterkey, const uint8_t *masterhmac, const uint8_t *overviewkey, const uint8_t *overviewhmac)
       
   622 {
       
   623     for (unsigned int i = 0; i < 16; i++) {
       
   624         char fname[16];
       
   625         snprintf(fname, sizeof (fname), "band_%X.js", i);
       
   626         cJSON *band = load_json(fname);
       
   627         if (band) {
       
   628             printf("\nBand %s...\n\n", fname);
       
   629             dump_band(band, masterkey, masterhmac, overviewkey, overviewhmac);
       
   630             cJSON_Delete(band);
       
   631         }
       
   632     }
       
   633 }
       
   634 
       
   635 int main(int argc, char **argv)
       
   636 {
       
   637     OpenSSL_add_all_algorithms();
       
   638     ERR_load_crypto_strings();
       
   639     OPENSSL_config(NULL);
       
   640 
       
   641     if (argc != 3) {
       
   642         fprintf(stderr, "\n\nUSAGE: %s </path/to/1Password.opvault> <keychain password>\n\n", argv[0]);
       
   643         return 2;
       
   644     }
       
   645 
       
   646     const char *opvaultpath = argv[1];
       
   647     const char *password = argv[2];
       
   648 
       
   649     if (chdir(opvaultpath) == -1) {
       
   650         fprintf(stderr, "chdir(\"%s\") failed: %s\n", opvaultpath, strerror(errno));
       
   651         return 1;
       
   652     } else if (chdir("default") == -1) {
       
   653         fprintf(stderr, "chdir(\"%s/default\") failed: %s\n", opvaultpath, strerror(errno));
       
   654         return 1;
       
   655     }
       
   656 
       
   657     cJSON *profile = load_json("profile.js");
       
   658     if (!profile) {
       
   659         fprintf(stderr, "load_json(\"profile.js\") failed.\n");
       
   660         return 1;
       
   661     }
       
   662 
       
   663     printf("profile : "); dump_json(profile); printf("\n");
       
   664 
       
   665     uint8_t masterkey[32];
       
   666     uint8_t masterhmac[32];
       
   667     uint8_t overviewkey[32];
       
   668     uint8_t overviewhmac[32];
       
   669     if (!prepare_keys(profile, password, masterkey, masterhmac, overviewkey, overviewhmac)) {
       
   670         return 1;
       
   671     }
       
   672 
       
   673     cJSON_Delete(profile);
       
   674 
       
   675     dump_folders(overviewkey, overviewhmac);
       
   676     dump_bands(masterkey, masterhmac, overviewkey, overviewhmac);
       
   677 
       
   678     return 0;
       
   679 }
       
   680