2
This file is part of libqobex.
4
Copyright (C) 2000 Stefan Westerfeld stefan@space.twc.de
5
Copyright (c) 2003 Mathias Froehlich <Mathias.Froehlich@web.de>
7
This library is free software; you can redistribute it and/or
8
modify it under the terms of the GNU Library General Public
9
License as published by the Free Software Foundation; either
10
version 2 of the License, or (at your option) any later version.
12
This library is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
Library General Public License for more details.
17
You should have received a copy of the GNU Library General Public License
18
along with this library; see the file COPYING.LIB. If not, write to
19
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
20
Boston, MA 02110-1301, USA.
29
#include <qtextstream.h>
30
#include <qtextcodec.h>
32
#include "qobexlengthvaluebase.h"
33
#include "qobexarray.h"
34
#include "qobexauth.h"
39
#define myDebug(a) qDebug a
41
#define myDebug(a) (void)0
46
Yet an other md5 sum implementation. The first cut was copied from arts
47
in the KDE cvs tree. Then modified it to fit here.
48
If you find libqobex as part of kde, note that we don't use kde's md5
49
routine since libqobex should also work without kde.
53
* I used the central part of Colin Plumb's public domain MD5 implementation
57
* This code implements the MD5 message-digest algorithm.
58
* The algorithm is due to Ron Rivest. This code was
59
* written by Colin Plumb in 1993, no copyright is claimed.
60
* This code is in the public domain; do with it what you wish.
62
* Equivalent code is available from RSA Data Security, Inc.
63
* This code has been tested against that, and is equivalent,
64
* except that you don't need to include two pages of legalese
69
/* The four core functions - F1 is optimized somewhat */
71
/* #define F1(x, y, z) (x & y | ~x & z) */
72
#define F1(x, y, z) (z ^ (x & (y ^ z)))
73
#define F2(x, y, z) F1(z, x, y)
74
#define F3(x, y, z) (x ^ y ^ z)
75
#define F4(x, y, z) (y ^ (x | ~z))
77
/* This is the central step in the MD5 algorithm. */
78
#define MD5STEP(f, w, x, y, z, data, s) \
79
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
82
* The core of the MD5 algorithm, this alters an existing MD5 hash to
83
* reflect the addition of 16 longwords of new data. MD5Update blocks
84
* the data and converts bytes into longwords for this routine.
86
void MD5Transform(Q_UINT32 buf[4], Q_UINT32 in[16])
88
myDebug(( "MD5Transform()" ));
94
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
95
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
96
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
97
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
98
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
99
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
100
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
101
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
102
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
103
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
104
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
105
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
106
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
107
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
108
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
109
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
111
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
112
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
113
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
114
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
115
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
116
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
117
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
118
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
119
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
120
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
121
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
122
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
123
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
124
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
125
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
126
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
128
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
129
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
130
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
131
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
132
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
133
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
134
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
135
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
136
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
137
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
138
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
139
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
140
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
141
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
142
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
143
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
145
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
146
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
147
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
148
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
149
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
150
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
151
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
152
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
153
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
154
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
155
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
156
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
157
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
158
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
159
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
160
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
173
QByteArray QObexMD5( const QByteArray& data ) {
174
myDebug(( "QObexMD5( ... )" ));
175
QByteArray md5sum(16);
176
unsigned long finalsize = data.size()+1; /* in bytes */
177
unsigned int i = 0, j = 0;
178
unsigned char w = '\0';
179
Q_UINT32 buffer[4] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476};
182
while((finalsize & 63) != 56) finalsize++;
185
for( i=0, j=0; i<finalsize; i++ )
187
if(i < data.size()) {
189
} else if(i == data.size()) {
191
} else if((finalsize-i > 8) || (finalsize-i <= 4)) {
194
/* well, the length thing encoded in here will only
195
work until 2^32 bits (though md5 would support 2^64
197
w = ((data.size()*8) >> ((i+8-finalsize)*8)) & 0xff;
202
case 1: in[j] |= w << 8;
204
case 2: in[j] |= w << 16;
206
case 3: in[j] |= w << 24;
209
MD5Transform(buffer,in);
216
for( i=0, j=0;j<4;j++)
218
md5sum[i++] = buffer[j] & 0xff;
219
md5sum[i++] = (buffer[j] >> 8) & 0xff;
220
md5sum[i++] = (buffer[j] >> 16) & 0xff;
221
md5sum[i++] = (buffer[j] >> 24) & 0xff;
228
QObexAuthDigestBase::text_encoding QObexAuthDigestBase::encoding_list[] = {
229
{ "ISO-8859-1", QObexAuthDigestBase::Realm_ISO_8859_1 },
230
{ "ISO-8859-2", QObexAuthDigestBase::Realm_ISO_8859_2 },
231
{ "ISO-8859-3", QObexAuthDigestBase::Realm_ISO_8859_3 },
232
{ "ISO-8859-4", QObexAuthDigestBase::Realm_ISO_8859_4 },
233
{ "ISO-8859-5", QObexAuthDigestBase::Realm_ISO_8859_5 },
234
{ "ISO-8859-6", QObexAuthDigestBase::Realm_ISO_8859_6 },
235
{ "ISO-8859-7", QObexAuthDigestBase::Realm_ISO_8859_7 },
236
{ "ISO-8859-8", QObexAuthDigestBase::Realm_ISO_8859_8 },
237
{ "ISO-8859-9", QObexAuthDigestBase::Realm_ISO_8859_9 },
238
{ "utf-16", QObexAuthDigestBase::Realm_UNICODE },
239
// Use latin 1 for ascii. Since latin1 is a superset this should be
240
// ok for conversion to QString.
241
// For conversion to binary arrays we have to check for real latin-1
242
// first and sign that encoding. This way outgoing engodings will
243
// never be ascii, but that does't matter ...
244
{ "ISO-8859-1", QObexAuthDigestBase::Realm_ASCII },
248
void QObexAuthDigestBase::appendStringValue( Q_UINT8 tag, const QString& s ) {
249
QTextCodec* codec = 0;
252
for ( idx = 0; encoding_list[idx].name; ++idx ) {
253
codec = QTextCodec::codecForName( encoding_list[idx].name );
254
if ( codec && codec->canEncode( s ) ) {
255
realm = encoding_list[idx].tag;
262
// if not found take unicode. This one will fit best ...
263
codec = QTextCodec::codecForName ( "utf-16" );
264
realm = Realm_UNICODE;
270
QCString encoded = codec->fromUnicode( s );
271
encoded = " " + encoded;
272
encoded.at( 0 ) = realm;
273
appendTag( tag, encoded );
276
QString QObexAuthDigestBase::getStringTag( Q_UINT8 tag ) const {
277
QByteArray data = getTag( tag );
278
if ( 1 < data.size() ) {
279
Q_UINT8 realm = data.at( 0 );
280
QTextCodec* codec = 0;
282
for ( idx = 0; encoding_list[idx].name; ++idx ) {
283
if ( encoding_list[idx].tag == realm ) {
284
codec = QTextCodec::codecForName ( encoding_list[idx].name );
290
codec = QTextCodec::codecForContent ( data.data() + 1, data.size() - 1);
293
return codec->toUnicode( data.data() + 1, data.size() - 1 );
295
return QString::null;
298
QByteArray QObexAuthDigestBase::computeDigest( const QByteArray& d1, const QByteArray& d2 ) {
299
QByteArray message( 1 + d1.size() + d2.size() );
300
::memcpy( message.data(), d1.data(), d1.size() );
301
message[ d1.size() ] = ':';
302
::memcpy( message.data()+d1.size()+1, d2.data(), d2.size() );
303
return QObexMD5( message );
306
QByteArray QObexAuthDigestBase::randomNonce() {
307
myDebug(( "QObexAuthDigestChallenge::randomNonce()" ));
308
QObexArray rnd( 16 );
309
rnd.uint32( 0, ::rand() );
310
rnd.uint32( 4, ::rand() );
311
rnd.uint32( 8, ::rand() );
312
rnd.uint32( 12, ::rand() );
313
return QObexMD5( rnd );
316
QString QObexAuthDigestBase::toString( const QByteArray& data ) {
318
QTextStream stream( &str, IO_WriteOnly );
320
for ( Q_ULONG i = 0; i < data.size(); ++i )
321
stream << QString().sprintf("%02X", (unsigned char)data[i] );
326
// Constructor to create data for a new AuthChallenge header.
327
QObexAuthDigestChallenge::QObexAuthDigestChallenge( const AuthInfo& ai ) {
328
myDebug(( "QObexAuthDigestChallenge::QObexAuthDigestChallenge( ... )" ));
330
appendTag( NonceTag, randomNonce() );
332
if ( !ai.realm.isNull() )
333
appendStringValue( RealmTag, ai.realm );
335
if ( 0 < ai.options ) {
336
QByteArray data( 1 );
337
data.at( 0 ) = ai.options;
338
appendTag( OptionsTag, data );
342
// Constructor used to parse the data given here.
343
QObexAuthDigestChallenge::QObexAuthDigestChallenge( const QByteArray& data )
344
: QObexAuthDigestBase( data ) {
345
myDebug(( "QObexAuthDigestChallenge::QObexAuthDigestChallenge( ... )" ));
348
// Needed for list handling
349
QObexAuthDigestChallenge::QObexAuthDigestChallenge() {
350
myDebug(( "QObexAuthDigestChallenge::QObexAuthDigestChallenge()" ));
353
// Read access to the fields of the header data.
354
QByteArray QObexAuthDigestChallenge::nonce() const {
355
myDebug(( "QObexAuthDigestChallenge::nonce()" ));
356
return getTag( NonceTag );
359
QString QObexAuthDigestChallenge::realm() const {
360
myDebug(( "QObexAuthDigestChallenge::realm()" ));
361
return getStringTag( RealmTag );
364
bool QObexAuthDigestChallenge::hasRealm() const {
365
myDebug(( "QObexAuthDigestChallenge::hasRealm()" ));
366
return hasTag( RealmTag );
369
int QObexAuthDigestChallenge::options() const {
370
myDebug(( "QObexAuthDigestChallenge::options()" ));
371
if ( hasTag( OptionsTag ) ) {
372
QObexArray data = getTag( OptionsTag );
373
if ( data.size() == 1 )
374
return data.uint8( 0 );
381
bool QObexAuthDigestChallenge::readOnly() const {
382
myDebug(( "QObexAuthDigestChallenge::readOnly()" ));
383
return options() & AccessReadOnly;
386
bool QObexAuthDigestChallenge::userIdRequired() const {
387
myDebug(( "QObexAuthDigestChallenge::userIdRequired()" ));
388
return options() & SendUserId;
391
bool QObexAuthDigestChallenge::hasOptions() const {
392
myDebug(( "QObexAuthDigestChallenge::hasOptions()" ));
393
return hasTag( OptionsTag );
396
QString QObexAuthDigestChallenge::toString( int indent ) const {
398
indentStr.fill( QChar( ' ' ), indent );
400
QTextStream stream( &str, IO_WriteOnly );
402
stream << indentStr << "Nonce: "
403
<< QObexAuthDigestBase::toString( nonce() ) << "\n";
405
stream << indentStr << "Realm: "
408
stream << indentStr << "Options: "
409
<< QString().sprintf("0x%02X", (unsigned char)options() ) << " "
410
<< ((options() & SendUserId) ? "(Send Userid) " : "" )
411
<< ((options() & AccessReadOnly) ? "(ReadOnly) " : "" )
414
stream << indentStr << "Options: Not set, defaults to 0x00\n";
419
// Constructor to create data for a new AuthResponse header.
420
QObexAuthDigestResponse::QObexAuthDigestResponse( const QByteArray& nonce, const AuthInfo& ai ) {
421
myDebug(( "QObexAuthDigestResponse::QObexAuthDigestResponse()" ));
423
Q_ASSERT( !nonce.isEmpty() );
424
appendTag( RequestDigestTag, computeDigest( nonce, ai.secret ) );
425
appendTag( NonceTag, nonce );
427
if ( !ai.userId.isNull() )
428
appendStringValue( UserIdTag, ai.userId );
431
// Constructor used to parse the data given here.
432
QObexAuthDigestResponse::QObexAuthDigestResponse( const QByteArray& data )
433
: QObexAuthDigestBase( data ) {
434
myDebug(( "QObexAuthDigestResponse::QObexAuthDigestResponse()" ));
437
// Needed for list handling
438
QObexAuthDigestResponse::QObexAuthDigestResponse() {
439
myDebug(( "QObexAuthDigestResponse::QObexAuthDigestResponse()" ));
442
// Read access to the fields of the header data.
443
QByteArray QObexAuthDigestResponse::requestDigest() const {
444
myDebug(( "QObexAuthDigestResponse::requestDigest()" ));
445
return getTag( RequestDigestTag );
448
QByteArray QObexAuthDigestResponse::nonce() const {
449
myDebug(( "QObexAuthDigestResponse::nonce()" ));
450
return getTag( NonceTag );
453
bool QObexAuthDigestResponse::hasNonce() const {
454
myDebug(( "QObexAuthDigestResponse::hasNonce()" ));
455
return hasTag( NonceTag );
458
QString QObexAuthDigestResponse::userId() const {
459
myDebug(( "QObexAuthDigestResponse::userId()" ));
460
return getStringTag( UserIdTag );
463
bool QObexAuthDigestResponse::hasUserId() const {
464
myDebug(( "QObexAuthDigestResponse::hasUserId()" ));
465
return hasTag( UserIdTag );
468
bool QObexAuthDigestResponse::authenticate( const QByteArray& secret, const QByteArray& nonce ) const {
469
myDebug(( "QObexAuthDigestResponse::authenticate( ... )" ));
470
return getTag( RequestDigestTag ) == computeDigest( nonce, secret );
473
QString QObexAuthDigestResponse::toString( int indent ) const {
475
indentStr.fill( QChar( ' ' ), indent );
477
QTextStream stream( &str, IO_WriteOnly );
479
stream << indentStr << "Request Digest: "
480
<< QObexAuthDigestBase::toString( requestDigest() ) << "\n";
482
stream << indentStr << "Nonce: "
483
<< QObexAuthDigestBase::toString( nonce() ) << "\n";
485
stream << indentStr << "UserId: " << userId() << "\n";