From f9cafc144b39461d7184eb5f7e50c492debd1e99 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 20 Oct 2018 00:05:10 +0200 Subject: [PATCH] refactor magic creates password gen to CryptoHelper --- .../conversations/ui/MagicCreateActivity.java | 16 +- .../conversations/utils/CryptoHelper.java | 474 +++++++++--------- 2 files changed, 243 insertions(+), 247 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java b/src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java index f210e0adc..3cdf1c203 100644 --- a/src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/MagicCreateActivity.java @@ -16,16 +16,13 @@ import java.security.SecureRandom; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.utils.CryptoHelper; import rocks.xmpp.addr.Jid; public class MagicCreateActivity extends XmppActivity implements TextWatcher { private TextView mFullJidDisplay; private EditText mUsername; - private SecureRandom mRandom; - - private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456780+-/#$!?"; - private static final int PW_LENGTH = 10; @Override protected void refreshUiReal() { @@ -57,7 +54,6 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher { configureActionBar(getSupportActionBar()); mFullJidDisplay = findViewById(R.id.full_jid); mUsername = findViewById(R.id.username); - mRandom = new SecureRandom(); Button next = findViewById(R.id.create_account); next.setOnClickListener(v -> { try { @@ -70,7 +66,7 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher { mUsername.setError(null); Account account = xmppConnectionService.findAccountByJid(jid); if (account == null) { - account = new Account(jid, createPassword()); + account = new Account(jid, CryptoHelper.createPassword(new SecureRandom())); account.setOption(Account.OPTION_REGISTER, true); account.setOption(Account.OPTION_DISABLED, true); account.setOption(Account.OPTION_MAGIC_CREATE, true); @@ -92,14 +88,6 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher { mUsername.addTextChangedListener(this); } - private String createPassword() { - StringBuilder builder = new StringBuilder(PW_LENGTH); - for (int i = 0; i < PW_LENGTH; ++i) { - builder.append(CHARS.charAt(mRandom.nextInt(CHARS.length() - 1))); - } - return builder.toString(); - } - @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index fa03fd142..cddea6440 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -36,261 +36,269 @@ import rocks.xmpp.addr.Jid; public final class CryptoHelper { - private static final char[] VOWELS = "aeiou".toCharArray(); - private static final char[] CONSONANTS = "bcfghjklmnpqrstvwxyz".toCharArray(); + public static final Pattern UUID_PATTERN = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"); + final public static byte[] ONE = new byte[]{0, 0, 0, 1}; + private static final char[] CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz123456789+-/#$!?".toCharArray(); + private static final int PW_LENGTH = 10; + private static final char[] VOWELS = "aeiou".toCharArray(); + private static final char[] CONSONANTS = "bcfghjklmnpqrstvwxyz".toCharArray(); + private final static char[] hexArray = "0123456789abcdef".toCharArray(); - private final static char[] hexArray = "0123456789abcdef".toCharArray(); + 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 final Pattern UUID_PATTERN = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"); - final public static byte[] ONE = new byte[] { 0, 0, 0, 1 }; + public static String createPassword(SecureRandom random) { + StringBuilder builder = new StringBuilder(PW_LENGTH); + for (int i = 0; i < PW_LENGTH; ++i) { + builder.append(CHARS[random.nextInt(CHARS.length - 1)]); + } + return builder.toString(); + } - 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 String pronounceable(SecureRandom random) { + char[] output = new char[random.nextInt(4) * 2 + 5]; + boolean vowel = random.nextBoolean(); + for (int i = 0; i < output.length; ++i) { + output[i] = vowel ? VOWELS[random.nextInt(VOWELS.length)] : CONSONANTS[random.nextInt(CONSONANTS.length)]; + vowel = !vowel; + } + return String.valueOf(output); + } - public static String pronounceable(SecureRandom random) { - char[] output = new char[random.nextInt(4) * 2 + 5]; - boolean vowel = random.nextBoolean(); - for(int i = 0; i < output.length; ++i) { - output[i] = vowel ? VOWELS[random.nextInt(VOWELS.length)] : CONSONANTS[random.nextInt(CONSONANTS.length)]; - vowel = !vowel; - } - return String.valueOf(output); - } + 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 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 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 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; - } + /** + * 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(); + } - /** - * 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 saslPrep(final String s) { - return Normalizer.normalize(s, Normalizer.Form.NFKC); - } + public static String random(int length, SecureRandom random) { + final byte[] bytes = new byte[length]; + random.nextBytes(bytes); + return Base64.encodeToString(bytes, Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE); + } - public static String random(int length, SecureRandom random) { - final byte[] bytes = new byte[length]; - random.nextBytes(bytes); - return Base64.encodeToString(bytes,Base64.NO_PADDING|Base64.NO_WRAP|Base64.URL_SAFE); - } + public static String prettifyFingerprint(String fingerprint) { + if (fingerprint == null) { + return ""; + } else if (fingerprint.length() < 40) { + return fingerprint; + } + StringBuilder builder = new StringBuilder(fingerprint); + for (int i = 8; i < builder.length(); i += 9) { + builder.insert(i, ' '); + } + return builder.toString(); + } - public static String prettifyFingerprint(String fingerprint) { - if (fingerprint==null) { - return ""; - } else if (fingerprint.length() < 40) { - return fingerprint; - } - StringBuilder builder = new StringBuilder(fingerprint); - for(int i=8;i cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS)); + final List platformCiphers = Arrays.asList(platformSupportedCipherSuites); + cipherSuites.retainAll(platformCiphers); + cipherSuites.addAll(platformCiphers); + filterWeakCipherSuites(cipherSuites); + cipherSuites.remove("TLS_FALLBACK_SCSV"); + return cipherSuites.toArray(new String[cipherSuites.size()]); + } - public static String[] getOrderedCipherSuites(final String[] platformSupportedCipherSuites) { - final Collection cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS)); - final List platformCiphers = Arrays.asList(platformSupportedCipherSuites); - cipherSuites.retainAll(platformCiphers); - cipherSuites.addAll(platformCiphers); - filterWeakCipherSuites(cipherSuites); - cipherSuites.remove("TLS_FALLBACK_SCSV"); - return cipherSuites.toArray(new String[cipherSuites.size()]); - } + private static void filterWeakCipherSuites(final Collection cipherSuites) { + final Iterator 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; + } + } + } + } - private static void filterWeakCipherSuites(final Collection cipherSuites) { - final Iterator 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 extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, IllegalArgumentException, CertificateParsingException { + Collection> alternativeNames = certificate.getSubjectAlternativeNames(); + List emails = new ArrayList<>(); + if (alternativeNames != null) { + for (List san : alternativeNames) { + Integer type = (Integer) san.get(0); + if (type == 1) { + emails.add((String) san.get(1)); + } + } + } + X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject(); + if (emails.size() == 0 && x500name.getRDNs(BCStyle.EmailAddress).length > 0) { + emails.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue())); + } + String name = x500name.getRDNs(BCStyle.CN).length > 0 ? IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue()) : null; + if (emails.size() >= 1) { + return new Pair<>(Jid.of(emails.get(0)), name); + } else if (name != null) { + try { + Jid jid = Jid.of(name); + if (jid.isBareJid() && jid.getLocal() != null) { + return new Pair<>(jid, null); + } + } catch (IllegalArgumentException e) { + return null; + } + } + return null; + } - public static Pair extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, IllegalArgumentException, CertificateParsingException { - Collection> alternativeNames = certificate.getSubjectAlternativeNames(); - List emails = new ArrayList<>(); - if (alternativeNames != null) { - for(List san : alternativeNames) { - Integer type = (Integer) san.get(0); - if (type == 1) { - emails.add((String) san.get(1)); - } - } - } - X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject(); - if (emails.size() == 0 && x500name.getRDNs(BCStyle.EmailAddress).length > 0) { - emails.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue())); - } - String name = x500name.getRDNs(BCStyle.CN).length > 0 ? IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue()) : null; - if (emails.size() >= 1) { - return new Pair<>(Jid.of(emails.get(0)), name); - } else if (name != null){ - try { - Jid jid = Jid.of(name); - if (jid.isBareJid() && jid.getLocal() != null) { - return new Pair<>(jid,null); - } - } catch (IllegalArgumentException e) { - return null; - } - } - return null; - } + public static Bundle extractCertificateInformation(X509Certificate certificate) { + Bundle information = new Bundle(); + try { + JcaX509CertificateHolder holder = new JcaX509CertificateHolder(certificate); + X500Name subject = holder.getSubject(); + try { + information.putString("subject_cn", subject.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } + try { + information.putString("subject_o", subject.getRDNs(BCStyle.O)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } - public static Bundle extractCertificateInformation(X509Certificate certificate) { - Bundle information = new Bundle(); - try { - JcaX509CertificateHolder holder = new JcaX509CertificateHolder(certificate); - X500Name subject = holder.getSubject(); - try { - information.putString("subject_cn", subject.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString()); - } catch (Exception e) { - //ignored - } - try { - information.putString("subject_o",subject.getRDNs(BCStyle.O)[0].getFirst().getValue().toString()); - } catch (Exception e) { - //ignored - } + X500Name issuer = holder.getIssuer(); + try { + information.putString("issuer_cn", issuer.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } + try { + information.putString("issuer_o", issuer.getRDNs(BCStyle.O)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } + try { + information.putString("sha1", getFingerprintCert(certificate.getEncoded())); + } catch (Exception e) { - X500Name issuer = holder.getIssuer(); - try { - information.putString("issuer_cn", issuer.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString()); - } catch (Exception e) { - //ignored - } - try { - information.putString("issuer_o", issuer.getRDNs(BCStyle.O)[0].getFirst().getValue().toString()); - } catch (Exception e) { - //ignored - } - try { - information.putString("sha1", getFingerprintCert(certificate.getEncoded())); - } catch (Exception e) { + } + return information; + } catch (CertificateEncodingException e) { + return information; + } + } - } - return information; - } catch (CertificateEncodingException e) { - return information; - } - } + public static String getFingerprintCert(byte[] input) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] fingerprint = md.digest(input); + return prettifyFingerprintCert(bytesToHex(fingerprint)); + } - public static String getFingerprintCert(byte[] input) throws NoSuchAlgorithmException { - MessageDigest md = MessageDigest.getInstance("SHA-1"); - byte[] fingerprint = md.digest(input); - return prettifyFingerprintCert(bytesToHex(fingerprint)); - } + public static String getAccountFingerprint(Account account, String androidId) { + return getFingerprint(account.getJid().asBareJid().toEscapedString() + "\00" + androidId); + } - public static String getAccountFingerprint(Account account, String androidId) { - return getFingerprint(account.getJid().asBareJid().toEscapedString()+"\00"+androidId); - } + public static String getFingerprint(String value) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + return bytesToHex(md.digest(value.getBytes("UTF-8"))); + } catch (Exception e) { + return ""; + } + } - public static String getFingerprint(String value) { - try { - MessageDigest md = MessageDigest.getInstance("SHA-1"); - return bytesToHex(md.digest(value.getBytes("UTF-8"))); - } catch (Exception e) { - return ""; - } - } + public static int encryptionTypeToText(int encryption) { + switch (encryption) { + case Message.ENCRYPTION_OTR: + return R.string.encryption_choice_otr; + case Message.ENCRYPTION_AXOLOTL: + case Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE: + return R.string.encryption_choice_omemo; + case Message.ENCRYPTION_NONE: + return R.string.encryption_choice_unencrypted; + default: + return R.string.encryption_choice_pgp; + } + } - public static int encryptionTypeToText(int encryption) { - switch (encryption) { - case Message.ENCRYPTION_OTR: - return R.string.encryption_choice_otr; - case Message.ENCRYPTION_AXOLOTL: - case Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE: - return R.string.encryption_choice_omemo; - case Message.ENCRYPTION_NONE: - return R.string.encryption_choice_unencrypted; - default: - return R.string.encryption_choice_pgp; - } - } + public static URL toAesGcmUrl(URL url) { + if (!url.getProtocol().equalsIgnoreCase("https")) { + return url; + } + try { + return new URL(AesGcmURLStreamHandler.PROTOCOL_NAME + url.toString().substring(url.getProtocol().length())); + } catch (MalformedURLException e) { + return url; + } + } - public static URL toAesGcmUrl(URL url) { - if (!url.getProtocol().equalsIgnoreCase("https")) { - return url; - } - try { - return new URL(AesGcmURLStreamHandler.PROTOCOL_NAME+url.toString().substring(url.getProtocol().length())); - } catch (MalformedURLException e) { - return url; - } - } + public static URL toHttpsUrl(URL url) { + if (!url.getProtocol().equalsIgnoreCase(AesGcmURLStreamHandler.PROTOCOL_NAME)) { + return url; + } + try { + return new URL("https" + url.toString().substring(url.getProtocol().length())); + } catch (MalformedURLException e) { + return url; + } + } - public static URL toHttpsUrl(URL url) { - if (!url.getProtocol().equalsIgnoreCase(AesGcmURLStreamHandler.PROTOCOL_NAME)) { - return url; - } - try { - return new URL("https"+url.toString().substring(url.getProtocol().length())); - } catch (MalformedURLException e) { - return url; - } - } - - public static boolean isPgpEncryptedUrl(String url) { - if (url == null) { - return false; - } - final String u = url.toLowerCase(); - return !u.contains(" ") && (u.startsWith("https://") || u.startsWith("http://") || u.startsWith("p1s3://")) && u.endsWith(".pgp"); - } + public static boolean isPgpEncryptedUrl(String url) { + if (url == null) { + return false; + } + final String u = url.toLowerCase(); + return !u.contains(" ") && (u.startsWith("https://") || u.startsWith("http://") || u.startsWith("p1s3://")) && u.endsWith(".pgp"); + } }