summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/msn/msnchallengehandler.cpp
blob: e0bcc9878a04514f15b84ce50b6950ec7a706359 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
    msnchallengehandler.h - Computes a msn challenge response hash key.

    Copyright (c) 2005 by Gregg Edghill       <[email protected]>
    Kopete    (c) 2003-2005 by The Kopete developers <[email protected]>

    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 <qdatastream.h>

#include <kdebug.h>
#include <kmdcodec.h>

MSNChallengeHandler::MSNChallengeHandler(const QString& productKey, const QString& productId)
{
	m_productKey = productKey;
	m_productId  = productId;
}


MSNChallengeHandler::~MSNChallengeHandler()
{
	kdDebug(14140) << k_funcinfo << endl;
}

QString MSNChallengeHandler::computeHash(const QString& challengeString)
{
  	// Step One: THe MD5 Hash.

  	// Combine the received challenge string with the product key.
 	KMD5 md5((challengeString + m_productKey).utf8());
 	QCString digest = md5.hexDigest();

 	kdDebug(14140) << k_funcinfo << "md5: " << digest << endl;

 	QValueVector<Q_INT32> md5Integers(4);
 	for(Q_UINT32 i=0; i < md5Integers.count(); i++)
 	{
 		md5Integers[i] = hexSwap(digest.mid(i*8, 8)).toUInt(0, 16) & 0x7FFFFFFF;
 		kdDebug(14140) << k_funcinfo << ("0x" + hexSwap(digest.mid(i*8, 8))) << " " << md5Integers[i] << endl;
 	}

	// Step Two: Create the challenge string key

	QString challengeKey = challengeString + m_productId;
	// Pad to multiple of 8.
	challengeKey = challengeKey.leftJustify(challengeKey.length() + (8 - challengeKey.length() % 8), '0');

	kdDebug(14140) << k_funcinfo << "challenge key: " << challengeKey << endl;

	QValueVector<Q_INT32> challengeIntegers(challengeKey.length() / 4);
	for(Q_UINT32 i=0; i < challengeIntegers.count(); i++)
	{
		QString sNum = challengeKey.mid(i*4, 4), sNumHex;

		// Go through the number string, determining the hex equivalent of each value
		// and add that to our new hex string for this number.
		for(uint j=0; j < sNum.length(); j++) {
			sNumHex += QString::number((int)sNum[j].latin1(), 16);
		}

		// swap because of the byte ordering issue.
		sNumHex = hexSwap(sNumHex);
		// Assign the converted number.
		challengeIntegers[i] = sNumHex.toInt(0, 16);
		kdDebug(14140) << k_funcinfo << sNum << (": 0x"+sNumHex) << " " << challengeIntegers[i] << endl;
	}

	// Step Three: Create the 64-bit hash key.

	// Get the hash key using the specified arrays.
	Q_INT64 key = createHashKey(md5Integers, challengeIntegers);
	kdDebug(14140) << k_funcinfo << "key: " << key << endl;

	// 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.rightJustify(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.rightJustify(lower.length() + (16 - lower.length() % 16), '0');

	return (upper + lower);
}

Q_INT64 MSNChallengeHandler::createHashKey(const QValueVector<Q_INT32>& md5Integers,
	const QValueVector<Q_INT32>& challengeIntegers)
{
	kdDebug(14140) << k_funcinfo << "Creating 64-bit key." << endl;

	Q_INT64 magicNumber = 0x0E79A9C1L, high = 0L, low = 0L;
		
	for(uint i=0; i < challengeIntegers.count(); i += 2)
	{
		Q_INT64 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;

	QDataStream buffer(QByteArray(8), IO_ReadWrite);
	buffer.setByteOrder(QDataStream::LittleEndian);
	buffer << (Q_INT32)high;
	buffer << (Q_INT32)low;

	buffer.device()->reset();
	buffer.setByteOrder(QDataStream::BigEndian);
	Q_INT64 key;
	buffer >> key;
	
	return key;
}

QString MSNChallengeHandler::hexSwap(const QString& in)
{
	QString sHex = in, swapped;
	while(sHex.length() > 0)
	{
		swapped = swapped + sHex.mid(sHex.length() - 2, 2);
		sHex = sHex.remove(sHex.length() - 2, 2);
	}
	return swapped;
}

QString MSNChallengeHandler::productId()
{
	return m_productId;
}

#include "msnchallengehandler.moc"