/* * This source file is used from Kopete, due the fact they've got * it working earlier, it takes advantage of the the KDE/Qt API, * and should work for both big and little endian systems. * The debug lines have been replaced with KMess-style messages. */ /* msnchallengehandler.h - Computes a msn challenge response hash key. Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> Kopete (c) 2003-2005 by The Kopete developers <kopete-devel@kde.org> Portions taken from http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges ************************************************************************* * * * 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. * * * ************************************************************************* */ #include "msnchallengehandler.h" #include "../kmessdebug.h" #include <QByteArray> #include <QCryptographicHash> #include <QDataStream> #define PRODUCT_KEY "ILTXC!4IXB5FB*PX" #define PRODUCT_ID "PROD0119GSJUC$18" MSNChallengeHandler::MSNChallengeHandler() { productKey_ = PRODUCT_KEY; productId_ = PRODUCT_ID; } MSNChallengeHandler::~MSNChallengeHandler() { } 00057 const QString MSNChallengeHandler::computeHash( const QString& challengeString ) { // Step One: THe MD5 Hash. // Combine the received challenge string with the product key. const QByteArray digest( QCryptographicHash::hash( ( challengeString + productKey_ ).toUtf8(), QCryptographicHash::Md5 ).toHex() ); #ifdef KMESSDEBUG_CHALLENGEHANDLER kDebug() << "md5: " << digest; #endif QVector<qint32> md5Integers(4); for( int i = 0; i < md5Integers.count(); i++ ) { md5Integers[i] = hexSwap( digest.mid( i * 8, 8 ) ).toUInt( 0, 16 ) & 0x7FFFFFFF; #ifdef KMESSDEBUG_CHALLENGEHANDLER kDebug() << "hex " << i << " 0x" + hexSwap( digest.mid( i * 8, 8 ) ) << " = " << md5Integers[i]; #endif } // Step Two: Create the challenge string key QString challengeKey( challengeString + productId_ ); // Pad to multiple of 8. challengeKey = challengeKey.leftJustified(challengeKey.length() + ( 8 - challengeKey.length() % 8 ), '0'); #ifdef KMESSDEBUG_CHALLENGEHANDLER kDebug() << "challenge key: " << challengeKey; #endif QVector<qint32> challengeIntegers( challengeKey.length() / 4 ); for( int i = 0; i < challengeIntegers.count(); i++ ) { QString sNumHex; const QString& sNum( challengeKey.mid( i * 4, 4 ) ); // Go through the number string, determining the hex equivalent of each value // and add that to our new hex string for this number. for( int j = 0; j < sNum.length(); j++ ) { sNumHex += QString::number( ( int ) sNum[j].toLatin1(), 16); } // swap because of the byte ordering issue. sNumHex = hexSwap( sNumHex ); // Assign the converted number. challengeIntegers[i] = sNumHex.toInt(0, 16); #ifdef KMESSDEBUG_CHALLENGEHANDLER kDebug() << "challenge int " << sNum << (": 0x" + sNumHex) << " = " << challengeIntegers[i]; #endif } // Step Three: Create the 64-bit hash key. // Get the hash key using the specified arrays. qint64 key = createHashKey( md5Integers, challengeIntegers ); // Step Four: Create the final hash key. QString upper( QString::number( QString( digest.mid( 0, 16 ) ).toULongLong( 0, 16 ) ^ key, 16 ) ); if( upper.length() % 16 != 0 ) { upper = upper.rightJustified( upper.length() + (16 - upper.length() % 16), '0' ); } QString lower( QString::number( QString( digest.mid( 16, 16 ) ).toULongLong( 0, 16 ) ^ key, 16 ) ); if( lower.length() % 16 != 0 ) { lower = lower.rightJustified( lower.length() + (16 - lower.length() % 16), '0' ); } return ( upper + lower ); } 00136 qint64 MSNChallengeHandler::createHashKey(const QVector<qint32>& md5Integers, const QVector<qint32>& challengeIntegers) { #ifdef KMESSDEBUG_CHALLENGEHANDLER kDebug(14140) << "Creating 64-bit key."; #endif qint64 magicNumber = 0x0E79A9C1L, high = 0L, low = 0L; for( int i = 0; i < challengeIntegers.count(); i += 2 ) { qint64 temp = ( (challengeIntegers[i] * magicNumber ) % 0x7FFFFFFF) + high; temp = ( ( temp * md5Integers[0]) + md5Integers[1] ) % 0x7FFFFFFF; high = ( challengeIntegers[i + 1] + temp ) % 0x7FFFFFFF; high = ( ( high * md5Integers[2] ) + md5Integers[3]) % 0x7FFFFFFF; low += high + temp; } high = ( high + md5Integers[1] ) % 0x7FFFFFFF; low = ( low + md5Integers[3] ) % 0x7FFFFFFF; QByteArray tempArray( 8, '0' ); QDataStream buffer(&tempArray,QIODevice::ReadWrite); buffer.setByteOrder(QDataStream::LittleEndian); buffer << (qint32) high; buffer << (qint32) low; buffer.device()->reset(); buffer.setByteOrder( QDataStream::BigEndian ); qint64 key; buffer >> key; return key; } 00177 const QString MSNChallengeHandler::hexSwap( const QString& in ) { QString swapped; QString sHex( in ); while(sHex.length() > 0) { swapped = swapped + sHex.mid( sHex.length() - 2, 2 ); sHex = sHex.remove( sHex.length() - 2, 2 ); } return swapped; } 00192 const QString& MSNChallengeHandler::getProductId() const { return productId_; }