aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/pixart/messenger/crypto/sasl/DigestMd5.java
blob: e974d8cd4f22e8026baedff0e465fd706c7652d6 (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
package de.pixart.messenger.crypto.sasl;

import android.util.Base64;

import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import de.pixart.messenger.entities.Account;
import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.xml.TagWriter;

public class DigestMd5 extends SaslMechanism {
    public DigestMd5(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
        super(tagWriter, account, rng);
    }

    @Override
    public int getPriority() {
        return 10;
    }

    @Override
    public String getMechanism() {
        return "DIGEST-MD5";
    }

    private State state = State.INITIAL;

    @Override
    public String getResponse(final String challenge) throws AuthenticationException {
        switch (state) {
            case INITIAL:
                state = State.RESPONSE_SENT;
                final String encodedResponse;
                try {
                    final Tokenizer tokenizer = new Tokenizer(Base64.decode(challenge, Base64.DEFAULT));
                    String nonce = "";
                    for (final String token : tokenizer) {
                        final String[] parts = token.split("=", 2);
                        if (parts[0].equals("nonce")) {
                            nonce = parts[1].replace("\"", "");
                        } else if (parts[0].equals("rspauth")) {
                            return "";
                        }
                    }
                    final String digestUri = "xmpp/" + account.getServer();
                    final String nonceCount = "00000001";
                    final String x = account.getUsername() + ":" + account.getServer() + ":"
                            + account.getPassword();
                    final MessageDigest md = MessageDigest.getInstance("MD5");
                    final byte[] y = md.digest(x.getBytes(Charset.defaultCharset()));
                    final String cNonce = new BigInteger(100, rng).toString(32);
                    final byte[] a1 = CryptoHelper.concatenateByteArrays(y,
                            (":" + nonce + ":" + cNonce).getBytes(Charset.defaultCharset()));
                    final String a2 = "AUTHENTICATE:" + digestUri;
                    final String ha1 = CryptoHelper.bytesToHex(md.digest(a1));
                    final String ha2 = CryptoHelper.bytesToHex(md.digest(a2.getBytes(Charset
                            .defaultCharset())));
                    final String kd = ha1 + ":" + nonce + ":" + nonceCount + ":" + cNonce
                            + ":auth:" + ha2;
                    final String response = CryptoHelper.bytesToHex(md.digest(kd.getBytes(Charset
                            .defaultCharset())));
                    final String saslString = "username=\"" + account.getUsername()
                            + "\",realm=\"" + account.getServer() + "\",nonce=\""
                            + nonce + "\",cnonce=\"" + cNonce + "\",nc=" + nonceCount
                            + ",qop=auth,digest-uri=\"" + digestUri + "\",response="
                            + response + ",charset=utf-8";
                    encodedResponse = Base64.encodeToString(
                            saslString.getBytes(Charset.defaultCharset()),
                            Base64.NO_WRAP);
                } catch (final NoSuchAlgorithmException e) {
                    throw new AuthenticationException(e);
                }

                return encodedResponse;
            case RESPONSE_SENT:
                state = State.VALID_SERVER_RESPONSE;
                break;
            case VALID_SERVER_RESPONSE:
                if (challenge == null) {
                    return null; //everything is fine
                }
            default:
                throw new InvalidStateException(state);
        }
        return null;
    }
}