Reputation: 47
I'm trying to approach a local XMPP server (Openfire) with a NodeJS application.
I would like to use the DIGEST-MD5 mechanism (I am aware that it has been declared obsolete).
I found this article explaining how the mechanism works: https://wiki.xmpp.org/web/SASL_and_DIGEST-MD5
However, when implementing the mechanism as described in the article, my calculated response is incorrect.
I have done my best to find out what I'm doing wrong, but I can't seem to figure it out.
I am certain that the rest of my stanza is correct, it's just the response that isn't right.
Here is my code for calculating the response:
var x = username + ':' + realm + ':' + pswd;
var y = crypto.createHash('md5').update(x).digest();
var a1 = y + ':' + nonce + ':' + cnonce + ':' + authzid;
var a2 = 'AUTHENTICATE:' + digestUri;
var ha1 = crypto.createHash('md5').update(a1).digest("hex");
var ha2 = crypto.createHash('md5').update(a2).digest("hex");
var kd = ha1 + ':' + nonce + ':00000001:' + cnonce + ':auth:' + ha2;
var z = crypto.createHash('md5').update(kd).digest("hex");
Where z is the final response.
As you can see I am making use of the crypto library for my hashing.
The example mentioned in the article above is a follows:
username="rob",realm="cataclysm.cx",nonce="OA6MG9tEQGm2hh",cnonce="OA6MHXh6VqTrRk",nc=00000001,qop=auth,digesturi="xmpp/cataclysm.cx",response=d388dad90d4bbd760a152321f2143af7,charset=utf-8,authzid="[email protected]/myResource"
When I plug all these values into my own implementation (with the password being 'secret'), my calculated response is:
5093acf6b3bc5687231539507cc2fb20
instead of the expected d388dad90d4bbd760a152321f2143af7.
Other examples don't give me the right result either.
So, what on earth am I doing wrong?
Any and all help would be greatly appreciated!
Upvotes: 1
Views: 936
Reputation: 49276
When calculating the response, the third line concatenates the buffer y
(which contains an MD5 hash) with strings, whereby y
is implicitly converted to a string using UTF-8.
However, an arbitrary byte sequence, such as a hash, will be corrupted by a UTF8 encoding, s. here. To prevent this, the individual parts should be concatenated as buffers rather than strings.
In contrast, the remaining concatenations (in this and the other lines) are not critical, since they are true strings:
var crypto = require('crypto');
var charset = 'utf-8';
var username = 'chris';
var realm = 'elwood.innosoft.com';
var nonce = 'OA6MG9tEQGm2hh';
var nc = '00000001';
var cnonce = 'OA6MHXh6VqTrRk';
var digestUri = 'imap/elwood.innosoft.com';
var response = 'd388dad90d4bbd760a152321f2143af7';
var qop = 'auth'
var pswd = 'secret';
var x = username + ':' + realm + ':' + pswd;
var y = crypto.createHash('md5').update(x).digest();
var a1 = Buffer.concat([y, Buffer.from(':' + nonce + ':' + cnonce, 'utf8')]); // + ':' + authzid; // Concat buffers instead of strings
var a2 = 'AUTHENTICATE:' + digestUri;
var ha1 = crypto.createHash('md5').update(a1).digest('hex');
var ha2 = crypto.createHash('md5').update(a2).digest('hex');
var kd = ha1 + ':' + nonce + ':00000001:' + cnonce + ':auth:' + ha2;
var z = crypto.createHash('md5').update(kd).digest('hex');
console.log(z); // d388dad90d4bbd760a152321f2143af7
Note that the sample data in the code snippet is not from the website linked in the question, but from RFC2831 (Using Digest Authentication as a SASL Mechanism), chapter 4:
charset=utf-8,username="chris",realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh",nc=00000001,cnonce="OA6MHXh6VqTrRk",digest-uri="imap/elwood.innosoft.com",response=d388dad90d4bbd760a152321f2143af7,qop=auth
The code returns the result specified in response
, which evidences the correctness of the calculated result.
I don't think that the example of the website linked in the question is consistent. The expected result is identical to that from the RFC example, although some of the relevant input data for the digests is different.
A coincidental match is very unlikely (s. collision probability for MD5), so with high probability the input data and expected result of the linked website do not belong together. In addition, the password is not specified there.
The algorithm applied is described in detail in chapter 2.1.2.1 Response-value. Note that in the RFC example authzid
is not specified in contrast to the example off the linked website. According to the description in chapter 2.1.2.1, in this case ':' + authzid
is simply to be omitted.
As already mentioned in the question, MD5 and thus the algorithm used here is deprecated (s. also RFC6331).
Upvotes: 1