1package eu.siacs.conversations.utils;
2
3import android.util.Log;
4import android.util.Pair;
5
6import org.bouncycastle.asn1.x500.X500Name;
7import org.bouncycastle.asn1.x500.style.BCStyle;
8import org.bouncycastle.asn1.x500.style.IETFUtils;
9import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
10import org.bouncycastle.jce.PrincipalUtil;
11
12import java.security.SecureRandom;
13import java.security.cert.CertificateEncodingException;
14import java.security.cert.CertificateParsingException;
15import java.security.cert.X509Certificate;
16import java.security.cert.X509Extension;
17import java.text.Normalizer;
18import java.util.ArrayList;
19import java.util.Arrays;
20import java.util.Collection;
21import java.util.Iterator;
22import java.util.LinkedHashSet;
23import java.util.List;
24
25import eu.siacs.conversations.Config;
26import eu.siacs.conversations.xmpp.jid.InvalidJidException;
27import eu.siacs.conversations.xmpp.jid.Jid;
28
29public final class CryptoHelper {
30 public static final String FILETRANSFER = "?FILETRANSFERv1:";
31 private final static char[] hexArray = "0123456789abcdef".toCharArray();
32 private final static char[] vowels = "aeiou".toCharArray();
33 private final static char[] consonants = "bcdfghjklmnpqrstvwxyz".toCharArray();
34 final public static byte[] ONE = new byte[] { 0, 0, 0, 1 };
35
36 public static String bytesToHex(byte[] bytes) {
37 char[] hexChars = new char[bytes.length * 2];
38 for (int j = 0; j < bytes.length; j++) {
39 int v = bytes[j] & 0xFF;
40 hexChars[j * 2] = hexArray[v >>> 4];
41 hexChars[j * 2 + 1] = hexArray[v & 0x0F];
42 }
43 return new String(hexChars);
44 }
45
46 public static byte[] hexToBytes(String hexString) {
47 int len = hexString.length();
48 byte[] array = new byte[len / 2];
49 for (int i = 0; i < len; i += 2) {
50 array[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
51 .digit(hexString.charAt(i + 1), 16));
52 }
53 return array;
54 }
55
56 public static String hexToString(final String hexString) {
57 return new String(hexToBytes(hexString));
58 }
59
60 public static byte[] concatenateByteArrays(byte[] a, byte[] b) {
61 byte[] result = new byte[a.length + b.length];
62 System.arraycopy(a, 0, result, 0, a.length);
63 System.arraycopy(b, 0, result, a.length, b.length);
64 return result;
65 }
66
67 public static String randomMucName(SecureRandom random) {
68 return randomWord(3, random) + "." + randomWord(7, random);
69 }
70
71 private static String randomWord(int lenght, SecureRandom random) {
72 StringBuilder builder = new StringBuilder(lenght);
73 for (int i = 0; i < lenght; ++i) {
74 if (i % 2 == 0) {
75 builder.append(consonants[random.nextInt(consonants.length)]);
76 } else {
77 builder.append(vowels[random.nextInt(vowels.length)]);
78 }
79 }
80 return builder.toString();
81 }
82
83 /**
84 * Escapes usernames or passwords for SASL.
85 */
86 public static String saslEscape(final String s) {
87 final StringBuilder sb = new StringBuilder((int) (s.length() * 1.1));
88 for (int i = 0; i < s.length(); i++) {
89 char c = s.charAt(i);
90 switch (c) {
91 case ',':
92 sb.append("=2C");
93 break;
94 case '=':
95 sb.append("=3D");
96 break;
97 default:
98 sb.append(c);
99 break;
100 }
101 }
102 return sb.toString();
103 }
104
105 public static String saslPrep(final String s) {
106 return Normalizer.normalize(s, Normalizer.Form.NFKC);
107 }
108
109 public static String prettifyFingerprint(String fingerprint) {
110 if (fingerprint==null) {
111 return "";
112 } else if (fingerprint.length() < 40) {
113 return fingerprint;
114 }
115 StringBuilder builder = new StringBuilder(fingerprint.replaceAll("\\s",""));
116 for(int i=8;i<builder.length();i+=9) {
117 builder.insert(i, ' ');
118 }
119 return builder.toString();
120 }
121
122 public static String[] getOrderedCipherSuites(final String[] platformSupportedCipherSuites) {
123 final Collection<String> cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS));
124 final List<String> platformCiphers = Arrays.asList(platformSupportedCipherSuites);
125 cipherSuites.retainAll(platformCiphers);
126 cipherSuites.addAll(platformCiphers);
127 filterWeakCipherSuites(cipherSuites);
128 return cipherSuites.toArray(new String[cipherSuites.size()]);
129 }
130
131 private static void filterWeakCipherSuites(final Collection<String> cipherSuites) {
132 final Iterator<String> it = cipherSuites.iterator();
133 while (it.hasNext()) {
134 String cipherName = it.next();
135 // remove all ciphers with no or very weak encryption or no authentication
136 for (String weakCipherPattern : Config.WEAK_CIPHER_PATTERNS) {
137 if (cipherName.contains(weakCipherPattern)) {
138 it.remove();
139 break;
140 }
141 }
142 }
143 }
144
145 public static Pair<Jid,String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException, CertificateParsingException {
146 Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
147 List<String> emails = new ArrayList<>();
148 if (alternativeNames != null) {
149 for(List<?> san : alternativeNames) {
150 Integer type = (Integer) san.get(0);
151 if (type == 1) {
152 emails.add((String) san.get(1));
153 }
154 }
155 }
156 X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
157 if (emails.size() == 0) {
158 emails.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()));
159 }
160 String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue());
161 if (emails.size() >= 1) {
162 return new Pair<>(Jid.fromString(emails.get(0)), name);
163 } else {
164 return null;
165 }
166 }
167}