Public feed #603057

Stuff

public c v1 · immutable
Raw Edit
·published 2007-07-04 14:15 UTC
rendered paste body
/* * Copyright (c) 2006  * Ralf-Philipp Weinmann <ralf@coderpunks.org> * Jacob Appelbaum <jacob@appelbaum.net> * Christian Fromme <kaner@strace.org> * * Decrypt a AES-128 encrypted disk image given the encryption key * and the hmacsha1key of the image. These two keys can be found * out by running hdiutil attach with -debug on the disk image. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. *  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <stdint.h>#include <arpa/inet.h>#include <openssl/sha.h>#include <openssl/aes.h>#include <openssl/hmac.h>#include <openssl/evp.h>#include <libkern/OSByteOrder.h>/* length of message digest output in bytes (160 bits) */#define MD_LENGTH		20/* length of cipher key in bytes (128 bits) */#define CIPHER_KEY_LENGTH	16/* block size of cipher in bytes (128 bits) */#define CIPHER_BLOCKSIZE	16/* number of iterations for PBKDF2 key derivation */#define PBKDF2_ITERATION_COUNT	1000typedef struct {  /* 0x000: */ uint8_t  filler1[48];  /* 0x034: */ uint32_t kdf_iteration_count;  /* 0x034: */ uint32_t kdf_salt_len;  /* 0x038: */ uint8_t  kdf_salt[48]; /* salt value for key derivation */  /* 0x068: */ uint8_t  unwrap_iv[32]; /* IV for encryption-key unwrapping */  /* 0x088: */ uint32_t len_wrapped_aes_key;  /* 0x08c: */ uint8_t  wrapped_aes_key[296];  /* 0x1b4: */ uint32_t len_hmac_sha1_key;  /* 0x1b8: */ uint8_t  wrapped_hmac_sha1_key[300];  /* 0x1b4: */ uint32_t len_integrity_key;  /* 0x2e8: */ uint8_t  wrapped_integrity_key[48];  /* 0x318: */ uint8_t  filler6[484];} cencrypted_v1_header;typedef struct {  unsigned char sig[8];  uint32_t version;  uint32_t enc_iv_size;  uint32_t unk1;  uint32_t unk2;  uint32_t unk3;  uint32_t unk4;  uint32_t unk5;  unsigned char uuid[16];  uint32_t blocksize;  uint64_t datasize;  uint64_t dataoffset;  uint8_t filler1[0x260];  uint32_t kdf_algorithm;  uint32_t kdf_prng_algorithm;  uint32_t kdf_iteration_count;  uint32_t kdf_salt_len; /* in bytes */  uint8_t  kdf_salt[32];  uint32_t blob_enc_iv_size;  uint8_t  blob_enc_iv[32];  uint32_t blob_enc_key_bits;  uint32_t blob_enc_algorithm;  uint32_t blob_enc_padding;  uint32_t blob_enc_mode;  uint32_t encrypted_keyblob_size;  uint8_t  encrypted_keyblob[0x30];} cencrypted_v2_pwheader;void print_hex(uint8_t * /* data */, uint32_t /* len */);void convert_hex(char * /* str */, uint8_t * /* bytes */,		 int /* maxlen */);void dump_v2_header(void * /* hdr */);void adjust_v1_header_byteorder(cencrypted_v1_header * /* hdr */);void adjust_v2_header_byteorder(cencrypted_v2_pwheader * /* pwhdr */);void print_hex(uint8_t *data, uint32_t len) {  uint32_t ctr;  char *sep;  if (len > 64) len = 64;  for(ctr = 0; ctr < len; ctr++) {    sep = (((ctr&7)==0)&&ctr) ? "\n" : "";    fprintf(stderr, "%s%02x ", sep, data[ctr]);  }  fprintf(stderr, "\n\n");}void convert_hex(char *str, uint8_t *bytes, int maxlen) {  int slen = strlen(str);  int bytelen = (slen+1)/2;  int rpos, wpos = 0;  while(wpos < maxlen) bytes[wpos++] = 0;  wpos = 0;  for(rpos = 0; rpos < maxlen; rpos++) {    sscanf(&str[rpos*2], "%02hhx", &bytes[wpos++]);  }}void dump_v2_header(void *hdr) {  cencrypted_v2_pwheader *pwhdr = (cencrypted_v2_pwheader *) hdr;  fprintf(stderr, "sig\t%8s\n", pwhdr->sig);  fprintf(stderr, "blocksize\t%lu\n", pwhdr->blocksize);  fprintf(stderr, "datasize\t%llu\n", pwhdr->datasize);  fprintf(stderr, "dataoffset\t%llu\n", pwhdr->dataoffset);  /* 103: CSSM_ALGID_PKCS5_PBKDF2 */  fprintf(stderr, "keyDerivationAlgorithm      %lu\n", (unsigned long) pwhdr->kdf_algorithm);  fprintf(stderr, "keyDerivationPRNGAlgorithm  %lu\n", (unsigned long) pwhdr->kdf_prng_algorithm);  /* by default the iteration count should be 1000 iterations */  fprintf(stderr, "keyDerivationIterationCount %lu\n", (unsigned long) pwhdr->kdf_iteration_count);  fprintf(stderr, "keyDerivationSaltSize       %lu\n", (unsigned long) pwhdr->kdf_salt_len);  fprintf(stderr, "keyDerivationSalt           \n");  print_hex(pwhdr->kdf_salt, pwhdr->kdf_salt_len);  fprintf(stderr, "blobEncryptionIVSize        %lu\n", (unsigned long) pwhdr->blob_enc_iv_size);  fprintf(stderr, "blobEncryptionIV            \n");  print_hex(pwhdr->blob_enc_iv, pwhdr->blob_enc_iv_size);  fprintf(stderr, "blobEncryptionKeySizeInBits %lu\n",  (unsigned long) pwhdr->blob_enc_key_bits);  /*  17: CSSM_ALGID_3DES_3KEY_EDE */  fprintf(stderr, "blobEncryptionAlgorithm     %lu\n",  (unsigned long) pwhdr->blob_enc_algorithm);  /*   7: CSSM_PADDING_PKCS7 */  fprintf(stderr, "blobEncryptionPadding       %lu\n",  (unsigned long) pwhdr->blob_enc_padding);  /*   6: CSSM_ALGMODE_CBCPadIV8 */  fprintf(stderr, "blobEncryptionMode          %lu\n",  (unsigned long)  pwhdr->blob_enc_mode);  fprintf(stderr, "encryptedBlobSize           %lu\n",  (unsigned long)  pwhdr->encrypted_keyblob_size);  fprintf(stderr, "encryptedBlob               \n");  print_hex(pwhdr->encrypted_keyblob, pwhdr->encrypted_keyblob_size);}void adjust_v1_header_byteorder(cencrypted_v1_header *hdr) {  hdr->kdf_iteration_count = htonl(hdr->kdf_iteration_count);  hdr->kdf_salt_len = htonl(hdr->kdf_salt_len);  hdr->len_wrapped_aes_key = htonl(hdr->len_wrapped_aes_key);  hdr->len_hmac_sha1_key = htonl(hdr->len_hmac_sha1_key);  hdr->len_integrity_key = htonl(hdr->len_integrity_key);}#define swap32(x) x = OSSwapHostToBigInt32(x)#define swap64(x) x = OSSwapHostToBigInt64(x)void adjust_v2_header_byteorder(cencrypted_v2_pwheader *pwhdr) {  swap32(pwhdr->blocksize);  swap64(pwhdr->datasize);  swap64(pwhdr->dataoffset);  pwhdr->kdf_algorithm = htonl(pwhdr->kdf_algorithm);  pwhdr->kdf_prng_algorithm = htonl(pwhdr->kdf_prng_algorithm);  pwhdr->kdf_iteration_count = htonl(pwhdr->kdf_iteration_count);  pwhdr->kdf_salt_len = htonl(pwhdr->kdf_salt_len);  pwhdr->blob_enc_iv_size = htonl(pwhdr->blob_enc_iv_size);  pwhdr->blob_enc_key_bits = htonl(pwhdr->blob_enc_key_bits);  pwhdr->blob_enc_algorithm = htonl(pwhdr->blob_enc_algorithm);  pwhdr->blob_enc_padding = htonl(pwhdr->blob_enc_padding);  pwhdr->blob_enc_mode = htonl(pwhdr->blob_enc_mode);  pwhdr->encrypted_keyblob_size = htonl(pwhdr->encrypted_keyblob_size);}HMAC_CTX hmacsha1_ctx;AES_KEY aes_decrypt_key;int CHUNK_SIZE=4096;  // default/** * Compute IV of current block as * truncate128(HMAC-SHA1(hmacsha1key||blockno)) */void compute_iv(uint32_t chunk_no, uint8_t *iv) {  unsigned char mdResult[MD_LENGTH];  unsigned int mdLen,i;    chunk_no = OSSwapHostToBigInt32(chunk_no);  HMAC_Init_ex(&hmacsha1_ctx, NULL, 0, NULL, NULL);  HMAC_Update(&hmacsha1_ctx, (void *) &chunk_no, sizeof(uint32_t));  HMAC_Final(&hmacsha1_ctx, mdResult, &mdLen);  memcpy(iv, mdResult, CIPHER_BLOCKSIZE);}void decrypt_chunk(uint8_t *ctext, uint8_t *ptext, uint32_t chunk_no) {  uint8_t iv[CIPHER_BLOCKSIZE];  compute_iv(chunk_no, iv);  AES_cbc_encrypt(ctext, ptext, CHUNK_SIZE, &aes_decrypt_key, iv, AES_DECRYPT);}/* DES3-EDE unwrap operation loosely based on to RFC 2630, section 12.6    wrapped_key has to be 40 bytes in length.  */int apple_des3_ede_unwrap_key(uint8_t *wrapped_key, int wrapped_key_len, uint8_t *decryptKey, uint8_t *unwrapped_key) {  EVP_CIPHER_CTX ctx;  uint8_t *TEMP1, *TEMP2, *CEKICV;  uint8_t IV[8] = { 0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05 };  int outlen, tmplen, i;  EVP_CIPHER_CTX_init(&ctx);  /* result of the decryption operation shouldn't be bigger than ciphertext */  TEMP1 = malloc(wrapped_key_len);  TEMP2 = malloc(wrapped_key_len);  CEKICV = malloc(wrapped_key_len);  /* uses PKCS#7 padding for symmetric key operations by default */  EVP_DecryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, decryptKey, IV);  if(!EVP_DecryptUpdate(&ctx, TEMP1, &outlen, wrapped_key, wrapped_key_len)) {    fprintf(stderr, "internal error (1) during key unwrap operation!\n");    return(-1);  }  if(!EVP_DecryptFinal_ex(&ctx, TEMP1 + outlen, &tmplen)) {    fprintf(stderr, "internal error (2) during key unwrap operation!\n");    return(-1);  }  outlen += tmplen;  EVP_CIPHER_CTX_cleanup(&ctx);  /* reverse order of TEMP3 */  for(i = 0; i < outlen; i++) TEMP2[i] = TEMP1[outlen - i - 1];  EVP_CIPHER_CTX_init(&ctx);  /* uses PKCS#7 padding for symmetric key operations by default */  EVP_DecryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, decryptKey, TEMP2);  if(!EVP_DecryptUpdate(&ctx, CEKICV, &outlen, TEMP2+8, outlen-8)) {    fprintf(stderr, "internal error (3) during key unwrap operation!\n");    return(-1);  }  if(!EVP_DecryptFinal_ex(&ctx, CEKICV + outlen, &tmplen)) {    fprintf(stderr, "internal error (4) during key unwrap operation!\n");    return(-1);  }  outlen += tmplen;  EVP_CIPHER_CTX_cleanup(&ctx);  memcpy(unwrapped_key, CEKICV+4, outlen-4);  free(TEMP1);  free(TEMP2);  free(CEKICV);  return(0);}int unwrap_v1_header(char *passphrase, cencrypted_v1_header *header, uint8_t *aes_key, uint8_t *hmacsha1_key) {  /* derived key is a 3DES-EDE key */  uint8_t derived_key[192/8];  PKCS5_PBKDF2_HMAC_SHA1(passphrase, strlen(passphrase), (unsigned char*)header->kdf_salt, 20, 			 PBKDF2_ITERATION_COUNT, sizeof(derived_key), derived_key);  if (apple_des3_ede_unwrap_key(header->wrapped_aes_key, 40, derived_key, aes_key) != 0)    return(-1);  if (apple_des3_ede_unwrap_key(header->wrapped_hmac_sha1_key, 48, derived_key, hmacsha1_key) != 0)    return(-1);  return(0);}int unwrap_v2_header(char *passphrase, cencrypted_v2_pwheader *header, uint8_t *aes_key, uint8_t *hmacsha1_key) {  /* derived key is a 3DES-EDE key */  uint8_t derived_key[192/8];  EVP_CIPHER_CTX ctx;  uint8_t *TEMP1;  int outlen, tmplen;  PKCS5_PBKDF2_HMAC_SHA1(passphrase, strlen(passphrase), (unsigned char*)header->kdf_salt, 20,			 PBKDF2_ITERATION_COUNT, sizeof(derived_key), derived_key);  print_hex(derived_key, 192/8);  EVP_CIPHER_CTX_init(&ctx);  /* result of the decryption operation shouldn't be bigger than ciphertext */  TEMP1 = malloc(header->encrypted_keyblob_size);  /* uses PKCS#7 padding for symmetric key operations by default */  EVP_DecryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, derived_key, header->blob_enc_iv);  if(!EVP_DecryptUpdate(&ctx, TEMP1, &outlen, header->encrypted_keyblob, header->encrypted_keyblob_size)) {    fprintf(stderr, "internal error (1) during key unwrap operation!\n");    return(-1);  }  if(!EVP_DecryptFinal_ex(&ctx, TEMP1 + outlen, &tmplen)) {    fprintf(stderr, "internal error (2) during key unwrap operation!\n");    return(-1);  }  outlen += tmplen;  EVP_CIPHER_CTX_cleanup(&ctx);  memcpy(aes_key, TEMP1, 16);  memcpy(hmacsha1_key, TEMP1, 20);  return(0);}int determine_header_version(FILE *dmg) {  char buf[8];  fseek(dmg, 0L, SEEK_SET);  fread(buf, 8, 1, dmg);  if (!strncmp(buf, "encrcdsa", 8)) return(2);  fseek(dmg, -8L, SEEK_END);  fread(buf, 8, 1, dmg);  if (!strncmp(buf, "cdsaencr", 8)) return(1);  return(-1);}int usage(char *message) {  fprintf(stderr, "%s\n", message);  fprintf(stderr, "Usage: vfdecrypt -i in-file [-p password] [-k key] -o out-file\n");  exit(1);}int main(int argc, char *argv[]) {  FILE *in, *out;  cencrypted_v1_header v1header;  cencrypted_v2_pwheader v2header;    uint8_t hmacsha1_key[20], aes_key[16], inbuf[CHUNK_SIZE], outbuf[CHUNK_SIZE];  uint32_t chunk_no;  int hdr_version, c,i, optError = 0;  char inFile[512], outFile[512], passphrase[512];  int iflag = 0, oflag = 0, pflag = 0, kflag = 0, verbose = 0;  extern char *optarg;  extern int optind, optopt;  memset(inFile, 0, 512);  memset(outFile, 0, 512);  memset(passphrase, 0, 512);    while((c = getopt(argc, argv, "hvi::o::p:k:")) != -1) {    switch(c) {    case 'h':            usage("Help is on the way. Stay calm.");      break;    case 'v':            verbose++;      break;    case 'i':      if(optarg) strncpy(inFile, optarg, sizeof(inFile)-1);      iflag = 1;      break;    case 'o':      if (optarg) strncpy(outFile, optarg, sizeof(outFile)-1);      oflag = 1;      break;    case 'p':      if (optarg) strncpy(passphrase, optarg, sizeof(passphrase)-1);      pflag = 1;      break;    case 'k':      convert_hex(optarg, aes_key, 16);      convert_hex(optarg+32, hmacsha1_key, 20);      kflag=1;      break;    case '?':      fprintf(stderr, "Unknown option: -%c\n", optopt);      optError++;      break;    }  }    /* check to see if our user gave incorrect options */  if (optError) usage("Incorrect arguments.");    if (strlen(inFile) == 0) {    in = stdin;   } else {    if ((in = fopen(inFile, "rb")) == NULL) {      fprintf(stderr, "Error: unable to open %s\n", inFile);      exit(1);    }  }    if (strlen(outFile) == 0) {    out = stdout;  } else {    if ((out = fopen(outFile, "wb")) == NULL) {      fprintf(stderr, "Error: unable to open %s\n", outFile);      exit(1);    }  }    if (!pflag && !kflag) {    fprintf(stderr, "No Passphrase given.\n");    exit(1);  }    hdr_version = determine_header_version(in);    if (verbose >= 1) {    if (hdr_version > 0) {      fprintf(stderr, "v%d header detected.\n", hdr_version);    } else {      fprintf(stderr, "unknown format.\n");      exit(1);    }  }    if (hdr_version == 1) {    fseek(in, (long) -sizeof(cencrypted_v1_header), SEEK_END);    if (fread(&v1header, sizeof(cencrypted_v1_header), 1, in) < 1) {      fprintf(stderr, "header corrupted?\n"), exit(1);    }    adjust_v1_header_byteorder(&v1header);    if(!kflag) unwrap_v1_header(passphrase, &v1header, aes_key, hmacsha1_key);  }    if (hdr_version == 2) {    fseek(in, 0L, SEEK_SET);    if (fread(&v2header, sizeof(cencrypted_v2_pwheader), 1, in) < 1) {      fprintf(stderr, "header corrupted?\n"), exit(1);    }    adjust_v2_header_byteorder(&v2header);    dump_v2_header(&v2header);    if(!kflag) unwrap_v2_header(passphrase, &v2header, aes_key, hmacsha1_key);    CHUNK_SIZE = v2header.blocksize;  }    HMAC_CTX_init(&hmacsha1_ctx);  HMAC_Init_ex(&hmacsha1_ctx, hmacsha1_key, sizeof(hmacsha1_key), EVP_sha1(), NULL);  AES_set_decrypt_key(aes_key, CIPHER_KEY_LENGTH * 8, &aes_decrypt_key);    if (verbose >= 1) {    printf("AES Key: \n");    print_hex(aes_key, 16);    printf("SHA1 seed: \n");    print_hex(hmacsha1_key, 20);  }    if (hdr_version == 2) fseek(in, v2header.dataoffset, SEEK_SET);  else fseek(in, 0L, SEEK_SET);    chunk_no = 0;  while(fread(inbuf, CHUNK_SIZE, 1, in) > 0) {    decrypt_chunk(inbuf, outbuf, chunk_no);    chunk_no++;    if(hdr_version == 2 && (v2header.datasize-ftell(out)) < CHUNK_SIZE) {      fwrite(outbuf, v2header.datasize - ftell(out), 1, out);      break;    }    fwrite(outbuf, CHUNK_SIZE, 1, out);  }    if (verbose)  fprintf(stderr, "%d chunks written\n", chunk_no);  return(0);}