aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
blob: e9ad71971c13f28ca1e0c775c239e2405c26c81c (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
package eu.siacs.conversations.utils;

import android.util.Pair;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;

import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.text.Normalizer;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;

import eu.siacs.conversations.Config;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;

public final class CryptoHelper {
	public static final String FILETRANSFER = "?FILETRANSFERv1:";
	private final static char[] hexArray = "0123456789abcdef".toCharArray();
	private final static char[] vowels = "aeiou".toCharArray();
	private final static char[] consonants = "bcdfghjklmnpqrstvwxyz".toCharArray();
	final public static byte[] ONE = new byte[] { 0, 0, 0, 1 };

	public static String bytesToHex(byte[] bytes) {
		char[] hexChars = new char[bytes.length * 2];
		for (int j = 0; j < bytes.length; j++) {
			int v = bytes[j] & 0xFF;
			hexChars[j * 2] = hexArray[v >>> 4];
			hexChars[j * 2 + 1] = hexArray[v & 0x0F];
		}
		return new String(hexChars);
	}

	public static byte[] hexToBytes(String hexString) {
		int len = hexString.length();
		byte[] array = new byte[len / 2];
		for (int i = 0; i < len; i += 2) {
			array[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
					.digit(hexString.charAt(i + 1), 16));
		}
		return array;
	}

	public static String hexToString(final String hexString) {
		return new String(hexToBytes(hexString));
	}

	public static byte[] concatenateByteArrays(byte[] a, byte[] b) {
		byte[] result = new byte[a.length + b.length];
		System.arraycopy(a, 0, result, 0, a.length);
		System.arraycopy(b, 0, result, a.length, b.length);
		return result;
	}

	public static String randomMucName(SecureRandom random) {
		return randomWord(3, random) + "." + randomWord(7, random);
	}

	private static String randomWord(int lenght, SecureRandom random) {
		StringBuilder builder = new StringBuilder(lenght);
		for (int i = 0; i < lenght; ++i) {
			if (i % 2 == 0) {
				builder.append(consonants[random.nextInt(consonants.length)]);
			} else {
				builder.append(vowels[random.nextInt(vowels.length)]);
			}
		}
		return builder.toString();
	}

	/**
	 * Escapes usernames or passwords for SASL.
	 */
	public static String saslEscape(final String s) {
		final StringBuilder sb = new StringBuilder((int) (s.length() * 1.1));
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			switch (c) {
				case ',':
					sb.append("=2C");
					break;
				case '=':
					sb.append("=3D");
					break;
				default:
					sb.append(c);
					break;
			}
		}
		return sb.toString();
	}

	public static String saslPrep(final String s) {
		return Normalizer.normalize(s, Normalizer.Form.NFKC);
	}

	public static String prettifyFingerprint(String fingerprint) {
		if (fingerprint==null) {
			return "";
		} else if (fingerprint.length() < 40) {
			return fingerprint;
		}
		StringBuilder builder = new StringBuilder(fingerprint.replaceAll("\\s",""));
		for(int i=8;i<builder.length();i+=9) {
			builder.insert(i, ' ');
		}
		return builder.toString();
	}

	public static String[] getOrderedCipherSuites(final String[] platformSupportedCipherSuites) {
		final Collection<String> cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS));
		final List<String> platformCiphers = Arrays.asList(platformSupportedCipherSuites);
		cipherSuites.retainAll(platformCiphers);
		cipherSuites.addAll(platformCiphers);
		filterWeakCipherSuites(cipherSuites);
		return cipherSuites.toArray(new String[cipherSuites.size()]);
	}

	private static void filterWeakCipherSuites(final Collection<String> cipherSuites) {
		final Iterator<String> it = cipherSuites.iterator();
		while (it.hasNext()) {
			String cipherName = it.next();
			// remove all ciphers with no or very weak encryption or no authentication
			for (String weakCipherPattern : Config.WEAK_CIPHER_PATTERNS) {
				if (cipherName.contains(weakCipherPattern)) {
					it.remove();
					break;
				}
			}
		}
	}

	public static Pair<Jid,String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException {
		X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
		//String xmpp = IETFUtils.valueToString(x500name.getRDNs(new ASN1ObjectIdentifier("1.3.6.1.5.5.7.8.5"))[0].getFirst().getValue());
		String email = IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue());
		String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue());
		return new Pair<>(Jid.fromString(email),name);
	}
}