/* ========================================================================== ** * oSSL_key_dx.c * * Copyright: * Copyright (C) 2011 by Christopher R. Hertel * * Email: crh@ubiqx.mn.org * * $Id: oSSL_key_dx.c 29 2011-07-08 18:49:31Z crh $ * * -------------------------------------------------------------------------- ** * * Description: * Decrypt an exported BranchCache Server Secret and Passphrase pair. * * -------------------------------------------------------------------------- ** * * License: * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful. * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * -------------------------------------------------------------------------- ** * * Notes: * * This code was developed in participation * with the Protocol Freedom Information Foundation. * * This is example code, using OpenSSL, that will decrypt the Server * Secret and Server Passphrase exported from a Windows system running * BranchCache[TM]. * * This code was developed using information from Microsoft's published * protocol documentation, particularly [MS-PCCRC], and help from * Microsoft's Documentation Support team (dochelp@microsoft.com). * * Terminology: * Export Passphrase: An arbitrary length binary string (typically * a text string in Unicode UTF-16-LE encoding) * used to encode and decode the extracted Server * Secret and Passphrase. * Server Passphrase: An arbitrary length binary string stored on the * server. The Server Passphrase is not used * directly. The SHA256 hash of the Passphrase is * used as a signing key to sign PeerDist segment * hashes. * Server Secret: As defined in section 1.1 of [MS-PCCRC], the * Server Secret is the SHA256 hash of what is * defined here as the Server Passphrase. * * To export the Server Secret & Passphrase, use the following command in * Microsoft Powershell: * * > netsh branchcache exportkey * * That will generate a file containing the both the Server Secret and the * Server Passphrase. The exported file will be AES encrypted, and will * need to be decrypted in order to be used by third-party software. * * This program performs the required decryption, and also allows the user * to validate the result by generating its own SHA256 of the extracted * Server Passphrase. The generated hash should be identical to the * extracted Server Secret. * * This program is intended as example code--not for production use. * * + To compile: * cc -o oSSL_key_dx oSSL_key_dx.c -lssl * * + These are the steps used to extract an exported PeerDist Server * Passphrase and Server Secret pair: * 1) Generate SHA256( export_passphrase ). * Note that the terminating NUL of the passphrase is NOT included * when calculating the hash. Also note that Windows systems use * UTF-16-LE encoding, not ASCII or UTF-8. * * 2) AES Decrypt the contents of the keyfile, passing the hashed * export_passphrase as the decryption key. * Use AES256. That is, AES with a keysize of 256 bits (32 bytes) * and a blocksize of 128 bits (16 bytes--standard for AES). The * Initialization Vector (IV) is 16 bytes of zeros (all NUL bytes). * * 3) Separate the first 32 bytes of the result of step 2 from the * remainder. * + The first 32 bytes are the Server Secret. * + The remainder is the Server Passphrase, padded per PKCS#7. See: * http://en.wikipedia.org/wiki/Padding_(cryptography)#Byte_padding * * 4) Remove the padding, and validate the extracted Server Passphrase by * calculating the SHA256 of the extracted Server Passhrase and * comparing that against the first 32 bytes of the decrypted value. * The two should match. * * FIX: * * + We currently make the assumption that the entire key file will fit * into a single input buffer. Not an unreasonable assumption, but * not good coding practice. * * + We take the user-supplied export_passphrase from the command line, * assume that it is in ASCII, and add NUL bytes to approximate UTF-16-LE * Unicode encoding. That's not the right way to do it. * * ========================================================================== ** */ #include /* Standard library. */ #include /* I/O stuff. */ #include /* For . */ #include /* For strerror(3). */ #include /* Variable arg lists.*/ #include /* OpenSSL AES tools. */ #include /* OpenSSL SHA tools. */ /* -------------------------------------------------------------------------- ** * Macros: * * Err() - This is a conceited little macro that simply replaces * fprintf( stderr, ... ) with something shorter and easier * to type. * Say() - This is a conceited little macro that simply replaces * printf(3) with something shorter and easier to type. * ErrStr - Shorthand for the error message associated with . */ #define Err( ... ) (void)fprintf( stderr, __VA_ARGS__ ) #define Say( ... ) (void)printf( __VA_ARGS__ ) #define ErrStr (strerror( errno )) /* -------------------------------------------------------------------------- ** * Defined constants: * * bSIZE - The size of the input and output buffers we will create. * Note that we assume that the key file will fit in a single * buffer. * BLOCK_SIZE - The standard size of a CBC block in AES. This is the * size of the required initialization vector. */ #define bSIZE 2048 #define BLOCK_SIZE 16 /* -------------------------------------------------------------------------- ** * Static Functions: */ void Fail( char *fmt, ... ) /* ------------------------------------------------------------------------ ** * Format and print a failure message on , then exit the process. * * Input: fmt - Format string, as used in printf(), etc. * ... - Variable parameter list. * * Output: none * * Notes: Exits the process returning EXIT_FAILURE. * * ------------------------------------------------------------------------ ** */ { va_list ap; va_start( ap, fmt ); (void)fprintf( stderr, "Failure: " ); (void)vfprintf( stderr, fmt, ap ); va_end( ap ); exit( EXIT_FAILURE ); } /* Fail */ static void hexlist( const unsigned char *bufr, int len ) /* ------------------------------------------------------------------------ ** * Output a hex string representing a block of bytes. * * Input: bufr - A pointer to a bunch of bytes. * len - Number of bytes of to output. * * Output: * * ------------------------------------------------------------------------ ** */ { int i; Err( "[" ); for( i = 0; i < len; i++ ) Err( "%.2x", bufr[i] ); Err( "]\n" ); } /* hexlist */ /* -------------------------------------------------------------------------- ** * Program Mainline. */ int main( int argc, char *argv[] ) /* ------------------------------------------------------------------------ ** * Program Mainline. * * Input: argc - You know what this is. * argv - You know what to do. * * Output: EXIT_SUCCESS * or, if you are having a really bad day, EXIT_FAILURE. * * ------------------------------------------------------------------------ ** */ { int i; FILE *keyf; unsigned char *doublewide; AES_KEY aes_ppHash[1]; size_t readlen; size_t phraselen; unsigned char inBufr[bSIZE]; unsigned char outBufr[bSIZE]; unsigned char ppHash[SHA256_DIGEST_LENGTH]; unsigned char iv[BLOCK_SIZE]; /* Validate the command line. Somewhat. */ if( argc != 3 ) { Err( "Usage:\t%s \n", argv[0] ); Err( "\tWhere is the name of the file that contains the\n" ); Err( "\texported key, and is the phrase used as the\n" ); Err( "\tencryption key when the server secret was exported.\n\tIt " ); Err( "may be necessary to place the passphrase in quotation marks.\n" ); exit( EXIT_FAILURE ); } /* Open the keyfile. */ keyf = fopen( argv[1], "r" ); if( NULL == keyf ) { Err( "Failure opening file \"%s\"; %s.\n", argv[1], ErrStr ); return( EXIT_FAILURE ); } /* Read the keyfile. Assume that the encrypted file fits into one buffer. */ readlen = fread( inBufr, 1, bSIZE, keyf ); if( 0 == readlen ) { Err( "Error reading input from %s; %s.\n", argv[1], ErrStr ); return( EXIT_FAILURE ); } /* Expand and hash the passphrase. * NOTE: This "emulates" Windows Unicode encoding. * Windows uses Unicode UTF-16-LE, so plain ASCII must be expanded. * This is a kludge. We should be using real Unicode here. */ phraselen = strlen( argv[2] ); doublewide = (unsigned char *)calloc( phraselen, 1 ); for( i = 0; i < phraselen; i++ ) { doublewide[i*2] = argv[2][i]; doublewide[1+(i*2)] = '\0'; } Err( "PassPhrase: " ); phraselen *= 2; /* We've doubled the length of the user passphrase. */ hexlist( doublewide, phraselen ); (void)SHA256( doublewide, phraselen, ppHash ); free( doublewide ); /* Initializes the AES_KEY with the key and set the 256-bit key size. * Note: 256 == key size (32 bytes) in bits. * This initializes the variable. */ (void)AES_set_decrypt_key( ppHash, 256, aes_ppHash ); /* Set the IV and perform the decryption (by calling * the "AES_cbc_encrypt()" ENcryption function?!). */ memset( iv, 0, BLOCK_SIZE ); AES_cbc_encrypt( inBufr, outBufr, readlen, aes_ppHash, iv, AES_DECRYPT ); /* We now have a decrypted blob... * Trim away the PKCS#7 padding. */ readlen -= (int)outBufr[readlen-1]; /* Output the results. * 1) The decrypted Server Passphrase. * 2) The decrypted Server Secret. * 3) The hash calculated from the decrypted Server Passphrase. * * The Server Passphrase starts at offset 32. */ Err( " Server\nPassphrase: " ); hexlist( &outBufr[SHA256_DIGEST_LENGTH], readlen-SHA256_DIGEST_LENGTH ); /* The first 32 bytes are the Server Secret (SHA256( Server_Passphrase )). */ Err( " Server\n Secret: " ); hexlist( outBufr, SHA256_DIGEST_LENGTH ); /* Calculate our own hash of the Server Passphrase so the user can compare. * This result should match the Server Secret. */ (void)SHA256( &outBufr[SHA256_DIGEST_LENGTH], (readlen - SHA256_DIGEST_LENGTH), ppHash ); Err( "Validation\n Hash: " ); hexlist( ppHash, SHA256_DIGEST_LENGTH ); return( EXIT_SUCCESS ); } /* main */ /* ========================================================================== */