/////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #include "gsCrypt.h" #include "gsLargeInt.h" #include "gsSHA1.h" // **Please refer to gsCrypt.h for public interface functions** /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #ifdef GS_CRYPT_RSA_ES_OAEP static gsi_i32 gsCryptRSAOAEPEncryptBuffer(const gsCryptRSAKey *publicKey, const unsigned char *plainText, gsi_u32 len, unsigned char buffer[GS_CRYPT_RSA_BYTE_SIZE]); static gsi_i32 gsCryptRSAOAEPDecryptBuffer(const gsCryptRSAKey *privateKey, const unsigned char cipherText[GS_CRYPT_RSA_BYTE_SIZE], unsigned char *plainText, gsi_u32 *lenout); static void gsiCryptRSAGenerateSeed(unsigned char *buffer, gsi_u32 len); static gsi_bool gsiCryptRSAMaskData(unsigned char *data, gsi_u32 len, const unsigned char *maskSeed, gsi_u32 maskLen); #else static gsi_i32 gsCryptRSAPKCS1EncryptBuffer(const gsCryptRSAKey *publicKey, const unsigned char *plainText, gsi_u32 len, unsigned char buffer[GS_CRYPT_RSA_BYTE_SIZE]); static gsi_i32 gsCryptRSAPKCS1DecryptBuffer(const gsCryptRSAKey *privateKey, const unsigned char ciperText[GS_CRYPT_RSA_BYTE_SIZE], unsigned char *plainTextOut, gsi_u32 *lenOut); static void gsiCryptRSAGeneratePad(unsigned char *buffer, gsi_u32 len); #endif /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #ifdef GS_CRYPT_RSA_ES_OAEP // generate a random seed for OAEP void gsiCryptRSAGenerateSeed(unsigned char *buffer, gsi_u32 len) { unsigned int i=0; Util_RandSeed(current_time()); for (i=0; i < len; i++) { //buffer[i] = 0x0c; buffer[i] = (unsigned char)(Util_RandInt(0x00, 0xFF)+1); GS_ASSERT(buffer[i] != 0x00); } } // PKCS#1 v2.1, B.2.1 MGF1, mask generation function // modification: generates mask and applies in place gsi_bool gsiCryptRSAMaskData(unsigned char *data, gsi_u32 dataLen, const unsigned char* seed, gsi_u32 seedLen) { int i=0; int k=0; // The datablock may be used as the seed unsigned char hashValue[GS_CRYPT_HASHSIZE]; // in integer form, NOT HEXSTRING // seed should never be larger than the data block size (but it may be less) if (seedLen > GS_CRYPT_RSA_DATABLOCKSIZE) return gsi_false; for (i=0; (gsi_u32)i= dataLen) return gsi_true; data[i+k] ^= (gsi_u8)hashValue[k]; } } return gsi_true; } #else // generate a random pad for PKCS1 // [0x01 - 0xFF] void gsiCryptRSAGeneratePad(unsigned char *buffer, gsi_u32 len) { unsigned int i=0; Util_RandSeed(current_time()); for (i=0; i < len; i++) { #if defined(GS_CRYPT_NO_RANDOM) #pragma message("GS_CRYPT_NO_RANDOM defined, SSL is NOT SECURE!!!!\r\n") buffer[i] = 0x0c; #else buffer[i] = (unsigned char)(Util_RandInt(0x00, 0xFF)+1); #endif GS_ASSERT(buffer[i] != 0x00); } } #endif /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // RSA Packet = encrypt(lhash, pad, 0x01, plainText) // lhash is constant, since labels are not supported // pad is variable length, depending on plainText length gsi_i32 gsCryptRSAEncryptBuffer(const gsCryptRSAKey *publicKey, const unsigned char *plainText, gsi_u32 len, unsigned char buffer[GS_CRYPT_RSA_BYTE_SIZE]) { #ifdef GS_CRYPT_RSA_ES_OAEP return gsCryptRSAOAEPEncryptBuffer(publicKey, plainText, len, buffer); #else return gsCryptRSAPKCS1EncryptBuffer(publicKey, plainText, len, buffer); #endif } #ifdef GS_CRYPT_RSA_ES_OAEP gsi_i32 gsCryptRSAOAEPEncryptBuffer(const gsCryptRSAKey *publicKey, const unsigned char *plainText, gsi_u32 len, unsigned char buffer[GS_CRYPT_RSA_BYTE_SIZE]) { gsLargeInt_t lintRSAPacket; gsCryptRSAOAEPPacket* packet = (gsCryptRSAOAEPPacket*)lintRSAPacket.mData; #if (GS_CRYPT_HASHSIZE==GS_CRYPT_MD5_HASHSIZE) const gsi_u8 lhash[GS_CRYPT_HASHSIZE] = {0xd4,0x1d,0xd4,0x1d,0x8c,0xd9,0x8f,0x00,0xb2,0x04,0xe9,0x80,0x09,0x98,0xec,0xf8,0x42,0x7e}; // hash of "" #else const gsi_u8 lhash[GS_CRYPT_HASHSIZE] = {0xda,0x39,0xa3,0xee,0x5e,0x6b,0x4b,0x0d,0x32,0x55,0xbf,0xef,0x95,0x60,0x18,0x90,0xaf,0xd8,0x07,0x09}; // hash of "" #endif const unsigned int maxPlainTextLen = GS_CRYPT_RSA_BYTE_SIZE-2*GS_CRYPT_HASHSIZE-2; const unsigned int padSize = maxPlainTextLen-len; // The steps below are taken from PKCS#1, section 7.1.1 "Encryption Operation" // 1. check length if (len > maxPlainTextLen) return -1; // 2. EME-OAEP encoding (pad & pad format) // a. precalculated above (const lhash) // b. create pad // c. concatenate hash+pad+0x01+plainText memcpy(packet->maskedData, lhash, GS_CRYPT_HASHSIZE); memset(&packet->maskedData[GS_CRYPT_HASHSIZE], 0, padSize); // pad with zero bytes packet->maskedData[GS_CRYPT_HASHSIZE+padSize] = 0x01; // RSA encoding format ID (EME-OAEP) memcpy(&packet->maskedData[GS_CRYPT_HASHSIZE+padSize+1], plainText, len); // d. generate random seed (seed isn't masked until h.) gsiCryptRSAGenerateSeed(packet->maskedSeed, GS_CRYPT_HASHSIZE); // e. use (still unmasked) seed to generate a mask for the datablock // f. apply it with xor gsiCryptRSAMaskData(packet->maskedData, GS_CRYPT_RSA_DATABLOCKSIZE, packet->maskedSeed, GS_CRYPT_HASHSIZE); // g. use the masked datablock to generate a mask for the seed // h. apply it with xor gsiCryptRSAMaskData(packet->maskedSeed, GS_CRYPT_HASHSIZE, packet->maskedData, GS_CRYPT_RSA_DATABLOCKSIZE); // i. set first byte to 0x00 packet->headerByte = 0x00; // 3. Encryptitize /* lintRSAPacket.mLength = GS_CRYPT_RSA_BYTE_SIZE/sizeof(gsi_u32); gsLargeIntReverseBytes(&lintRSAPacket); gsLargeIntPowerMod(&lintRSAPacket, &publicKey->exponent, &publicKey->modulus, &lintRSAPacket); gsLargeIntReverseBytes(&lintRSAPacket); // 4. return cipher text memcpy(buffer, lintRSAPacket.mData, GS_CRYPT_RSA_BYTE_SIZE); */ GS_ASSERT(0); // Section above needs revision due to byte order issues return 0; } #else gsi_i32 gsCryptRSAPKCS1EncryptBuffer(const gsCryptRSAKey *publicKey, const unsigned char *plainText, gsi_u32 len, unsigned char buffer[GS_CRYPT_RSA_BYTE_SIZE]) { gsi_u8 buf[GS_CRYPT_RSA_BYTE_SIZE]; gsCryptRSAPKCS1Packet* packet = (gsCryptRSAPKCS1Packet*)buf; gsLargeInt_t lintRSAPacket; if (len > (GS_CRYPT_RSA_BYTE_SIZE-11)) // 2 byte header, 8 byte pad minimum, 1 byte separator return -1; // form the packet packet->headerByte[0] = 0x00; packet->headerByte[1] = 0x02; gsiCryptRSAGeneratePad(packet->data, GS_CRYPT_RSA_BYTE_SIZE-len-3); packet->data[GS_CRYPT_RSA_BYTE_SIZE-len-3] = 0x00; // separator memcpy(&packet->data[GS_CRYPT_RSA_BYTE_SIZE-len-3+1], plainText, len); if (gsi_is_false(gsLargeIntSetFromMemoryStream(&lintRSAPacket, (const gsi_u8*)buf, GS_CRYPT_RSA_BYTE_SIZE)) || gsi_is_false(gsLargeIntPowerMod(&lintRSAPacket, &publicKey->exponent, &publicKey->modulus, &lintRSAPacket)) || gsi_is_false(gsLargeIntWriteToMemoryStream(&lintRSAPacket, buffer)) ) { return -1; } return 0; } #endif /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// gsi_i32 gsCryptRSADecryptBuffer(const gsCryptRSAKey *privateKey, const unsigned char cipherText[GS_CRYPT_RSA_BYTE_SIZE], unsigned char *plainText, gsi_u32 *lenout) { #ifdef GS_CRYPT_RSA_ES_OAEP return gsCryptRSAOAEPDecryptBuffer(privateKey, cipherText, plainText, lenout); #else return gsCryptRSAPKCS1DecryptBuffer(privateKey, cipherText, plainText, lenout); #endif } // Since decryption requires the privatekey, it is usually done by the server. // Decryption is also much slower than encryption. // This is included here as a testing utility but should probably not be used in a game client. #ifndef GS_CRYPT_RSA_ES_OAEP static gsi_i32 gsCryptRSAPKCS1DecryptBuffer(const gsCryptRSAKey *privateKey, const unsigned char cipherText[GS_CRYPT_RSA_BYTE_SIZE], unsigned char *plainText, gsi_u32 *lenout) { int i=0; char* temp; gsLargeInt_t lintRSAPacket; lintRSAPacket.mLength = GS_CRYPT_RSA_BYTE_SIZE/GS_LARGEINT_DIGIT_SIZE_BYTES; memcpy(lintRSAPacket.mData, cipherText, GS_CRYPT_RSA_BYTE_SIZE); if (gsi_is_false(gsLargeIntReverseBytes(&lintRSAPacket)) || // reverse from bytebuffer to lint format gsi_is_false(gsLargeIntPowerMod(&lintRSAPacket, &privateKey->exponent, &privateKey->modulus, &lintRSAPacket)) || gsi_is_false(gsLargeIntReverseBytes(&lintRSAPacket)) // reverse back into a bytebuffer ) { return -1; } // check post exponentiation length if (lintRSAPacket.mLength < (GS_CRYPT_RSA_BYTE_SIZE/GS_LARGEINT_DIGIT_SIZE_BYTES)) return -1; // Check the packet for legality // 1. first byte must be 0x00 // 2. send byte must be 0x02 // 3. pad must be at least 8 bytes and end with 0x00 // 4. payload must be at least 1 byte temp = (char*)lintRSAPacket.mData; if (temp[0] != 0x00) return -1; if (temp[1] != 0x02) return -2; // find the start of the data (first 0x00 byte after the 1st) temp = (char*)lintRSAPacket.mData; for (i=2; iexponent, &privateKey->modulus, &lintRSAPacket); gsLargeIntReverseBytes(&lintRSAPacket); // reverse back into a bytebuffer // check post exponentiation length if (lintRSAPacket.mLength < (GS_CRYPT_RSA_BYTE_SIZE/4)) return -1; // Check the packet for legality // 1. "un-mask" the maskedSeed, using the maskedData gsiCryptRSAMaskData(packet->maskedSeed, GS_CRYPT_HASHSIZE, packet->maskedData, GS_CRYPT_RSA_DATABLOCKSIZE); // 2. "un-mask" the maskedData, using the previously unmasked maskedSeed gsiCryptRSAMaskData(packet->maskedData, GS_CRYPT_RSA_DATABLOCKSIZE, packet->maskedSeed, GS_CRYPT_HASHSIZE); // 3. datablock = [lhash][0x00...][0x01][M] if (0 != memcmp(packet->maskedData, lhash, GS_CRYPT_HASHSIZE)) return -2; // label has doesn't match (mismatched hash algorithms?) i = 33; while(imaskedData[i] == 0x00) i++; // may be zero bytes pad if (i==GS_CRYPT_RSA_BYTE_SIZE || packet->maskedData[i] != 0x01) return -3; // must be a 0x01 following the pad i++; if (i == GS_CRYPT_RSA_BYTE_SIZE) return -4; // founnd the separator, but no message! memcpy(plainText, &packet->maskedData[i], (size_t)(GS_CRYPT_RSA_DATABLOCKSIZE-i)); *lenout = (gsi_u32)(GS_CRYPT_RSA_DATABLOCKSIZE-i); // final length = blocksize - pad return 0; } #endif /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// gsi_i32 gsCryptRSASignData(const gsCryptRSAKey *privateKey, const unsigned char *plainText, gsi_u32 plainTextLen, unsigned char *signedDataOut, gsi_u32 *lenOut) { const unsigned char * hash = NULL; GSI_UNUSED(privateKey); GSI_UNUSED(plainText); GSI_UNUSED(plainTextLen); GSI_UNUSED(signedDataOut); GSI_UNUSED(lenOut); // 1) hash data // hash = MD5(plainText); //GS_ASSERT(0); // not implemented yet // 2) Sign return gsCryptRSASignHash(privateKey, hash, GS_CRYPT_MD5_HASHSIZE, signedDataOut, lenOut); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // The more usual use of RSA signatures // Constructs a PKCS1 signature form of type { [0x00][0x01][0xFF..FF][0x00][hash oid][hash] } = RSA key length gsi_i32 gsCryptRSASignHash(const gsCryptRSAKey *privateKey, const unsigned char *hash, gsi_u32 hashLen, unsigned char *signedDataOut, gsi_u32 *lenOut) { // Encode to PKCS1 signature form gsi_u32 aKeyByteLength = privateKey->modulus.mLength * GS_LARGEINT_DIGIT_SIZE_BYTES; // key length in bytes gsi_u32 aReservedLength = 3; gsi_u32 anOidLen; gsLargeInt_t dataToSign; char * writeBuf = (char*)dataToSign.mData; // Microsoft PKCS #1 headers for various hash algorithms. gsi_u8 md5Header[18] = {0x30,0x20,0x30,0x0C,0x06,0x08,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x05,0x05,0x00,0x04,0x10}; gsi_u8 sha1Header[15] = {0x30,0x21,0x30,0x09,0x06,0x05,0x2B,0x0E,0x03,0x02,0x1A,0x05,0x00,0x04,0x14}; if (hashLen == GS_CRYPT_MD5_HASHSIZE) anOidLen = sizeof(md5Header); else if (hashLen == GS_CRYPT_SHA1_HASHSIZE) anOidLen = sizeof(sha1Header); else return -1; // hash algorithm could not be identified from hashLen // Make sure the key is large enough to sign this hash GS_ASSERT(hashLen + anOidLen + aReservedLength <= aKeyByteLength); if (hashLen + anOidLen + aReservedLength > aKeyByteLength) return -2; // key is too small or hash is too large // fill in header bytes writeBuf[0] = 0x00; writeBuf[1] = 0x01; // pad with 0xFF memset(&writeBuf[2], 0xFF, aKeyByteLength - hashLen - anOidLen - aReservedLength); // set a 0x00 at the end of the 0xFF pad writeBuf[aKeyByteLength - hashLen - anOidLen - 1] = 0x00; // copy in the oid if (hashLen == GS_CRYPT_MD5_HASHSIZE) memcpy(&writeBuf[aKeyByteLength-hashLen-anOidLen], md5Header, sizeof(md5Header)); else if (hashLen == GS_CRYPT_SHA1_HASHSIZE) memcpy(&writeBuf[aKeyByteLength-hashLen-anOidLen], sha1Header, sizeof(sha1Header)); else return -1; // should probably assert here // copy in the hash memcpy(&writeBuf[aKeyByteLength-hashLen], hash, hashLen); // fix byte order for large int dataToSign.mLength = privateKey->modulus.mLength; gsLargeIntReverseBytes(&dataToSign); // sign (a.k.a. encrypt) gsLargeIntPowerMod(&dataToSign, &privateKey->exponent, &privateKey->modulus, &dataToSign); // length of output data is always the length of the private key's modulus GS_ASSERT(dataToSign.mLength == privateKey->modulus.mLength); gsLargeIntReverseBytes(&dataToSign); // switch back to rawbuffer byte order memcpy(signedDataOut, dataToSign.mData, aKeyByteLength); *lenOut = aKeyByteLength; return 0; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // "data" is the text that was signed, encrypted or plaintext // "sig" is the signature value attached to the msg (BIG-endian) // Signature format: // [ 0x00 0x01 0xFF ... 0x00 HashHeader Hash(Data) ] gsi_i32 gsCryptRSAVerifySignedHash(const gsCryptRSAKey *publicKey, const unsigned char *hash, gsi_u32 hashLen, const unsigned char *sig, gsi_u32 sigLen) { gsLargeInt_t lintRSASignature; gsi_u8* packet = (gsi_u8*)lintRSASignature.mData; int i=0; // Microsoft PKCS #1 headers for various hash algorithms. gsi_u8 md5Header[18] = {0x30,0x20,0x30,0x0C,0x06,0x08,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x05,0x05,0x00,0x04,0x10}; gsi_u8 sha1Header[15] = {0x30,0x21,0x30,0x09,0x06,0x05,0x2B,0x0E,0x03,0x02,0x1A,0x05,0x00,0x04,0x14}; // check parameters for common errors if (hash==NULL || sig==NULL) return -1; if (sigLen != publicKey->modulus.mLength*GS_LARGEINT_DIGIT_SIZE_BYTES) return -1; if (hashLen != GS_CRYPT_MD5_HASHSIZE && hashLen != GS_CRYPT_SHA1_HASHSIZE) return -1; // invalid hashsize // "decrypt" the signature lintRSASignature.mLength = (l_word)(sigLen / GS_LARGEINT_DIGIT_SIZE_BYTES); memcpy(lintRSASignature.mData, sig, sigLen); gsLargeIntReverseBytes(&lintRSASignature); gsLargeIntPowerMod(&lintRSASignature, &publicKey->exponent, &publicKey->modulus, &lintRSASignature); gsLargeIntReverseBytes(&lintRSASignature); // Check format, first by 0x00, second byte 0x01 if (packet[0] != 0x00 || packet[1] != 0x01) return -2; // Loop through the 0xFF's for (i=2; i