From 8122978959e85709168126c3b1eed978475e603b Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 26 Jan 2022 10:37:13 +0100 Subject: [PATCH 1/7] remove unused method Utils.getLocaleFromAndroidLangTag() --- .../main/java/org/fdroid/fdroid/Utils.java | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index 56af645b6..3f0353345 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -453,36 +453,6 @@ public final class Utils { return sigHash; } - /** - * There is a method {@link java.util.Locale#forLanguageTag(String)} which would be useful - * for this, however it doesn't deal with android-specific language tags, which are a little - * different. For example, android language tags may have an "r" before the country code, - * such as "zh-rHK", however {@link java.util.Locale} expects them to be "zr-HK". - */ - public static Locale getLocaleFromAndroidLangTag(String languageTag) { - if (TextUtils.isEmpty(languageTag)) { - return null; - } - - final String[] parts = languageTag.split("-"); - if (parts.length == 1) { - return new Locale(parts[0]); - } - if (parts.length == 2) { - String country = parts[1]; - // Some languages have an "r" before the country as per the values folders, such - // as "zh-rCN". As far as the Locale class is concerned, the "r" is - // not helpful, and this should be "zh-CN". Thus, we will - // strip the "r" when found. - if (country.charAt(0) == 'r' && country.length() == 3) { - country = country.substring(1); - } - return new Locale(parts[0], country); - } - Log.e(TAG, "Locale could not be parsed from language tag: " + languageTag); - return new Locale(languageTag); - } - /** * Gets the {@link RequestOptions} instance used to configure * {@link Glide} instances used to display app icons that should always be From 172751ea9838f5e368d862342dd24fb8b2eb66fc Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 26 Jan 2022 10:45:34 +0100 Subject: [PATCH 2/7] rename Utils.getBinaryHash() to Utils.getFileHexDigest() Since this method works on any file, not just APKs or binaries... --- app/src/main/java/org/fdroid/fdroid/Utils.java | 14 +++++++------- app/src/main/java/org/fdroid/fdroid/data/App.java | 6 +++--- .../fdroid/data/InstalledAppProviderService.java | 2 +- app/src/test/java/org/fdroid/fdroid/UtilsTest.java | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index 3f0353345..6f66583e8 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -500,8 +500,8 @@ public final class Utils { } /** - * Get the checksum hash of the file {@code apk} using the algorithm in {@code algo}. - * {@code apk} must exist on the filesystem and {@code algo} must be supported + * Get the checksum hash of the file {@code file} using the algorithm in {@code hashAlgo}. + * {@code file} must exist on the filesystem and {@code hashAlgo} must be supported * by this device, otherwise an {@link IllegalArgumentException} is thrown. This * method must be very defensive about checking whether the file exists, since APKs * can be uninstalled/deleted in background at any time, even if this is in the @@ -514,11 +514,11 @@ public final class Utils { * for more detail. */ @Nullable - public static String getBinaryHash(File apk, String algo) { + public static String getFileHexDigest(File file, String hashAlgo) { FileInputStream fis = null; try { - MessageDigest md = MessageDigest.getInstance(algo); - fis = new FileInputStream(apk); + MessageDigest md = MessageDigest.getInstance(hashAlgo); + fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); byte[] dataBytes = new byte[8192]; @@ -532,9 +532,9 @@ public final class Utils { } catch (IOException e) { String message = e.getMessage(); if (message.contains("read failed: EIO (I/O error)")) { - Utils.debugLog(TAG, "potential filesystem corruption while accessing " + apk + ": " + message); + Utils.debugLog(TAG, "potential filesystem corruption while accessing " + file + ": " + message); } else if (message.contains(" ENOENT ")) { - Utils.debugLog(TAG, apk + " vanished: " + message); + Utils.debugLog(TAG, file + " vanished: " + message); } } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(e); diff --git a/app/src/main/java/org/fdroid/fdroid/data/App.java b/app/src/main/java/org/fdroid/fdroid/data/App.java index bb975679a..3c35debf3 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -431,7 +431,7 @@ public class App extends ValueObject implements Comparable, Parcelable { app.installedApk.hash = installedApp.getHash(); } else if (apkFile.canRead()) { String hashType = "sha256"; - String hash = Utils.getBinaryHash(apkFile, hashType); + String hash = Utils.getFileHexDigest(apkFile, hashType); if (TextUtils.isEmpty(hash)) { return null; } @@ -876,10 +876,10 @@ public class App extends ValueObject implements Comparable, Parcelable { if (Integer.parseInt(segments[1]) <= apk.versionCode) { if ("main".equals(segments[0])) { apk.obbMainFile = filename; - apk.obbMainFileSha256 = Utils.getBinaryHash(f, apk.hashType); + apk.obbMainFileSha256 = Utils.getFileHexDigest(f, apk.hashType); } else if ("patch".equals(segments[0])) { apk.obbPatchFile = filename; - apk.obbPatchFileSha256 = Utils.getBinaryHash(f, apk.hashType); + apk.obbPatchFileSha256 = Utils.getFileHexDigest(f, apk.hashType); } } } diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java index a285d680f..0f3024bf5 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java @@ -299,7 +299,7 @@ public class InstalledAppProviderService extends JobIntentService { if (apk.exists() && apk.canRead()) { try { String hashType = "sha256"; - String hash = Utils.getBinaryHash(apk, hashType); + String hash = Utils.getFileHexDigest(apk, hashType); insertAppIntoDb(this, packageInfo, hashType, hash); } catch (IllegalArgumentException e) { Utils.debugLog(TAG, e.getMessage()); diff --git a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java index 044157daa..fe202a874 100644 --- a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java +++ b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java @@ -204,19 +204,19 @@ public class UtilsTest { } @Test - public void testGetBinaryHash() { + public void testGetFileHexDigest() { File f = TestUtils.copyResourceToTempFile("largeRepo.xml"); assertEquals("df1754aa4b56c86c06d7842dfd02064f0781c1f740f489d3fc158bb541c8d197", - Utils.getBinaryHash(f, "sha256")); + Utils.getFileHexDigest(f, "sha256")); f = TestUtils.copyResourceToTempFile("masterKeyIndex.jar"); assertEquals("625d5aedcd0499fe04ebab81f3c7ae30c236cee653a914ffb587d890198f3aba", - Utils.getBinaryHash(f, "sha256")); + Utils.getFileHexDigest(f, "sha256")); f = TestUtils.copyResourceToTempFile("index.fdroid.2016-10-30.jar"); assertEquals("c138b503c6475aa749585d0e3ad4dba3546b6d33ec485efd8ac8bd603d93fedb", - Utils.getBinaryHash(f, "sha256")); + Utils.getFileHexDigest(f, "sha-256")); f = TestUtils.copyResourceToTempFile("index.fdroid.2016-11-10.jar"); assertEquals("93bea45814fd8955cabb957e7a3f8790d6c568eaa16fa30425c2d26c60490bde", - Utils.getBinaryHash(f, "sha256")); + Utils.getFileHexDigest(f, "SHA-256")); } // TODO write tests that work with a Certificate From 88b22c95bcfc29f8d5d991898a8cf1be6286125e Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 17 Jan 2022 09:51:22 +0100 Subject: [PATCH 3/7] purge enableBouncyCastleOnLollipop, minSdkVersion is higher --- .../java/org/fdroid/fdroid/FDroidApp.java | 12 ----- .../java/org/fdroid/fdroid/IndexUpdater.java | 6 --- .../main/java/org/fdroid/fdroid/data/App.java | 47 +++++++------------ 3 files changed, 18 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java index ff8b36ca2..4c568b875 100644 --- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java +++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java @@ -226,18 +226,6 @@ public class FDroidApp extends Application implements androidx.work.Configuratio Security.addProvider(BOUNCYCASTLE_PROVIDER); } - public static void enableBouncyCastleOnLollipop() { - if (Build.VERSION.SDK_INT == 21) { - Security.addProvider(BOUNCYCASTLE_PROVIDER); - } - } - - public static void disableBouncyCastleOnLollipop() { - if (Build.VERSION.SDK_INT == 21) { - Security.removeProvider(BOUNCYCASTLE_PROVIDER.getName()); - } - } - /** * Initialize the settings needed to run a local swap repo. This should * only ever be called in {@link WifiStateChangeService.WifiInfoThread}, diff --git a/app/src/main/java/org/fdroid/fdroid/IndexUpdater.java b/app/src/main/java/org/fdroid/fdroid/IndexUpdater.java index 9ce0d6278..76204be54 100644 --- a/app/src/main/java/org/fdroid/fdroid/IndexUpdater.java +++ b/app/src/main/java/org/fdroid/fdroid/IndexUpdater.java @@ -207,11 +207,6 @@ public class IndexUpdater { throw new UpdateException(repo, downloadedFile + " does not exist!"); } - // Due to a bug in Android 5.0 Lollipop, the inclusion of bouncycastle causes - // breakage when verifying the signature of the downloaded .jar. For more - // details, check out https://gitlab.com/fdroid/fdroidclient/issues/111. - FDroidApp.disableBouncyCastleOnLollipop(); - JarFile jarFile = new JarFile(downloadedFile, true); JarEntry indexEntry = (JarEntry) jarFile.getEntry(IndexUpdater.DATA_FILE_NAME); indexInputStream = new ProgressBufferedInputStream(jarFile.getInputStream(indexEntry), @@ -241,7 +236,6 @@ public class IndexUpdater { } catch (SAXException | ParserConfigurationException | IOException e) { throw new UpdateException(repo, "Error parsing index", e); } finally { - FDroidApp.enableBouncyCastleOnLollipop(); Utils.closeQuietly(indexInputStream); if (downloadedFile != null) { if (!downloadedFile.delete()) { diff --git a/app/src/main/java/org/fdroid/fdroid/data/App.java b/app/src/main/java/org/fdroid/fdroid/data/App.java index 3c35debf3..10338a4d6 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -24,7 +24,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.io.filefilter.RegexFileFilter; -import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; @@ -934,35 +933,25 @@ public class App extends ValueObject implements Comparable, Parcelable { throw new CertificateEncodingException("null signed entry!"); } - byte[] rawCertBytes; - - // Due to a bug in android 5.0 lollipop, the inclusion of BouncyCastle causes - // breakage when verifying the signature of most .jars. For more - // details, check out https://gitlab.com/fdroid/fdroidclient/issues/111. - try { - FDroidApp.disableBouncyCastleOnLollipop(); - final InputStream tmpIn = apkJar.getInputStream(aSignedEntry); - byte[] buff = new byte[2048]; - //noinspection StatementWithEmptyBody - while (tmpIn.read(buff, 0, buff.length) != -1) { - /* - * NOP - apparently have to READ from the JarEntry before you can - * call getCerficates() and have it return != null. Yay Java. - */ - } - tmpIn.close(); - - if (aSignedEntry.getCertificates() == null - || aSignedEntry.getCertificates().length == 0) { - apkJar.close(); - throw new CertificateEncodingException("No Certificates found!"); - } - - final Certificate signer = aSignedEntry.getCertificates()[0]; - rawCertBytes = signer.getEncoded(); - } finally { - FDroidApp.enableBouncyCastleOnLollipop(); + final InputStream tmpIn = apkJar.getInputStream(aSignedEntry); + byte[] buff = new byte[2048]; + //noinspection StatementWithEmptyBody + while (tmpIn.read(buff, 0, buff.length) != -1) { + /* + * NOP - apparently have to READ from the JarEntry before you can + * call getCerficates() and have it return != null. Yay Java. + */ } + tmpIn.close(); + + if (aSignedEntry.getCertificates() == null + || aSignedEntry.getCertificates().length == 0) { + apkJar.close(); + throw new CertificateEncodingException("No Certificates found!"); + } + + final Certificate signer = aSignedEntry.getCertificates()[0]; + byte[] rawCertBytes = signer.getEncoded(); apkJar.close(); apk.sig = Utils.getsig(rawCertBytes); From 8a573a7b59d1268c1406681493460081916a04dd Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 26 Jan 2022 11:31:23 +0100 Subject: [PATCH 4/7] vendor commons-codec to implement getFileHexDigest and more Android includes a crippled version of commons-codec 1.3, which it will silently use instead of any version included in an app. So we are forced to vendor it. This comes from the tag "commons-codec-1.16-rc1": https://github.com/apache/commons-codec/tree/commons-codec-1.16-rc1/src/main/java/org/apache/commons/codec For more on this: * https://github.com/ExCiteS/apache-commons-codec-shaded * https://gitlab.com/fdroid/fdroidclient/-/merge_requests/1089#note_822501322 --- app/build.gradle | 2 + .../main/java/org/fdroid/fdroid/Utils.java | 23 +- .../apache/commons/codec/BinaryDecoder.java | 37 + .../apache/commons/codec/BinaryEncoder.java | 37 + .../apache/commons/codec/CharEncoding.java | 119 ++ .../org/apache/commons/codec/Decoder.java | 46 + .../commons/codec/DecoderException.java | 84 + .../org/apache/commons/codec/Encoder.java | 43 + .../commons/codec/EncoderException.java | 87 + .../codec/binary/CharSequenceUtils.java | 82 + .../org/apache/commons/codec/binary/Hex.java | 567 ++++++ .../commons/codec/binary/StringUtils.java | 419 ++++ .../commons/codec/digest/DigestUtils.java | 1743 +++++++++++++++++ .../codec/digest/MessageDigestAlgorithms.java | 174 ++ .../java/org/fdroid/fdroid/UtilsTest.java | 28 +- config/checkstyle/checkstyle.xml | 2 +- config/checkstyle/suppressions.xml | 1 + 17 files changed, 3473 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/BinaryDecoder.java create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/BinaryEncoder.java create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/CharEncoding.java create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/Decoder.java create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/DecoderException.java create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/Encoder.java create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/EncoderException.java create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/binary/CharSequenceUtils.java create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/binary/Hex.java create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/binary/StringUtils.java create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/digest/DigestUtils.java create mode 100644 app/src/main/java/vendored/org/apache/commons/codec/digest/MessageDigestAlgorithms.java diff --git a/app/build.gradle b/app/build.gradle index 96298ac29..e4725a811 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -225,6 +225,7 @@ task pmdMain(type: Pmd) { ruleSets = [] // otherwise defaults clash with the list in rules.xml source 'src/main/java' include '**/*.java' + exclude '**/vendored/**/*.java' } task pmdTest(type: Pmd) { @@ -233,6 +234,7 @@ task pmdTest(type: Pmd) { ruleSets = [] // otherwise defaults clash with the list in rules.xml source 'src/test/java', 'src/androidTest/java' include '**/*.java' + exclude '**/vendored/**/*.java' } task pmd(dependsOn: [pmdMain, pmdTest]) {} diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index 6f66583e8..4fc22574e 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -62,7 +62,6 @@ import org.fdroid.fdroid.data.SanitizedFile; import org.fdroid.fdroid.data.Schema; import org.xml.sax.XMLReader; -import java.io.BufferedInputStream; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; @@ -104,6 +103,8 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.schedulers.Schedulers; +import vendored.org.apache.commons.codec.binary.Hex; +import vendored.org.apache.commons.codec.digest.DigestUtils; public final class Utils { @@ -512,23 +513,13 @@ public final class Utils { * exception-message-parsing-and-throwing-a-new-ignorable-exception-hackery is * probably warranted. See https://www.gitlab.com/fdroid/fdroidclient/issues/855 * for more detail. + * + * @see forced to vendor Apache Commons Codec */ @Nullable public static String getFileHexDigest(File file, String hashAlgo) { - FileInputStream fis = null; try { - MessageDigest md = MessageDigest.getInstance(hashAlgo); - fis = new FileInputStream(file); - BufferedInputStream bis = new BufferedInputStream(fis); - - byte[] dataBytes = new byte[8192]; - int nread; - while ((nread = bis.read(dataBytes)) != -1) { // NOPMD Avoid assignments in operands - md.update(dataBytes, 0, nread); - } - - byte[] mdbytes = md.digest(); - return toHexString(mdbytes); + return Hex.encodeHexString(DigestUtils.digest(DigestUtils.getDigest(hashAlgo), file)); } catch (IOException e) { String message = e.getMessage(); if (message.contains("read failed: EIO (I/O error)")) { @@ -536,10 +527,6 @@ public final class Utils { } else if (message.contains(" ENOENT ")) { Utils.debugLog(TAG, file + " vanished: " + message); } - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException(e); - } finally { - closeQuietly(fis); } return null; } diff --git a/app/src/main/java/vendored/org/apache/commons/codec/BinaryDecoder.java b/app/src/main/java/vendored/org/apache/commons/codec/BinaryDecoder.java new file mode 100644 index 000000000..85239aa20 --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/BinaryDecoder.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package vendored.org.apache.commons.codec; + +/** + * Defines common decoding methods for byte array decoders. + * + */ +public interface BinaryDecoder extends Decoder { + + /** + * Decodes a byte array and returns the results as a byte array. + * + * @param source + * A byte array which has been encoded with the appropriate encoder + * @return a byte array that contains decoded content + * @throws DecoderException + * A decoder exception is thrown if a Decoder encounters a failure condition during the decode process. + */ + byte[] decode(byte[] source) throws DecoderException; +} + diff --git a/app/src/main/java/vendored/org/apache/commons/codec/BinaryEncoder.java b/app/src/main/java/vendored/org/apache/commons/codec/BinaryEncoder.java new file mode 100644 index 000000000..9bb8dc02d --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/BinaryEncoder.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package vendored.org.apache.commons.codec; + +/** + * Defines common encoding methods for byte array encoders. + * + */ +public interface BinaryEncoder extends Encoder { + + /** + * Encodes a byte array and return the encoded data as a byte array. + * + * @param source + * Data to be encoded + * @return A byte array containing the encoded data + * @throws EncoderException + * thrown if the Encoder encounters a failure condition during the encoding process. + */ + byte[] encode(byte[] source) throws EncoderException; +} + diff --git a/app/src/main/java/vendored/org/apache/commons/codec/CharEncoding.java b/app/src/main/java/vendored/org/apache/commons/codec/CharEncoding.java new file mode 100644 index 000000000..9e5ac8b47 --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/CharEncoding.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package vendored.org.apache.commons.codec; + +/** + * Character encoding names required of every implementation of the Java platform. + * + * From the Java documentation Standard charsets: + *

+ * Every implementation of the Java platform is required to support the following character encodings. Consult the + * release documentation for your implementation to see if any other encodings are supported. Consult the release + * documentation for your implementation to see if any other encodings are supported. + *

+ * + *
    + *
  • {@code US-ASCII}

    + * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set.

  • + *
  • {@code ISO-8859-1}

    + * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.

  • + *
  • {@code UTF-8}

    + * Eight-bit Unicode Transformation Format.

  • + *
  • {@code UTF-16BE}

    + * Sixteen-bit Unicode Transformation Format, big-endian byte order.

  • + *
  • {@code UTF-16LE}

    + * Sixteen-bit Unicode Transformation Format, little-endian byte order.

  • + *
  • {@code UTF-16}

    + * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either order + * accepted on input, big-endian used on output.)

  • + *
+ * + * This perhaps would best belong in the [lang] project. Even if a similar interface is defined in [lang], it is not + * foreseen that [codec] would be made to depend on [lang]. + * + *

+ * This class is immutable and thread-safe. + *

+ * + * @see Standard charsets + * @since 1.4 + */ +public class CharEncoding { + + /** + * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1. + *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String ISO_8859_1 = "ISO-8859-1"; + + /** + * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. + *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String US_ASCII = "US-ASCII"; + + /** + * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial byte-order mark + * (either order accepted on input, big-endian used on output) + *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_16 = "UTF-16"; + + /** + * Sixteen-bit Unicode Transformation Format, big-endian byte order. + *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_16BE = "UTF-16BE"; + + /** + * Sixteen-bit Unicode Transformation Format, little-endian byte order. + *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_16LE = "UTF-16LE"; + + /** + * Eight-bit Unicode Transformation Format. + *

+ * Every implementation of the Java platform is required to support this character encoding. + *

+ * + * @see Standard charsets + */ + public static final String UTF_8 = "UTF-8"; +} diff --git a/app/src/main/java/vendored/org/apache/commons/codec/Decoder.java b/app/src/main/java/vendored/org/apache/commons/codec/Decoder.java new file mode 100644 index 000000000..79578ee7a --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/Decoder.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package vendored.org.apache.commons.codec; + +/** + * Provides the highest level of abstraction for Decoders. + *

+ * This is the sister interface of {@link Encoder}. All Decoders implement this common generic interface. + * Allows a user to pass a generic Object to any Decoder implementation in the codec package. + *

+ * One of the two interfaces at the center of the codec package. + * + */ +public interface Decoder { + + /** + * Decodes an "encoded" Object and returns a "decoded" Object. Note that the implementation of this interface will + * try to cast the Object parameter to the specific type expected by a particular Decoder implementation. If a + * {@link ClassCastException} occurs this decode method will throw a DecoderException. + * + * @param source + * the object to decode + * @return a 'decoded" object + * @throws DecoderException + * a decoder exception can be thrown for any number of reasons. Some good candidates are that the + * parameter passed to this method is null, a param cannot be cast to the appropriate type for a + * specific encoder. + */ + Object decode(Object source) throws DecoderException; +} + diff --git a/app/src/main/java/vendored/org/apache/commons/codec/DecoderException.java b/app/src/main/java/vendored/org/apache/commons/codec/DecoderException.java new file mode 100644 index 000000000..ee9351927 --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/DecoderException.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package vendored.org.apache.commons.codec; + +/** + * Thrown when there is a failure condition during the decoding process. This exception is thrown when a {@link Decoder} + * encounters a decoding specific exception such as invalid data, or characters outside of the expected range. + * + */ +public class DecoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with {@code null} as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public DecoderException() { + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + */ + public DecoderException(final String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and cause. + *

+ * Note that the detail message associated with {@code cause} is not automatically incorporated into this + * exception's detail message. + * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A {@code null} + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of {@code cause}). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A {@code null} + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException(final Throwable cause) { + super(cause); + } +} diff --git a/app/src/main/java/vendored/org/apache/commons/codec/Encoder.java b/app/src/main/java/vendored/org/apache/commons/codec/Encoder.java new file mode 100644 index 000000000..b8b15a153 --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/Encoder.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package vendored.org.apache.commons.codec; + +/** + * Provides the highest level of abstraction for Encoders. + *

+ * This is the sister interface of {@link Decoder}. Every implementation of Encoder provides this + * common generic interface which allows a user to pass a generic Object to any Encoder implementation + * in the codec package. + * + */ +public interface Encoder { + + /** + * Encodes an "Object" and returns the encoded content as an Object. The Objects here may just be + * {@code byte[]} or {@code String}s depending on the implementation used. + * + * @param source + * An object to encode + * @return An "encoded" Object + * @throws EncoderException + * An encoder exception is thrown if the encoder experiences a failure condition during the encoding + * process. + */ + Object encode(Object source) throws EncoderException; +} + diff --git a/app/src/main/java/vendored/org/apache/commons/codec/EncoderException.java b/app/src/main/java/vendored/org/apache/commons/codec/EncoderException.java new file mode 100644 index 000000000..b50c4cfb8 --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/EncoderException.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package vendored.org.apache.commons.codec; + +/** + * Thrown when there is a failure condition during the encoding process. This exception is thrown when an + * {@link Encoder} encounters a encoding specific exception such as invalid data, inability to calculate a checksum, + * characters outside of the expected range. + * + */ +public class EncoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with {@code null} as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public EncoderException() { + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * a useful message relating to the encoder specific error. + */ + public EncoderException(final String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and cause. + * + *

+ * Note that the detail message associated with {@code cause} is not automatically incorporated into this + * exception's detail message. + *

+ * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A {@code null} + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of {@code cause}). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A {@code null} + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException(final Throwable cause) { + super(cause); + } +} diff --git a/app/src/main/java/vendored/org/apache/commons/codec/binary/CharSequenceUtils.java b/app/src/main/java/vendored/org/apache/commons/codec/binary/CharSequenceUtils.java new file mode 100644 index 000000000..f9e3fbb28 --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/binary/CharSequenceUtils.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package vendored.org.apache.commons.codec.binary; + +/** + *

+ * Operations on {@link CharSequence} that are {@code null} safe. + *

+ *

+ * Copied from Apache Commons Lang r1586295 on April 10, 2014 (day of 3.3.2 release). + *

+ * + * @see CharSequence + * @since 1.10 + */ +public class CharSequenceUtils { + + /** + * Green implementation of regionMatches. + * + *

Note: This function differs from the current implementation in Apache Commons Lang + * where the input indices are not valid. It is only used within this package. + * + * @param cs + * the {@code CharSequence} to be processed + * @param ignoreCase + * whether or not to be case insensitive + * @param thisStart + * the index to start on the {@code cs} CharSequence + * @param substring + * the {@code CharSequence} to be looked for + * @param start + * the index to start on the {@code substring} CharSequence + * @param length + * character length of the region + * @return whether the region matched + */ + static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart, + final CharSequence substring, final int start, final int length) { + if (cs instanceof String && substring instanceof String) { + return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length); + } + int index1 = thisStart; + int index2 = start; + int tmpLen = length; + + while (tmpLen-- > 0) { + final char c1 = cs.charAt(index1++); + final char c2 = substring.charAt(index2++); + + if (c1 == c2) { + continue; + } + + if (!ignoreCase) { + return false; + } + + // The same check as in String.regionMatches(): + if (Character.toUpperCase(c1) != Character.toUpperCase(c2) && + Character.toLowerCase(c1) != Character.toLowerCase(c2)) { + return false; + } + } + + return true; + } +} diff --git a/app/src/main/java/vendored/org/apache/commons/codec/binary/Hex.java b/app/src/main/java/vendored/org/apache/commons/codec/binary/Hex.java new file mode 100644 index 000000000..83d3660a1 --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/binary/Hex.java @@ -0,0 +1,567 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package vendored.org.apache.commons.codec.binary; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +import vendored.org.apache.commons.codec.BinaryDecoder; +import vendored.org.apache.commons.codec.BinaryEncoder; +import vendored.org.apache.commons.codec.CharEncoding; +import vendored.org.apache.commons.codec.DecoderException; +import vendored.org.apache.commons.codec.EncoderException; + +/** + * Converts hexadecimal Strings. The Charset used for certain operation can be set, the default is set in + * {@link #DEFAULT_CHARSET_NAME} + * + * This class is thread-safe. + * + * @since 1.1 + */ +public class Hex implements BinaryEncoder, BinaryDecoder { + + /** + * Default charset is {@link StandardCharsets#UTF_8}. + * + * @since 1.7 + */ + public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + + /** + * Default charset name is {@link CharEncoding#UTF_8}. + * + * @since 1.4 + */ + public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8; + + /** + * Used to build output as hex. + */ + private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', + 'e', 'f' }; + + /** + * Used to build output as hex. + */ + private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F' }; + + /** + * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The + * returned array will be half the length of the passed array, as it takes two characters to represent any given + * byte. An exception is thrown if the passed char array has an odd number of elements. + * + * @param data An array of characters containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied char array. + * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied + */ + public static byte[] decodeHex(final char[] data) throws DecoderException { + final byte[] out = new byte[data.length >> 1]; + decodeHex(data, out, 0); + return out; + } + + /** + * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The + * returned array will be half the length of the passed array, as it takes two characters to represent any given + * byte. An exception is thrown if the passed char array has an odd number of elements. + * + * @param data An array of characters containing hexadecimal digits + * @param out A byte array to contain the binary data decoded from the supplied char array. + * @param outOffset The position within {@code out} to start writing the decoded bytes. + * @return the number of bytes written to {@code out}. + * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied + * @since 1.15 + */ + public static int decodeHex(final char[] data, final byte[] out, final int outOffset) throws DecoderException { + final int len = data.length; + + if ((len & 0x01) != 0) { + throw new DecoderException("Odd number of characters."); + } + + final int outLen = len >> 1; + if (out.length - outOffset < outLen) { + throw new DecoderException("Output array is not large enough to accommodate decoded data."); + } + + // two characters form the hex value. + for (int i = outOffset, j = 0; j < len; i++) { + int f = toDigit(data[j], j) << 4; + j++; + f = f | toDigit(data[j], j); + j++; + out[i] = (byte) (f & 0xFF); + } + + return outLen; + } + + /** + * Converts a String representing hexadecimal values into an array of bytes of those same values. The returned array + * will be half the length of the passed String, as it takes two characters to represent any given byte. An + * exception is thrown if the passed String has an odd number of elements. + * + * @param data A String containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied char array. + * @throws DecoderException Thrown if an odd number of characters or illegal characters are supplied + * @since 1.11 + */ + public static byte[] decodeHex(final String data) throws DecoderException { + return decodeHex(data.toCharArray()); + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * The returned array will be double the length of the passed array, as it takes two characters to represent any + * given byte. + * + * @param data a byte[] to convert to hex characters + * @return A char[] containing lower-case hexadecimal characters + */ + public static char[] encodeHex(final byte[] data) { + return encodeHex(data, true); + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * The returned array will be double the length of the passed array, as it takes two characters to represent any + * given byte. + * + * @param data a byte[] to convert to Hex characters + * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase + * @return A char[] containing hexadecimal characters in the selected case + * @since 1.4 + */ + public static char[] encodeHex(final byte[] data, final boolean toLowerCase) { + return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * The returned array will be double the length of the passed array, as it takes two characters to represent any + * given byte. + * + * @param data a byte[] to convert to hex characters + * @param toDigits the output alphabet (must contain at least 16 chars) + * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either + * upper- or lower-case hex. + * @since 1.4 + */ + protected static char[] encodeHex(final byte[] data, final char[] toDigits) { + final int dataLength = data.length; + final char[] out = new char[dataLength << 1]; + encodeHex(data, 0, dataLength, toDigits, out, 0); + return out; + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * + * @param data a byte[] to convert to hex characters + * @param dataOffset the position in {@code data} to start encoding from + * @param dataLen the number of bytes from {@code dataOffset} to encode + * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase + * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either + * upper- or lower-case hex. + * @since 1.15 + */ + public static char[] encodeHex(final byte[] data, final int dataOffset, final int dataLen, + final boolean toLowerCase) { + final char[] out = new char[dataLen << 1]; + encodeHex(data, dataOffset, dataLen, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER, out, 0); + return out; + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * + * @param data a byte[] to convert to hex characters + * @param dataOffset the position in {@code data} to start encoding from + * @param dataLen the number of bytes from {@code dataOffset} to encode + * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase + * @param out a char[] which will hold the resultant appropriate characters from the alphabet. + * @param outOffset the position within {@code out} at which to start writing the encoded characters. + * @since 1.15 + */ + public static void encodeHex(final byte[] data, final int dataOffset, final int dataLen, + final boolean toLowerCase, final char[] out, final int outOffset) { + encodeHex(data, dataOffset, dataLen, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER, out, outOffset); + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * + * @param data a byte[] to convert to hex characters + * @param dataOffset the position in {@code data} to start encoding from + * @param dataLen the number of bytes from {@code dataOffset} to encode + * @param toDigits the output alphabet (must contain at least 16 chars) + * @param out a char[] which will hold the resultant appropriate characters from the alphabet. + * @param outOffset the position within {@code out} at which to start writing the encoded characters. + */ + private static void encodeHex(final byte[] data, final int dataOffset, final int dataLen, final char[] toDigits, + final char[] out, final int outOffset) { + // two characters form the hex value. + for (int i = dataOffset, j = outOffset; i < dataOffset + dataLen; i++) { + out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; + out[j++] = toDigits[0x0F & data[i]]; + } + } + + /** + * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The + * returned array will be double the length of the passed array, as it takes two characters to represent any given + * byte. + * + *

All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method + * the value {@link ByteBuffer#remaining() remaining()} will be zero.

+ * + * @param data a byte buffer to convert to hex characters + * @return A char[] containing lower-case hexadecimal characters + * @since 1.11 + */ + public static char[] encodeHex(final ByteBuffer data) { + return encodeHex(data, true); + } + + /** + * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The + * returned array will be double the length of the passed array, as it takes two characters to represent any given + * byte. + * + *

All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method + * the value {@link ByteBuffer#remaining() remaining()} will be zero.

+ * + * @param data a byte buffer to convert to hex characters + * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase + * @return A char[] containing hexadecimal characters in the selected case + * @since 1.11 + */ + public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) { + return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); + } + + /** + * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The + * returned array will be double the length of the passed array, as it takes two characters to represent any given + * byte. + * + *

All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method + * the value {@link ByteBuffer#remaining() remaining()} will be zero.

+ * + * @param byteBuffer a byte buffer to convert to hex characters + * @param toDigits the output alphabet (must be at least 16 characters) + * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either + * upper- or lower-case hex. + * @since 1.11 + */ + protected static char[] encodeHex(final ByteBuffer byteBuffer, final char[] toDigits) { + return encodeHex(toByteArray(byteBuffer), toDigits); + } + + /** + * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned + * String will be double the length of the passed array, as it takes two characters to represent any given byte. + * + * @param data a byte[] to convert to hex characters + * @return A String containing lower-case hexadecimal characters + * @since 1.4 + */ + public static String encodeHexString(final byte[] data) { + return new String(encodeHex(data)); + } + + /** + * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned + * String will be double the length of the passed array, as it takes two characters to represent any given byte. + * + * @param data a byte[] to convert to hex characters + * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase + * @return A String containing lower-case hexadecimal characters + * @since 1.11 + */ + public static String encodeHexString(final byte[] data, final boolean toLowerCase) { + return new String(encodeHex(data, toLowerCase)); + } + + /** + * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned + * String will be double the length of the passed array, as it takes two characters to represent any given byte. + * + *

All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method + * the value {@link ByteBuffer#remaining() remaining()} will be zero.

+ * + * @param data a byte buffer to convert to hex characters + * @return A String containing lower-case hexadecimal characters + * @since 1.11 + */ + public static String encodeHexString(final ByteBuffer data) { + return new String(encodeHex(data)); + } + + /** + * Converts a byte buffer into a String representing the hexadecimal values of each byte in order. The returned + * String will be double the length of the passed array, as it takes two characters to represent any given byte. + * + *

All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method + * the value {@link ByteBuffer#remaining() remaining()} will be zero.

+ * + * @param data a byte buffer to convert to hex characters + * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase + * @return A String containing lower-case hexadecimal characters + * @since 1.11 + */ + public static String encodeHexString(final ByteBuffer data, final boolean toLowerCase) { + return new String(encodeHex(data, toLowerCase)); + } + + /** + * Convert the byte buffer to a byte array. All bytes identified by + * {@link ByteBuffer#remaining()} will be used. + * + * @param byteBuffer the byte buffer + * @return the byte[] + */ + private static byte[] toByteArray(final ByteBuffer byteBuffer) { + final int remaining = byteBuffer.remaining(); + // Use the underlying buffer if possible + if (byteBuffer.hasArray()) { + final byte[] byteArray = byteBuffer.array(); + if (remaining == byteArray.length) { + byteBuffer.position(remaining); + return byteArray; + } + } + // Copy the bytes + final byte[] byteArray = new byte[remaining]; + byteBuffer.get(byteArray); + return byteArray; + } + + /** + * Converts a hexadecimal character to an integer. + * + * @param ch A character to convert to an integer digit + * @param index The index of the character in the source + * @return An integer + * @throws DecoderException Thrown if ch is an illegal hex character + */ + protected static int toDigit(final char ch, final int index) throws DecoderException { + final int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index); + } + return digit; + } + + private final Charset charset; + + /** + * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET} + */ + public Hex() { + // use default encoding + this.charset = DEFAULT_CHARSET; + } + + /** + * Creates a new codec with the given Charset. + * + * @param charset the charset. + * @since 1.7 + */ + public Hex(final Charset charset) { + this.charset = charset; + } + + /** + * Creates a new codec with the given charset name. + * + * @param charsetName the charset name. + * @throws java.nio.charset.UnsupportedCharsetException If the named charset is unavailable + * @since 1.4 + * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable + */ + public Hex(final String charsetName) { + this(Charset.forName(charsetName)); + } + + /** + * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values. + * The returned array will be half the length of the passed array, as it takes two characters to represent any given + * byte. An exception is thrown if the passed char array has an odd number of elements. + * + * @param array An array of character bytes containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied byte array (representing characters). + * @throws DecoderException Thrown if an odd number of characters is supplied to this function + * @see #decodeHex(char[]) + */ + @Override + public byte[] decode(final byte[] array) throws DecoderException { + return decodeHex(new String(array, getCharset()).toCharArray()); + } + + /** + * Converts a buffer of character bytes representing hexadecimal values into an array of bytes of those same values. + * The returned array will be half the length of the passed array, as it takes two characters to represent any given + * byte. An exception is thrown if the passed char array has an odd number of elements. + * + *

All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method + * the value {@link ByteBuffer#remaining() remaining()} will be zero.

+ * + * @param buffer An array of character bytes containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied byte array (representing characters). + * @throws DecoderException Thrown if an odd number of characters is supplied to this function + * @see #decodeHex(char[]) + * @since 1.11 + */ + public byte[] decode(final ByteBuffer buffer) throws DecoderException { + return decodeHex(new String(toByteArray(buffer), getCharset()).toCharArray()); + } + + /** + * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those + * same values. The returned array will be half the length of the passed String or array, as it takes two characters + * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements. + * + * @param object A String, ByteBuffer, byte[], or an array of character bytes containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied byte array (representing characters). + * @throws DecoderException Thrown if an odd number of characters is supplied to this function or the object is not + * a String or char[] + * @see #decodeHex(char[]) + */ + @Override + public Object decode(final Object object) throws DecoderException { + if (object instanceof String) { + return decode(((String) object).toCharArray()); + } + if (object instanceof byte[]) { + return decode((byte[]) object); + } + if (object instanceof ByteBuffer) { + return decode((ByteBuffer) object); + } + try { + return decodeHex((char[]) object); + } catch (final ClassCastException e) { + throw new DecoderException(e.getMessage(), e); + } + } + + /** + * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each + * byte in order. The returned array will be double the length of the passed array, as it takes two characters to + * represent any given byte. + *

+ * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by + * {@link #getCharset()}. + *

+ * + * @param array a byte[] to convert to hex characters + * @return A byte[] containing the bytes of the lower-case hexadecimal characters + * @since 1.7 No longer throws IllegalStateException if the charsetName is invalid. + * @see #encodeHex(byte[]) + */ + @Override + public byte[] encode(final byte[] array) { + return encodeHexString(array).getBytes(this.getCharset()); + } + + /** + * Converts byte buffer into an array of bytes for the characters representing the hexadecimal values of each byte + * in order. The returned array will be double the length of the passed array, as it takes two characters to + * represent any given byte. + * + *

The conversion from hexadecimal characters to the returned bytes is performed with the charset named by + * {@link #getCharset()}.

+ * + *

All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method + * the value {@link ByteBuffer#remaining() remaining()} will be zero.

+ * + * @param array a byte buffer to convert to hex characters + * @return A byte[] containing the bytes of the lower-case hexadecimal characters + * @see #encodeHex(byte[]) + * @since 1.11 + */ + public byte[] encode(final ByteBuffer array) { + return encodeHexString(array).getBytes(this.getCharset()); + } + + /** + * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each + * byte in order. The returned array will be double the length of the passed String or array, as it takes two + * characters to represent any given byte. + *

+ * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by + * {@link #getCharset()}. + *

+ * + * @param object a String, ByteBuffer, or byte[] to convert to hex characters + * @return A char[] containing lower-case hexadecimal characters + * @throws EncoderException Thrown if the given object is not a String or byte[] + * @see #encodeHex(byte[]) + */ + @Override + public Object encode(final Object object) throws EncoderException { + final byte[] byteArray; + if (object instanceof String) { + byteArray = ((String) object).getBytes(this.getCharset()); + } else if (object instanceof ByteBuffer) { + byteArray = toByteArray((ByteBuffer) object); + } else { + try { + byteArray = (byte[]) object; + } catch (final ClassCastException e) { + throw new EncoderException(e.getMessage(), e); + } + } + return encodeHex(byteArray); + } + + /** + * Gets the charset. + * + * @return the charset. + * @since 1.7 + */ + public Charset getCharset() { + return this.charset; + } + + /** + * Gets the charset name. + * + * @return the charset name. + * @since 1.4 + */ + public String getCharsetName() { + return this.charset.name(); + } + + /** + * Returns a string representation of the object, which includes the charset name. + * + * @return a string representation of the object. + */ + @Override + public String toString() { + return super.toString() + "[charsetName=" + this.charset + "]"; + } +} diff --git a/app/src/main/java/vendored/org/apache/commons/codec/binary/StringUtils.java b/app/src/main/java/vendored/org/apache/commons/codec/binary/StringUtils.java new file mode 100644 index 000000000..c1f1e3957 --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/binary/StringUtils.java @@ -0,0 +1,419 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package vendored.org.apache.commons.codec.binary; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +import vendored.org.apache.commons.codec.CharEncoding; + +/** + * Converts String to and from bytes using the encodings required by the Java specification. These encodings are + * specified in + * Standard charsets. + * + *

This class is immutable and thread-safe.

+ * + * @see CharEncoding + * @see Standard charsets + * @since 1.4 + */ +public class StringUtils { + + /** + *

+ * Compares two CharSequences, returning {@code true} if they represent equal sequences of characters. + *

+ * + *

+ * {@code null}s are handled without exceptions. Two {@code null} references are considered to be equal. + * The comparison is case sensitive. + *

+ * + *
+     * StringUtils.equals(null, null)   = true
+     * StringUtils.equals(null, "abc")  = false
+     * StringUtils.equals("abc", null)  = false
+     * StringUtils.equals("abc", "abc") = true
+     * StringUtils.equals("abc", "ABC") = false
+     * 
+ * + *

+ * Copied from Apache Commons Lang r1583482 on April 10, 2014 (day of 3.3.2 release). + *

+ * + * @see Object#equals(Object) + * @param cs1 + * the first CharSequence, may be {@code null} + * @param cs2 + * the second CharSequence, may be {@code null} + * @return {@code true} if the CharSequences are equal (case-sensitive), or both {@code null} + * @since 1.10 + */ + public static boolean equals(final CharSequence cs1, final CharSequence cs2) { + if (cs1 == cs2) { + return true; + } + if (cs1 == null || cs2 == null) { + return false; + } + if (cs1 instanceof String && cs2 instanceof String) { + return cs1.equals(cs2); + } + return cs1.length() == cs2.length() && CharSequenceUtils.regionMatches(cs1, false, 0, cs2, 0, cs1.length()); + } + + /** + * Calls {@link String#getBytes(Charset)} + * + * @param string + * The string to encode (if null, return null). + * @param charset + * The {@link Charset} to encode the {@code String} + * @return the encoded bytes + */ + private static ByteBuffer getByteBuffer(final String string, final Charset charset) { + if (string == null) { + return null; + } + return ByteBuffer.wrap(string.getBytes(charset)); + } + + /** + * Encodes the given string into a byte buffer using the UTF-8 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be {@code null} + * @return encoded bytes, or {@code null} if the input string was {@code null} + * @throws NullPointerException + * Thrown if {@link StandardCharsets#UTF_8} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + * @since 1.11 + */ + public static ByteBuffer getByteBufferUtf8(final String string) { + return getByteBuffer(string, StandardCharsets.UTF_8); + } + + /** + * Calls {@link String#getBytes(Charset)} + * + * @param string + * The string to encode (if null, return null). + * @param charset + * The {@link Charset} to encode the {@code String} + * @return the encoded bytes + */ + private static byte[] getBytes(final String string, final Charset charset) { + if (string == null) { + return null; + } + return string.getBytes(charset); + } + + /** + * Encodes the given string into a sequence of bytes using the ISO-8859-1 charset, storing the result into a new + * byte array. + * + * @param string + * the String to encode, may be {@code null} + * @return encoded bytes, or {@code null} if the input string was {@code null} + * @throws NullPointerException + * Thrown if {@link StandardCharsets#ISO_8859_1} is not initialized, which should never happen + * since it is required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesIso8859_1(final String string) { + return getBytes(string, StandardCharsets.ISO_8859_1); + } + + + /** + * Encodes the given string into a sequence of bytes using the named charset, storing the result into a new byte + * array. + *

+ * This method catches {@link UnsupportedEncodingException} and rethrows it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

+ * + * @param string + * the String to encode, may be {@code null} + * @param charsetName + * The name of a required {@link java.nio.charset.Charset} + * @return encoded bytes, or {@code null} if the input string was {@code null} + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#getBytes(String) + */ + public static byte[] getBytesUnchecked(final String string, final String charsetName) { + if (string == null) { + return null; + } + try { + return string.getBytes(charsetName); + } catch (final UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + /** + * Encodes the given string into a sequence of bytes using the US-ASCII charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be {@code null} + * @return encoded bytes, or {@code null} if the input string was {@code null} + * @throws NullPointerException + * Thrown if {@link StandardCharsets#US_ASCII} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUsAscii(final String string) { + return getBytes(string, StandardCharsets.US_ASCII); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be {@code null} + * @return encoded bytes, or {@code null} if the input string was {@code null} + * @throws NullPointerException + * Thrown if {@link StandardCharsets#UTF_16} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16(final String string) { + return getBytes(string, StandardCharsets.UTF_16); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16BE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be {@code null} + * @return encoded bytes, or {@code null} if the input string was {@code null} + * @throws NullPointerException + * Thrown if {@link StandardCharsets#UTF_16BE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Be(final String string) { + return getBytes(string, StandardCharsets.UTF_16BE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16LE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be {@code null} + * @return encoded bytes, or {@code null} if the input string was {@code null} + * @throws NullPointerException + * Thrown if {@link StandardCharsets#UTF_16LE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Le(final String string) { + return getBytes(string, StandardCharsets.UTF_16LE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-8 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be {@code null} + * @return encoded bytes, or {@code null} if the input string was {@code null} + * @throws NullPointerException + * Thrown if {@link StandardCharsets#UTF_8} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf8(final String string) { + return getBytes(string, StandardCharsets.UTF_8); + } + + private static IllegalStateException newIllegalStateException(final String charsetName, + final UnsupportedEncodingException e) { + return new IllegalStateException(charsetName + ": " + e); + } + + /** + * Constructs a new {@code String} by decoding the specified array of bytes using the given charset. + * + * @param bytes + * The bytes to be decoded into characters + * @param charset + * The {@link Charset} to encode the {@code String}; not {@code null} + * @return A new {@code String} decoded from the specified array of bytes using the given charset, + * or {@code null} if the input byte array was {@code null}. + * @throws NullPointerException + * Thrown if charset is {@code null} + */ + private static String newString(final byte[] bytes, final Charset charset) { + return bytes == null ? null : new String(bytes, charset); + } + + /** + * Constructs a new {@code String} by decoding the specified array of bytes using the given charset. + *

+ * This method catches {@link UnsupportedEncodingException} and re-throws it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

+ * + * @param bytes + * The bytes to be decoded into characters, may be {@code null} + * @param charsetName + * The name of a required {@link java.nio.charset.Charset} + * @return A new {@code String} decoded from the specified array of bytes using the given charset, + * or {@code null} if the input byte array was {@code null}. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#String(byte[], String) + */ + public static String newString(final byte[] bytes, final String charsetName) { + if (bytes == null) { + return null; + } + try { + return new String(bytes, charsetName); + } catch (final UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + /** + * Constructs a new {@code String} by decoding the specified array of bytes using the ISO-8859-1 charset. + * + * @param bytes + * The bytes to be decoded into characters, may be {@code null} + * @return A new {@code String} decoded from the specified array of bytes using the ISO-8859-1 charset, or + * {@code null} if the input byte array was {@code null}. + * @throws NullPointerException + * Thrown if {@link StandardCharsets#ISO_8859_1} is not initialized, which should never happen + * since it is required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringIso8859_1(final byte[] bytes) { + return newString(bytes, StandardCharsets.ISO_8859_1); + } + + /** + * Constructs a new {@code String} by decoding the specified array of bytes using the US-ASCII charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new {@code String} decoded from the specified array of bytes using the US-ASCII charset, + * or {@code null} if the input byte array was {@code null}. + * @throws NullPointerException + * Thrown if {@link StandardCharsets#US_ASCII} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUsAscii(final byte[] bytes) { + return newString(bytes, StandardCharsets.US_ASCII); + } + + /** + * Constructs a new {@code String} by decoding the specified array of bytes using the UTF-16 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new {@code String} decoded from the specified array of bytes using the UTF-16 charset + * or {@code null} if the input byte array was {@code null}. + * @throws NullPointerException + * Thrown if {@link StandardCharsets#UTF_16} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf16(final byte[] bytes) { + return newString(bytes, StandardCharsets.UTF_16); + } + + /** + * Constructs a new {@code String} by decoding the specified array of bytes using the UTF-16BE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new {@code String} decoded from the specified array of bytes using the UTF-16BE charset, + * or {@code null} if the input byte array was {@code null}. + * @throws NullPointerException + * Thrown if {@link StandardCharsets#UTF_16BE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf16Be(final byte[] bytes) { + return newString(bytes, StandardCharsets.UTF_16BE); + } + + /** + * Constructs a new {@code String} by decoding the specified array of bytes using the UTF-16LE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new {@code String} decoded from the specified array of bytes using the UTF-16LE charset, + * or {@code null} if the input byte array was {@code null}. + * @throws NullPointerException + * Thrown if {@link StandardCharsets#UTF_16LE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf16Le(final byte[] bytes) { + return newString(bytes, StandardCharsets.UTF_16LE); + } + + /** + * Constructs a new {@code String} by decoding the specified array of bytes using the UTF-8 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new {@code String} decoded from the specified array of bytes using the UTF-8 charset, + * or {@code null} if the input byte array was {@code null}. + * @throws NullPointerException + * Thrown if {@link StandardCharsets#UTF_8} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf8(final byte[] bytes) { + return newString(bytes, StandardCharsets.UTF_8); + } + +} diff --git a/app/src/main/java/vendored/org/apache/commons/codec/digest/DigestUtils.java b/app/src/main/java/vendored/org/apache/commons/codec/digest/DigestUtils.java new file mode 100644 index 000000000..0db245497 --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/digest/DigestUtils.java @@ -0,0 +1,1743 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package vendored.org.apache.commons.codec.digest; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import vendored.org.apache.commons.codec.binary.Hex; +import vendored.org.apache.commons.codec.binary.StringUtils; + +import androidx.annotation.RequiresApi; + +/** + * Operations to simplify common {@link java.security.MessageDigest} tasks. + * This class is immutable and thread-safe. + * However the MessageDigest instances it creates generally won't be. + *

+ * The {@link MessageDigestAlgorithms} class provides constants for standard + * digest algorithms that can be used with the {@link #getDigest(String)} method + * and other methods that require the Digest algorithm name. + *

+ * Note: the class has short-hand methods for all the algorithms present as standard in Java 6. + * This approach requires lots of methods for each algorithm, and quickly becomes unwieldy. + * The following code works with all algorithms: + *

+ * import static org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA_224;
+ * ...
+ * byte [] digest = new DigestUtils(SHA_224).digest(dataToDigest);
+ * String hdigest = new DigestUtils(SHA_224).digestAsHex(new File("pom.xml"));
+ * 
+ * @see MessageDigestAlgorithms + */ +public class DigestUtils { + + private static final int STREAM_BUFFER_LENGTH = 1024; + + /** + * Reads through a byte array and returns the digest for the data. Provided for symmetry with other methods. + * + * @param messageDigest + * The MessageDigest to use (e.g. MD5) + * @param data + * Data to digest + * @return the digest + * @since 1.11 + */ + public static byte[] digest(final MessageDigest messageDigest, final byte[] data) { + return messageDigest.digest(data); + } + + /** + * Reads through a ByteBuffer and returns the digest for the data + * + * @param messageDigest + * The MessageDigest to use (e.g. MD5) + * @param data + * Data to digest + * @return the digest + * + * @since 1.11 + */ + public static byte[] digest(final MessageDigest messageDigest, final ByteBuffer data) { + messageDigest.update(data); + return messageDigest.digest(); + } + + /** + * Reads through a File and returns the digest for the data + * + * @param messageDigest + * The MessageDigest to use (e.g. MD5) + * @param data + * Data to digest + * @return the digest + * @throws IOException + * On error reading from the stream + * @since 1.11 + */ + public static byte[] digest(final MessageDigest messageDigest, final File data) throws IOException { + return updateDigest(messageDigest, data).digest(); + } + + /** + * Reads through an InputStream and returns the digest for the data + * + * @param messageDigest + * The MessageDigest to use (e.g. MD5) + * @param data + * Data to digest + * @return the digest + * @throws IOException + * On error reading from the stream + * @since 1.11 (was private) + */ + public static byte[] digest(final MessageDigest messageDigest, final InputStream data) throws IOException { + return updateDigest(messageDigest, data).digest(); + } + + /** + * Reads through a File and returns the digest for the data + * + * @param messageDigest + * The MessageDigest to use (e.g. MD5) + * @param data + * Data to digest + * @param options + * options How to open the file + * @return the digest + * @throws IOException + * On error reading from the stream + * @since 1.14 + */ + @RequiresApi(api = 26) + public static byte[] digest(final MessageDigest messageDigest, final Path data, final OpenOption... options) + throws IOException { + return updateDigest(messageDigest, data, options).digest(); + } + + /** + * Reads through a RandomAccessFile using non-blocking-io (NIO) and returns the digest for the data + * + * @param messageDigest The MessageDigest to use (e.g. MD5) + * @param data Data to digest + * @return the digest + * @throws IOException On error reading from the stream + * @since 1.14 + */ + public static byte[] digest(final MessageDigest messageDigest, final RandomAccessFile data) throws IOException { + return updateDigest(messageDigest, data).digest(); + } + + /** + * Returns a {@code MessageDigest} for the given {@code algorithm}. + * + * @param algorithm + * the name of the algorithm requested. See Appendix A in the Java Cryptography Architecture Reference Guide for information about standard + * algorithm names. + * @return A digest instance. + * @see MessageDigest#getInstance(String) + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught. + */ + public static MessageDigest getDigest(final String algorithm) { + try { + return MessageDigest.getInstance(algorithm); + } catch (final NoSuchAlgorithmException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Returns a {@code MessageDigest} for the given {@code algorithm} or a default if there is a problem + * getting the algorithm. + * + * @param algorithm + * the name of the algorithm requested. See + * + * Appendix A in the Java Cryptography Architecture Reference Guide for information about standard + * algorithm names. + * @param defaultMessageDigest + * The default MessageDigest. + * @return A digest instance. + * @see MessageDigest#getInstance(String) + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught. + * @since 1.11 + */ + public static MessageDigest getDigest(final String algorithm, final MessageDigest defaultMessageDigest) { + try { + return MessageDigest.getInstance(algorithm); + } catch (final Exception e) { + return defaultMessageDigest; + } + } + + /** + * Returns an MD2 MessageDigest. + * + * @return An MD2 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen because MD2 is a + * built-in algorithm + * @see MessageDigestAlgorithms#MD2 + * @since 1.7 + */ + public static MessageDigest getMd2Digest() { + return getDigest(MessageDigestAlgorithms.MD2); + } + + /** + * Returns an MD5 MessageDigest. + * + * @return An MD5 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen because MD5 is a + * built-in algorithm + * @see MessageDigestAlgorithms#MD5 + */ + public static MessageDigest getMd5Digest() { + return getDigest(MessageDigestAlgorithms.MD5); + } + + /** + * Returns an SHA-1 digest. + * + * @return An SHA-1 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen because SHA-1 is a + * built-in algorithm + * @see MessageDigestAlgorithms#SHA_1 + * @since 1.7 + */ + public static MessageDigest getSha1Digest() { + return getDigest(MessageDigestAlgorithms.SHA_1); + } + + /** + * Returns an SHA-256 digest. + * + * @return An SHA-256 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen because SHA-256 is a + * built-in algorithm + * @see MessageDigestAlgorithms#SHA_256 + */ + public static MessageDigest getSha256Digest() { + return getDigest(MessageDigestAlgorithms.SHA_256); + } + + /** + * Returns an SHA3-224 digest. + * + * @return An SHA3-224 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should not happen on + * Oracle Java 9 andgreater. + * @see MessageDigestAlgorithms#SHA3_224 + * @since 1.12 + */ + public static MessageDigest getSha3_224Digest() { + return getDigest(MessageDigestAlgorithms.SHA3_224); + } + + /** + * Returns an SHA3-256 digest. + * + * @return An SHA3-256 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should not happen on + * Oracle Java 9 and greater. + * @see MessageDigestAlgorithms#SHA3_256 + * @since 1.12 + */ + public static MessageDigest getSha3_256Digest() { + return getDigest(MessageDigestAlgorithms.SHA3_256); + } + + /** + * Returns an SHA3-384 digest. + * + * @return An SHA3-384 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should not happen on + * Oracle Java 9 and greater. + * @see MessageDigestAlgorithms#SHA3_384 + * @since 1.12 + */ + public static MessageDigest getSha3_384Digest() { + return getDigest(MessageDigestAlgorithms.SHA3_384); + } + + /** + * Returns an SHA3-512 digest. + * + * @return An SHA3-512 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should not happen + * on Oracle Java 9 and greater. + * @see MessageDigestAlgorithms#SHA3_512 + * @since 1.12 + */ + public static MessageDigest getSha3_512Digest() { + return getDigest(MessageDigestAlgorithms.SHA3_512); + } + + /** + * Returns an SHA-384 digest. + * + * @return An SHA-384 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen + * because SHA-384 is a built-in algorithm + * @see MessageDigestAlgorithms#SHA_384 + */ + public static MessageDigest getSha384Digest() { + return getDigest(MessageDigestAlgorithms.SHA_384); + } + + /** + * Returns an SHA-512/224 digest. + * + * @return An SHA-512/224 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught. + * @see MessageDigestAlgorithms#SHA_512_224 + */ + public static MessageDigest getSha512_224Digest() { + return getDigest(MessageDigestAlgorithms.SHA_512_224); + } + + /** + * Returns an SHA-512/256 digest. + * + * @return An SHA-512/256 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught. + * @see MessageDigestAlgorithms#SHA_512_224 + */ + public static MessageDigest getSha512_256Digest() { + return getDigest(MessageDigestAlgorithms.SHA_512_256); + } + + /** + * Returns an SHA-512 digest. + * + * @return An SHA-512 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen + * because SHA-512 is a built-in algorithm + * @see MessageDigestAlgorithms#SHA_512 + */ + public static MessageDigest getSha512Digest() { + return getDigest(MessageDigestAlgorithms.SHA_512); + } + + /** + * Returns an SHA-1 digest. + * + * @return An SHA-1 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught + * @deprecated (1.11) Use {@link #getSha1Digest()} + */ + @Deprecated + public static MessageDigest getShaDigest() { + return getSha1Digest(); + } + + /** + * Test whether the algorithm is supported. + * @param messageDigestAlgorithm the algorithm name + * @return {@code true} if the algorithm can be found + * @since 1.11 + */ + public static boolean isAvailable(final String messageDigestAlgorithm) { + return getDigest(messageDigestAlgorithm, null) != null; + } + + /** + * Calculates the MD2 digest and returns the value as a 16 element {@code byte[]}. + * + * @param data + * Data to digest + * @return MD2 digest + * @since 1.7 + */ + public static byte[] md2(final byte[] data) { + return getMd2Digest().digest(data); + } + + /** + * Calculates the MD2 digest and returns the value as a 16 element {@code byte[]}. + * + * @param data + * Data to digest + * @return MD2 digest + * @throws IOException + * On error reading from the stream + * @since 1.7 + */ + public static byte[] md2(final InputStream data) throws IOException { + return digest(getMd2Digest(), data); + } + + /** + * Calculates the MD2 digest and returns the value as a 16 element {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return MD2 digest + * @since 1.7 + */ + public static byte[] md2(final String data) { + return md2(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the MD2 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD2 digest as a hex string + * @since 1.7 + */ + public static String md2Hex(final byte[] data) { + return Hex.encodeHexString(md2(data)); + } + + /** + * Calculates the MD2 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD2 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.7 + */ + public static String md2Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(md2(data)); + } + + /** + * Calculates the MD2 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD2 digest as a hex string + * @since 1.7 + */ + public static String md2Hex(final String data) { + return Hex.encodeHexString(md2(data)); + } + + /** + * Calculates the MD5 digest and returns the value as a 16 element {@code byte[]}. + * + * @param data + * Data to digest + * @return MD5 digest + */ + public static byte[] md5(final byte[] data) { + return getMd5Digest().digest(data); + } + + /** + * Calculates the MD5 digest and returns the value as a 16 element {@code byte[]}. + * + * @param data + * Data to digest + * @return MD5 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] md5(final InputStream data) throws IOException { + return digest(getMd5Digest(), data); + } + + /** + * Calculates the MD5 digest and returns the value as a 16 element {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return MD5 digest + */ + public static byte[] md5(final String data) { + return md5(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD5 digest as a hex string + */ + public static String md5Hex(final byte[] data) { + return Hex.encodeHexString(md5(data)); + } + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD5 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String md5Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(md5(data)); + } + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD5 digest as a hex string + */ + public static String md5Hex(final String data) { + return Hex.encodeHexString(md5(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-1 digest + * @deprecated (1.11) Use {@link #sha1(byte[])} + */ + @Deprecated + public static byte[] sha(final byte[] data) { + return sha1(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-1 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + * @deprecated (1.11) Use {@link #sha1(InputStream)} + */ + @Deprecated + public static byte[] sha(final InputStream data) throws IOException { + return sha1(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-1 digest + * @deprecated (1.11) Use {@link #sha1(String)} + */ + @Deprecated + public static byte[] sha(final String data) { + return sha1(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-1 digest + * @since 1.7 + */ + public static byte[] sha1(final byte[] data) { + return getSha1Digest().digest(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-1 digest + * @throws IOException + * On error reading from the stream + * @since 1.7 + */ + public static byte[] sha1(final InputStream data) throws IOException { + return digest(getSha1Digest(), data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA-1 digest + */ + public static byte[] sha1(final String data) { + return sha1(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @since 1.7 + */ + public static String sha1Hex(final byte[] data) { + return Hex.encodeHexString(sha1(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.7 + */ + public static String sha1Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(sha1(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @since 1.7 + */ + public static String sha1Hex(final String data) { + return Hex.encodeHexString(sha1(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-256 digest + * @since 1.4 + */ + public static byte[] sha256(final byte[] data) { + return getSha256Digest().digest(data); + } + + /** + * Calculates the SHA-256 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-256 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] sha256(final InputStream data) throws IOException { + return digest(getSha256Digest(), data); + } + + /** + * Calculates the SHA-256 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA-256 digest + * @since 1.4 + */ + public static byte[] sha256(final String data) { + return sha256(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-256 digest as a hex string + * @since 1.4 + */ + public static String sha256Hex(final byte[] data) { + return Hex.encodeHexString(sha256(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-256 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String sha256Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(sha256(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-256 digest as a hex string + * @since 1.4 + */ + public static String sha256Hex(final String data) { + return Hex.encodeHexString(sha256(data)); + } + + /** + * Calculates the SHA3-224 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA3-224 digest + * @since 1.12 + */ + public static byte[] sha3_224(final byte[] data) { + return getSha3_224Digest().digest(data); + } + + /** + * Calculates the SHA3-224 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA3-224 digest + * @throws IOException + * On error reading from the stream + * @since 1.12 + */ + public static byte[] sha3_224(final InputStream data) throws IOException { + return digest(getSha3_224Digest(), data); + } + + /** + * Calculates the SHA3-224 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA3-224 digest + * @since 1.12 + */ + public static byte[] sha3_224(final String data) { + return sha3_224(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA3-224 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-224 digest as a hex string + * @since 1.12 + */ + public static String sha3_224Hex(final byte[] data) { + return Hex.encodeHexString(sha3_224(data)); + } + + /** + * Calculates the SHA3-224 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-224 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.12 + */ + public static String sha3_224Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(sha3_224(data)); + } + + /** + * Calculates the SHA3-224 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-224 digest as a hex string + * @since 1.12 + */ + public static String sha3_224Hex(final String data) { + return Hex.encodeHexString(sha3_224(data)); + } + + /** + * Calculates the SHA3-256 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA3-256 digest + * @since 1.12 + */ + public static byte[] sha3_256(final byte[] data) { + return getSha3_256Digest().digest(data); + } + + /** + * Calculates the SHA3-256 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA3-256 digest + * @throws IOException + * On error reading from the stream + * @since 1.12 + */ + public static byte[] sha3_256(final InputStream data) throws IOException { + return digest(getSha3_256Digest(), data); + } + + /** + * Calculates the SHA3-256 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA3-256 digest + * @since 1.12 + */ + public static byte[] sha3_256(final String data) { + return sha3_256(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA3-256 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-256 digest as a hex string + * @since 1.12 + */ + public static String sha3_256Hex(final byte[] data) { + return Hex.encodeHexString(sha3_256(data)); + } + + /** + * Calculates the SHA3-256 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-256 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.12 + */ + public static String sha3_256Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(sha3_256(data)); + } + + /** + * Calculates the SHA3-256 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-256 digest as a hex string + * @since 1.12 + */ + public static String sha3_256Hex(final String data) { + return Hex.encodeHexString(sha3_256(data)); + } + + /** + * Calculates the SHA3-384 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA3-384 digest + * @since 1.12 + */ + public static byte[] sha3_384(final byte[] data) { + return getSha3_384Digest().digest(data); + } + + /** + * Calculates the SHA3-384 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA3-384 digest + * @throws IOException + * On error reading from the stream + * @since 1.12 + */ + public static byte[] sha3_384(final InputStream data) throws IOException { + return digest(getSha3_384Digest(), data); + } + + /** + * Calculates the SHA3-384 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA3-384 digest + * @since 1.12 + */ + public static byte[] sha3_384(final String data) { + return sha3_384(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA3-384 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-384 digest as a hex string + * @since 1.12 + */ + public static String sha3_384Hex(final byte[] data) { + return Hex.encodeHexString(sha3_384(data)); + } + + /** + * Calculates the SHA3-384 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-384 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.12 + */ + public static String sha3_384Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(sha3_384(data)); + } + + /** + * Calculates the SHA3-384 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-384 digest as a hex string + * @since 1.12 + */ + public static String sha3_384Hex(final String data) { + return Hex.encodeHexString(sha3_384(data)); + } + + /** + * Calculates the SHA3-512 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA3-512 digest + * @since 1.12 + */ + public static byte[] sha3_512(final byte[] data) { + return getSha3_512Digest().digest(data); + } + + /** + * Calculates the SHA3-512 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA3-512 digest + * @throws IOException + * On error reading from the stream + * @since 1.12 + */ + public static byte[] sha3_512(final InputStream data) throws IOException { + return digest(getSha3_512Digest(), data); + } + + /** + * Calculates the SHA3-512 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA3-512 digest + * @since 1.12 + */ + public static byte[] sha3_512(final String data) { + return sha3_512(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA3-512 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-512 digest as a hex string + * @since 1.12 + */ + public static String sha3_512Hex(final byte[] data) { + return Hex.encodeHexString(sha3_512(data)); + } + + /** + * Calculates the SHA3-512 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-512 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.12 + */ + public static String sha3_512Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(sha3_512(data)); + } + + /** + * Calculates the SHA3-512 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA3-512 digest as a hex string + * @since 1.12 + */ + public static String sha3_512Hex(final String data) { + return Hex.encodeHexString(sha3_512(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-384 digest + * @since 1.4 + */ + public static byte[] sha384(final byte[] data) { + return getSha384Digest().digest(data); + } + + /** + * Calculates the SHA-384 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-384 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] sha384(final InputStream data) throws IOException { + return digest(getSha384Digest(), data); + } + + /** + * Calculates the SHA-384 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA-384 digest + * @since 1.4 + */ + public static byte[] sha384(final String data) { + return sha384(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-384 digest as a hex string + * @since 1.4 + */ + public static String sha384Hex(final byte[] data) { + return Hex.encodeHexString(sha384(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-384 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String sha384Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(sha384(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-384 digest as a hex string + * @since 1.4 + */ + public static String sha384Hex(final String data) { + return Hex.encodeHexString(sha384(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-512 digest + * @since 1.4 + */ + public static byte[] sha512(final byte[] data) { + return getSha512Digest().digest(data); + } + + /** + * Calculates the SHA-512 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-512 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] sha512(final InputStream data) throws IOException { + return digest(getSha512Digest(), data); + } + + /** + * Calculates the SHA-512 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA-512 digest + * @since 1.4 + */ + public static byte[] sha512(final String data) { + return sha512(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA-512/224 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-512/224 digest + * @since 1.14 + */ + public static byte[] sha512_224(final byte[] data) { + return getSha512_224Digest().digest(data); + } + + /** + * Calculates the SHA-512/224 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-512/224 digest + * @throws IOException + * On error reading from the stream + * @since 1.14 + */ + public static byte[] sha512_224(final InputStream data) throws IOException { + return digest(getSha512_224Digest(), data); + } + + /** + * Calculates the SHA-512/224 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA-512/224 digest + * @since 1.14 + */ + public static byte[] sha512_224(final String data) { + return sha512_224(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA-512/224 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-512/224 digest as a hex string + * @since 1.14 + */ + public static String sha512_224Hex(final byte[] data) { + return Hex.encodeHexString(sha512_224(data)); + } + + /** + * Calculates the SHA-512/224 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-512/224 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.14 + */ + public static String sha512_224Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(sha512_224(data)); + } + + /** + * Calculates the SHA-512/224 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-512/224 digest as a hex string + * @since 1.14 + */ + public static String sha512_224Hex(final String data) { + return Hex.encodeHexString(sha512_224(data)); + } + + /** + * Calculates the SHA-512/256 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-512/256 digest + * @since 1.14 + */ + public static byte[] sha512_256(final byte[] data) { + return getSha512_256Digest().digest(data); + } + + /** + * Calculates the SHA-512/256 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest + * @return SHA-512/256 digest + * @throws IOException + * On error reading from the stream + * @since 1.14 + */ + public static byte[] sha512_256(final InputStream data) throws IOException { + return digest(getSha512_256Digest(), data); + } + + /** + * Calculates the SHA-512/256 digest and returns the value as a {@code byte[]}. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA-512/224 digest + * @since 1.14 + */ + public static byte[] sha512_256(final String data) { + return sha512_256(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA-512/256 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-512/256 digest as a hex string + * @since 1.14 + */ + public static String sha512_256Hex(final byte[] data) { + return Hex.encodeHexString(sha512_256(data)); + } + + /** + * Calculates the SHA-512/256 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-512/256 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.14 + */ + public static String sha512_256Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(sha512_256(data)); + } + + /** + * Calculates the SHA-512/256 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-512/256 digest as a hex string + * @since 1.14 + */ + public static String sha512_256Hex(final String data) { + return Hex.encodeHexString(sha512_256(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-512 digest as a hex string + * @since 1.4 + */ + public static String sha512Hex(final byte[] data) { + return Hex.encodeHexString(sha512(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-512 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String sha512Hex(final InputStream data) throws IOException { + return Hex.encodeHexString(sha512(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-512 digest as a hex string + * @since 1.4 + */ + public static String sha512Hex(final String data) { + return Hex.encodeHexString(sha512(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @deprecated (1.11) Use {@link #sha1Hex(byte[])} + */ + @Deprecated + public static String shaHex(final byte[] data) { + return sha1Hex(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + * @deprecated (1.11) Use {@link #sha1Hex(InputStream)} + */ + @Deprecated + public static String shaHex(final InputStream data) throws IOException { + return sha1Hex(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @deprecated (1.11) Use {@link #sha1Hex(String)} + */ + @Deprecated + public static String shaHex(final String data) { + return sha1Hex(data); + } + + /** + * Updates the given {@link MessageDigest}. + * + * @param messageDigest + * the {@link MessageDigest} to update + * @param valueToDigest + * the value to update the {@link MessageDigest} with + * @return the updated {@link MessageDigest} + * @since 1.7 + */ + public static MessageDigest updateDigest(final MessageDigest messageDigest, final byte[] valueToDigest) { + messageDigest.update(valueToDigest); + return messageDigest; + } + + /** + * Updates the given {@link MessageDigest}. + * + * @param messageDigest + * the {@link MessageDigest} to update + * @param valueToDigest + * the value to update the {@link MessageDigest} with + * @return the updated {@link MessageDigest} + * @since 1.11 + */ + public static MessageDigest updateDigest(final MessageDigest messageDigest, final ByteBuffer valueToDigest) { + messageDigest.update(valueToDigest); + return messageDigest; + } + + /** + * Reads through a File and updates the digest for the data + * + * @param digest + * The MessageDigest to use (e.g. MD5) + * @param data + * Data to digest + * @return the digest + * @throws IOException + * On error reading from the stream + * @since 1.11 + */ + public static MessageDigest updateDigest(final MessageDigest digest, final File data) throws IOException { + try (final BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(data))) { + return updateDigest(digest, inputStream); + } + } + + /** + * Reads through a RandomAccessFile and updates the digest for the data using non-blocking-io (NIO). + * + * TODO Decide if this should be public. + * + * @param digest The MessageDigest to use (e.g. MD5) + * @param data Data to digest + * @return the digest + * @throws IOException On error reading from the stream + * @since 1.14 + */ + private static MessageDigest updateDigest(final MessageDigest digest, final FileChannel data) throws IOException { + final ByteBuffer buffer = ByteBuffer.allocate(STREAM_BUFFER_LENGTH); + while (data.read(buffer) > 0) { + buffer.flip(); + digest.update(buffer); + buffer.clear(); + } + return digest; + } + + /** + * Reads through an InputStream and updates the digest for the data + * + * @param digest + * The MessageDigest to use (e.g. MD5) + * @param inputStream + * Data to digest + * @return the digest + * @throws IOException + * On error reading from the stream + * @since 1.8 + */ + public static MessageDigest updateDigest(final MessageDigest digest, final InputStream inputStream) + throws IOException { + final byte[] buffer = new byte[STREAM_BUFFER_LENGTH]; + int read = inputStream.read(buffer, 0, STREAM_BUFFER_LENGTH); + + while (read > -1) { + digest.update(buffer, 0, read); + read = inputStream.read(buffer, 0, STREAM_BUFFER_LENGTH); + } + + return digest; + } + + /** + * Reads through a Path and updates the digest for the data + * + * @param digest + * The MessageDigest to use (e.g. MD5) + * @param path + * Data to digest + * @param options + * options How to open the file + * @return the digest + * @throws IOException + * On error reading from the stream + * @since 1.14 + */ + @RequiresApi(api = 26) + public static MessageDigest updateDigest(final MessageDigest digest, final Path path, final OpenOption... options) + throws IOException { + try (final BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(path, options))) { + return updateDigest(digest, inputStream); + } + } + + /** + * Reads through a RandomAccessFile and updates the digest for the data using non-blocking-io (NIO) + * + * @param digest The MessageDigest to use (e.g. MD5) + * @param data Data to digest + * @return the digest + * @throws IOException On error reading from the stream + * @since 1.14 + */ + public static MessageDigest updateDigest(final MessageDigest digest, final RandomAccessFile data) + throws IOException { + return updateDigest(digest, data.getChannel()); + } + + /** + * Updates the given {@link MessageDigest} from a String (converted to bytes using UTF-8). + *

+ * To update the digest using a different charset for the conversion, + * convert the String to a byte array using + * {@link String#getBytes(java.nio.charset.Charset)} and pass that + * to the {@link DigestUtils#updateDigest(MessageDigest, byte[])} method + * + * @param messageDigest + * the {@link MessageDigest} to update + * @param valueToDigest + * the value to update the {@link MessageDigest} with; + * converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return the updated {@link MessageDigest} + * @since 1.7 + */ + public static MessageDigest updateDigest(final MessageDigest messageDigest, final String valueToDigest) { + messageDigest.update(StringUtils.getBytesUtf8(valueToDigest)); + return messageDigest; + } + + private final MessageDigest messageDigest; + + /** + * Preserves binary compatibility only. + * As for previous versions does not provide useful behavior + * @deprecated since 1.11; only useful to preserve binary compatibility + */ + @Deprecated + public DigestUtils() { + this.messageDigest = null; + } + + /** + * Creates an instance using the provided {@link MessageDigest} parameter. + * + * This can then be used to create digests using methods such as + * {@link #digest(byte[])} and {@link #digestAsHex(File)}. + * + * @param digest the {@link MessageDigest} to use + * @since 1.11 + */ + public DigestUtils(final MessageDigest digest) { + this.messageDigest = digest; + } + + /** + * Creates an instance using the provided {@link MessageDigest} parameter. + * + * This can then be used to create digests using methods such as + * {@link #digest(byte[])} and {@link #digestAsHex(File)}. + * + * @param name the name of the {@link MessageDigest} to use + * @see #getDigest(String) + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught. + * @since 1.11 + */ + public DigestUtils(final String name) { + this(getDigest(name)); + } + + /** + * Reads through a byte array and returns the digest for the data. + * + * @param data + * Data to digest + * @return the digest + * @since 1.11 + */ + public byte[] digest(final byte[] data) { + return updateDigest(messageDigest, data).digest(); + } + + /** + * Reads through a ByteBuffer and returns the digest for the data + * + * @param data + * Data to digest + * @return the digest + * + * @since 1.11 + */ + public byte[] digest(final ByteBuffer data) { + return updateDigest(messageDigest, data).digest(); + } + + /** + * Reads through a File and returns the digest for the data + * + * @param data + * Data to digest + * @return the digest + * @throws IOException + * On error reading from the stream + * @since 1.11 + */ + public byte[] digest(final File data) throws IOException { + return updateDigest(messageDigest, data).digest(); + } + + /** + * Reads through an InputStream and returns the digest for the data + * + * @param data + * Data to digest + * @return the digest + * @throws IOException + * On error reading from the stream + * @since 1.11 + */ + public byte[] digest(final InputStream data) throws IOException { + return updateDigest(messageDigest, data).digest(); + } + + /** + * Reads through a File and returns the digest for the data + * + * @param data + * Data to digest + * @param options + * options How to open the file + * @return the digest + * @throws IOException + * On error reading from the stream + * @since 1.14 + */ + @RequiresApi(api = 26) + public byte[] digest(final Path data, final OpenOption... options) throws IOException { + return updateDigest(messageDigest, data, options).digest(); + } + + /** + * Reads through a byte array and returns the digest for the data. + * + * @param data + * Data to digest treated as UTF-8 string + * @return the digest + * @since 1.11 + */ + public byte[] digest(final String data) { + return updateDigest(messageDigest, data).digest(); + } + + /** + * Reads through a byte array and returns the digest for the data. + * + * @param data + * Data to digest + * @return the digest as a hex string + * @since 1.11 + */ + public String digestAsHex(final byte[] data) { + return Hex.encodeHexString(digest(data)); + } + + /** + * Reads through a ByteBuffer and returns the digest for the data + * + * @param data + * Data to digest + * @return the digest as a hex string + * + * @since 1.11 + */ + public String digestAsHex(final ByteBuffer data) { + return Hex.encodeHexString(digest(data)); + } + + /** + * Reads through a File and returns the digest for the data + * + * @param data + * Data to digest + * @return the digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.11 + */ + public String digestAsHex(final File data) throws IOException { + return Hex.encodeHexString(digest(data)); + } + + /** + * Reads through an InputStream and returns the digest for the data + * + * @param data + * Data to digest + * @return the digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.11 + */ + public String digestAsHex(final InputStream data) throws IOException { + return Hex.encodeHexString(digest(data)); + } + + /** + * Reads through a File and returns the digest for the data + * + * @param data + * Data to digest + * @param options + * options How to open the file + * @return the digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.11 + */ + @RequiresApi(api = 26) + public String digestAsHex(final Path data, final OpenOption... options) throws IOException { + return Hex.encodeHexString(digest(data, options)); + } + + /** + * Reads through a byte array and returns the digest for the data. + * + * @param data + * Data to digest treated as UTF-8 string + * @return the digest as a hex string + * @since 1.11 + */ + public String digestAsHex(final String data) { + return Hex.encodeHexString(digest(data)); + } + + /** + * Returns the message digest instance. + * @return the message digest instance + * @since 1.11 + */ + public MessageDigest getMessageDigest() { + return messageDigest; + } + +} diff --git a/app/src/main/java/vendored/org/apache/commons/codec/digest/MessageDigestAlgorithms.java b/app/src/main/java/vendored/org/apache/commons/codec/digest/MessageDigestAlgorithms.java new file mode 100644 index 000000000..e5401f3ee --- /dev/null +++ b/app/src/main/java/vendored/org/apache/commons/codec/digest/MessageDigestAlgorithms.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package vendored.org.apache.commons.codec.digest; + +import java.security.MessageDigest; + +/** + * Standard {@link MessageDigest} algorithm names from the Java Cryptography Architecture Standard Algorithm Name + * Documentation. + *

+ * This class is immutable and thread-safe. + *

+ *

+ * Java 8 and up: SHA-224. + *

+ *

+ * Java 9 and up: SHA3-224, SHA3-256, SHA3-384, SHA3-512. + *

+ * + * @see + * Java 7 Cryptography Architecture Standard Algorithm Name Documentation + * @see + * Java 8 Cryptography Architecture Standard Algorithm Name Documentation + * @see + * Java 9 Cryptography Architecture Standard Algorithm Name Documentation + * @see + * Java 10 Cryptography Architecture Standard Algorithm Name Documentation + * @see + * Java 11 Cryptography Architecture Standard Algorithm Name Documentation + * @see + * Java 12 Cryptography Architecture Standard Algorithm Name Documentation + * @see + * Java 13 Cryptography Architecture Standard Algorithm Name Documentation + * + * @see FIPS PUB 180-4 + * @see FIPS PUB 202 + * @since 1.7 + */ +public class MessageDigestAlgorithms { + + /** + * The MD2 message digest algorithm defined in RFC 1319. + */ + public static final String MD2 = "MD2"; + + /** + * The MD5 message digest algorithm defined in RFC 1321. + */ + public static final String MD5 = "MD5"; + + /** + * The SHA-1 hash algorithm defined in the FIPS PUB 180-2. + */ + public static final String SHA_1 = "SHA-1"; + + /** + * The SHA-224 hash algorithm defined in the FIPS PUB 180-3. + *

+ * Present in Oracle Java 8. + *

+ * + * @since 1.11 + */ + public static final String SHA_224 = "SHA-224"; + + /** + * The SHA-256 hash algorithm defined in the FIPS PUB 180-2. + */ + public static final String SHA_256 = "SHA-256"; + + /** + * The SHA-384 hash algorithm defined in the FIPS PUB 180-2. + */ + public static final String SHA_384 = "SHA-384"; + + /** + * The SHA-512 hash algorithm defined in the FIPS PUB 180-2. + */ + public static final String SHA_512 = "SHA-512"; + + /** + * The SHA-512 hash algorithm defined in the FIPS PUB 180-4. + *

+ * Included starting in Oracle Java 9. + *

+ * + * @since 1.14 + */ + public static final String SHA_512_224 = "SHA-512/224"; + + /** + * The SHA-512 hash algorithm defined in the FIPS PUB 180-4. + *

+ * Included starting in Oracle Java 9. + *

+ * + * @since 1.14 + */ + public static final String SHA_512_256 = "SHA-512/256"; + + /** + * The SHA3-224 hash algorithm defined in the FIPS PUB 202. + *

+ * Included starting in Oracle Java 9. + *

+ * + * @since 1.11 + */ + public static final String SHA3_224 = "SHA3-224"; + + /** + * The SHA3-256 hash algorithm defined in the FIPS PUB 202. + *

+ * Included starting in Oracle Java 9. + *

+ * + * @since 1.11 + */ + public static final String SHA3_256 = "SHA3-256"; + + /** + * The SHA3-384 hash algorithm defined in the FIPS PUB 202. + *

+ * Included starting in Oracle Java 9. + *

+ * + * @since 1.11 + */ + public static final String SHA3_384 = "SHA3-384"; + + /** + * The SHA3-512 hash algorithm defined in the FIPS PUB 202. + *

+ * Included starting in Oracle Java 9. + *

+ * + * @since 1.11 + */ + public static final String SHA3_512 = "SHA3-512"; + + /** + * Gets all constant values defined in this class. + * + * @return all constant values defined in this class. + * @since 1.11 + */ + public static String[] values() { + // N.B. do not use a constant array here as that can be changed externally by accident or design + return new String[] { + MD2, MD5, SHA_1, SHA_224, SHA_256, SHA_384, + SHA_512, SHA_512_224, SHA_512_256, SHA3_224, SHA3_256, SHA3_384, SHA3_512 + }; + } + + private MessageDigestAlgorithms() { + // cannot be instantiated. + } + +} diff --git a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java index fe202a874..19a5bec6d 100644 --- a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java +++ b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java @@ -16,6 +16,7 @@ import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import java.io.File; +import java.io.IOException; import java.util.Date; import java.util.Random; import java.util.TimeZone; @@ -28,7 +29,12 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static vendored.org.apache.commons.codec.digest.MessageDigestAlgorithms.MD5; +import static vendored.org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA_256; +/** + * @see forced to vendor Apache Commons Codec + */ @RunWith(RobolectricTestRunner.class) @SuppressWarnings("LineLength") public class UtilsTest { @@ -204,7 +210,7 @@ public class UtilsTest { } @Test - public void testGetFileHexDigest() { + public void testGetFileHexDigest() throws IOException { File f = TestUtils.copyResourceToTempFile("largeRepo.xml"); assertEquals("df1754aa4b56c86c06d7842dfd02064f0781c1f740f489d3fc158bb541c8d197", Utils.getFileHexDigest(f, "sha256")); @@ -217,7 +223,25 @@ public class UtilsTest { f = TestUtils.copyResourceToTempFile("index.fdroid.2016-11-10.jar"); assertEquals("93bea45814fd8955cabb957e7a3f8790d6c568eaa16fa30425c2d26c60490bde", Utils.getFileHexDigest(f, "SHA-256")); + + // zero size file should have a stable hex digest file + assertEquals("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + Utils.getFileHexDigest(File.createTempFile("asdf", "asdf"), SHA_256)); + + assertNull(Utils.getFileHexDigest(new File("/kasdfkjasdhflkjasd"), SHA_256)); } + + @Test(expected = IllegalArgumentException.class) + public void testGetFileHexDigestBadAlgo() { + File f = TestUtils.copyResourceToTempFile("additional_repos.xml"); + assertNull(Utils.getFileHexDigest(f, "FAKE")); + } + + @Test(expected = NullPointerException.class) + public void testGetFileHexDigestNullFile() { + assertNull(Utils.getFileHexDigest(null, SHA_256)); + } + // TODO write tests that work with a Certificate @Test @@ -269,7 +293,7 @@ public class UtilsTest { d = v & 0xF; fdroidSig[j * 2 + 1] = (byte) (d >= 10 ? ('a' + d - 10) : ('0' + d)); } - String sig = Utils.hashBytes(fdroidSig, "md5"); + String sig = Utils.hashBytes(fdroidSig, MD5); assertEquals(sig, Utils.getsig(rawCertBytes)); PackageInfo packageInfo = new PackageInfo(); diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 2631fe63e..3852e9d4d 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -40,7 +40,7 @@ + value="org.fdroid.fdroid.Assert.*, vendored.org.apache.commons.codec.digest.MessageDigestAlgorithms.*, org.assertj.core.api.Assertions.*, org.junit.Assert.*, org.junit.Assume.*, org.junit.internal.matchers.ThrowableMessageMatcher.*, org.hamcrest.core.IsNot.*, org.hamcrest.CoreMatchers.*, org.hamcrest.Matchers.*, org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.*, org.springframework.boot.configurationprocessor.TestCompiler.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.Matchers.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*, org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo, androidx.test.espresso.Espresso.*, androidx.test.espresso.assertion.ViewAssertions.*, androidx.test.espresso.matcher.ViewMatchers.*, androidx.test.espresso.action.ViewActions.*" /> diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml index 7a5d76323..61aae2970 100644 --- a/config/checkstyle/suppressions.xml +++ b/config/checkstyle/suppressions.xml @@ -4,4 +4,5 @@ "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd"> + From 119086cf4aceda742b527bb450e23b8a8c3e2e7d Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 26 Jan 2022 11:33:40 +0100 Subject: [PATCH 5/7] replace Utils.toHexString with Hex.encodeHexString --- .../main/java/org/fdroid/fdroid/Utils.java | 23 ++----------------- .../installer/ApkSignatureVerifier.java | 8 +++++-- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index 4fc22574e..c5aa41abf 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -428,7 +428,7 @@ public final class Utils { * @see org.fdroid.fdroid.data.Apk#sig */ public static String getsig(byte[] rawCertBytes) { - return Utils.hashBytes(toHexString(rawCertBytes).getBytes(), "md5"); + return Utils.hashBytes(Hex.encodeHexString(rawCertBytes).getBytes(), "md5"); } /** @@ -490,7 +490,7 @@ public final class Utils { try { MessageDigest md = MessageDigest.getInstance(algo); byte[] hashBytes = md.digest(input); - String hash = toHexString(hashBytes); + String hash = Hex.encodeHexString(hashBytes); md.reset(); return hash; @@ -531,25 +531,6 @@ public final class Utils { return null; } - /** - * Computes the base 16 representation of the byte array argument. - * - * @param bytes an array of bytes. - * @return the bytes represented as a string of lowercase hexadecimal digits. - * @see source - */ - public static String toHexString(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] = HEX_LOOKUP_ARRAY[v >>> 4]; - hexChars[j * 2 + 1] = HEX_LOOKUP_ARRAY[v & 0x0F]; - } - return new String(hexChars); - } - - private static final char[] HEX_LOOKUP_ARRAY = "0123456789abcdef".toCharArray(); - public static int parseInt(String str, int fallback) { if (str == null || str.length() == 0) { return fallback; diff --git a/app/src/main/java/org/fdroid/fdroid/installer/ApkSignatureVerifier.java b/app/src/main/java/org/fdroid/fdroid/installer/ApkSignatureVerifier.java index 139449ce5..b518595ca 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/ApkSignatureVerifier.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/ApkSignatureVerifier.java @@ -33,10 +33,14 @@ import java.io.File; import java.io.IOException; import java.util.Arrays; +import vendored.org.apache.commons.codec.binary.Hex; + /** * NOTE: Silly Android API naming: APK signatures are actually certificates! * Thus, we are comparing certificates (including the public key) used to sign an APK, * not the actual APK signature. + * + * @see forced to vendor Apache Commons Codec */ class ApkSignatureVerifier { @@ -69,8 +73,8 @@ class ApkSignatureVerifier { } Utils.debugLog(TAG, "Signature mismatch!"); - Utils.debugLog(TAG, "APK sig: " + Utils.toHexString(getApkSignature(apkFile))); - Utils.debugLog(TAG, "F-Droid sig: " + Utils.toHexString(getFDroidSignature())); + Utils.debugLog(TAG, "APK sig: " + Hex.encodeHexString(getApkSignature(apkFile))); + Utils.debugLog(TAG, "F-Droid sig: " + Hex.encodeHexString(getFDroidSignature())); return false; } From 50f1b110a2903d8b3ececb6dd06d561bc04a6cf7 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 26 Jan 2022 13:37:34 +0100 Subject: [PATCH 6/7] implement Hasher.isFileMatchingHash() using commons-codec --- .../main/java/org/fdroid/fdroid/Hasher.java | 36 +--------------- .../main/java/org/fdroid/fdroid/Utils.java | 9 ++++ .../org/fdroid/fdroid/installer/ApkCache.java | 6 +-- .../installer/InstallManagerService.java | 6 ++- .../java/org/fdroid/fdroid/UtilsTest.java | 42 +++++++++++++++++++ 5 files changed, 59 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/Hasher.java b/app/src/main/java/org/fdroid/fdroid/Hasher.java index ade76c33f..0a05745d4 100644 --- a/app/src/main/java/org/fdroid/fdroid/Hasher.java +++ b/app/src/main/java/org/fdroid/fdroid/Hasher.java @@ -28,20 +28,14 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; -import java.util.Locale; public class Hasher { private MessageDigest digest; private File file; - private byte[] array; + private final byte[] array; private String hashCache; - public Hasher(String type, File f) throws NoSuchAlgorithmException { - init(type); - this.file = f; - } - public Hasher(String type, byte[] a) throws NoSuchAlgorithmException { init(type); this.array = a; @@ -84,34 +78,6 @@ public class Hasher { return hashCache; } - // Compare the calculated hash to another string, ignoring case, - // returning true if they are equal. The empty string and null are - // considered non-matching. - public boolean match(String otherHash) { - if (otherHash == null) { - return false; - } - if (hashCache == null) { - getHash(); - } - return hashCache.equals(otherHash.toLowerCase(Locale.ENGLISH)); - } - - /** - * Checks the file against the provided hash, returning whether it is a match. - */ - public static boolean isFileMatchingHash(File file, String hash, String hashType) { - if (!file.exists()) { - return false; - } - try { - Hasher hasher = new Hasher(hashType, file); - return hasher.match(hash); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - public static String hex(Certificate cert) { byte[] encoded; try { diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index c5aa41abf..c1d13c538 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -418,6 +418,15 @@ public final class Utils { return ret; } + /** + * Checks the file against the provided hash, returning whether it is a match. + */ + public static boolean isFileMatchingHash(File file, String hash, String hashType) { + if (file == null || !file.exists() || TextUtils.isEmpty(hash)) { + return false; + } + return hash.equals(getFileHexDigest(file, hashType)); + } /** * Get the fingerprint used to represent an APK signing key in F-Droid. diff --git a/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java b/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java index e7eaefd2f..24fff30b4 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java @@ -25,7 +25,7 @@ import android.content.pm.PackageInfo; import android.net.Uri; import org.apache.commons.io.FileUtils; -import org.fdroid.fdroid.Hasher; +import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.AppProvider; @@ -89,7 +89,7 @@ public class ApkCache { FileUtils.copyFile(apkFile, sanitizedApkFile); // verify copied file's hash with expected hash from Apk class - if (verifyHash && !Hasher.isFileMatchingHash(sanitizedApkFile, hash, hashType)) { + if (verifyHash && !Utils.isFileMatchingHash(sanitizedApkFile, hash, hashType)) { FileUtils.deleteQuietly(apkFile); throw new IOException(apkFile + " failed to verify!"); } @@ -137,7 +137,7 @@ public class ApkCache { */ public static boolean apkIsCached(File apkFile, Apk apkToCheck) { return apkFile.length() == apkToCheck.size && - Hasher.isFileMatchingHash(apkFile, apkToCheck.hash, apkToCheck.hashType); + Utils.isFileMatchingHash(apkFile, apkToCheck.hash, apkToCheck.hashType); } /** diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java index c9a2c7671..f79e02cc2 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java @@ -17,7 +17,6 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.WildcardFileFilter; import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.FDroidApp; -import org.fdroid.fdroid.Hasher; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.compat.PackageManagerCompat; import org.fdroid.fdroid.data.Apk; @@ -33,6 +32,8 @@ import java.io.IOException; import androidx.annotation.NonNull; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import static vendored.org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA_256; + /** * Manages the whole process when a background update triggers an install or the user * requests an APK to be installed. It handles checking whether the APK is cached, @@ -86,6 +87,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; * has a different name/description/summary, etc). * * @see APK Expansion Files + * @see forced to vendor Apache Commons Codec */ @SuppressWarnings("LineLength") public class InstallManagerService extends Service { @@ -281,7 +283,7 @@ public class InstallManagerService extends Service { + " to " + localApkUri); try { - if (Hasher.isFileMatchingHash(localFile, hash, "sha256")) { + if (Utils.isFileMatchingHash(localFile, hash, SHA_256)) { Utils.debugLog(TAG, "Installing OBB " + localFile + " to " + obbDestFile); FileUtils.forceMkdirParent(obbDestFile); FileUtils.copyFile(localFile, obbDestFile); diff --git a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java index 19a5bec6d..3d1242aaa 100644 --- a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java +++ b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java @@ -169,6 +169,48 @@ public class UtilsTest { Utils.formatFingerprint(context, "3082035e30820246a00302010202044c49cd00300d06092a864886f70d01010505003071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b73301e170d3130303732333137313032345a170d3337313230383137313032345a3071310b300906035504061302554b3110300e06035504081307556e6b6e6f776e3111300f0603550407130857657468657262793110300e060355040a1307556e6b6e6f776e3110300e060355040b1307556e6b6e6f776e311930170603550403131043696172616e2047756c746e69656b7330820122300d06092a864886f70d01010105000382010f003082010a028201010096d075e47c014e7822c89fd67f795d23203e2a8843f53ba4e6b1bf5f2fd0e225938267cfcae7fbf4fe596346afbaf4070fdb91f66fbcdf2348a3d92430502824f80517b156fab00809bdc8e631bfa9afd42d9045ab5fd6d28d9e140afc1300917b19b7c6c4df4a494cf1f7cb4a63c80d734265d735af9e4f09455f427aa65a53563f87b336ca2c19d244fcbba617ba0b19e56ed34afe0b253ab91e2fdb1271f1b9e3c3232027ed8862a112f0706e234cf236914b939bcf959821ecb2a6c18057e070de3428046d94b175e1d89bd795e535499a091f5bc65a79d539a8d43891ec504058acb28c08393b5718b57600a211e803f4a634e5c57f25b9b8c4422c6fd90203010001300d06092a864886f70d0101050500038201010008e4ef699e9807677ff56753da73efb2390d5ae2c17e4db691d5df7a7b60fc071ae509c5414be7d5da74df2811e83d3668c4a0b1abc84b9fa7d96b4cdf30bba68517ad2a93e233b042972ac0553a4801c9ebe07bf57ebe9a3b3d6d663965260e50f3b8f46db0531761e60340a2bddc3426098397fda54044a17e5244549f9869b460ca5e6e216b6f6a2db0580b480ca2afe6ec6b46eedacfa4aa45038809ece0c5978653d6c85f678e7f5a2156d1bedd8117751e64a4b0dcd140f3040b021821a8d93aed8d01ba36db6c82372211fed714d9a32607038cdfd565bd529ffc637212aaa2c224ef22b603eccefb5bf1e085c191d4b24fe742b17ab3f55d4e6f05ef")); } + @Test + public void testIsFileMatchingHash() { + Utils.isFileMatchingHash(null, null, null); + Utils.isFileMatchingHash(new File("/"), "", null); + + assertFalse(Utils.isFileMatchingHash(null, null, "")); + assertFalse(Utils.isFileMatchingHash(null, null, SHA_256)); + assertFalse(Utils.isFileMatchingHash(new File("/"), null, SHA_256)); + assertFalse(Utils.isFileMatchingHash(new File("/"), "", SHA_256)); + + assertTrue(Utils.isFileMatchingHash(TestUtils.copyResourceToTempFile("Norway_bouvet_europe_2.obf.zip"), + "6e8a584e004c6cd26d3822a04b0591e355dc5d07b5a3d0f8e309443f47ad1208", SHA_256)); + assertTrue(Utils.isFileMatchingHash(TestUtils.copyResourceToTempFile("install_history_all"), + "4ad118d4a600dcc104834635d248a89e337fc91b173163d646996b9c54d77372", SHA_256)); + assertFalse("wrong sha256 value", + Utils.isFileMatchingHash(TestUtils.copyResourceToTempFile("simpleIndex.jar"), + "6e8a584e004c6cd26d3822a04b0591e355dc5d07b5a3d0f8e309443f47ad1208", SHA_256)); + + File f = TestUtils.copyResourceToTempFile("additional_repos.xml"); + assertTrue(Utils.isFileMatchingHash(f, + "47ad2284d3042373e6280012cc10e9b82f75352db6d6d9bab1e06934b7b1dab7", SHA_256)); + assertFalse("uppercase fails", + Utils.isFileMatchingHash(f, + "47AD2284D3042373E6280012CC10E9B82F75352DB6D6D9BAB1E06934B7B1DAB7", SHA_256)); + assertFalse("one uppercase digit fails", + Utils.isFileMatchingHash(f, + "47Ad2284d3042373e6280012cc10e9b82f75352db6d6d9bab1e06934b7b1dab7", SHA_256)); + assertFalse("missing digit fails", + Utils.isFileMatchingHash(f, + "47ad2284d3042373e6280012cc10e9b82f75352db6d6d9bab1e06934b7b1dab", SHA_256)); + assertFalse("extra digit fails", + Utils.isFileMatchingHash(f, + "47ad2284d3042373e6280012cc10e9b82f75352db6d6d9bab1e06934b7b1dab71", SHA_256)); + assertFalse("all zeros fails", + Utils.isFileMatchingHash(f, + "0000000000000000000000000000000000000000000000000000000000000000", SHA_256)); + assertFalse("null fails", + Utils.isFileMatchingHash(f, null, SHA_256)); + assertFalse("empty string fails", + Utils.isFileMatchingHash(f, "", SHA_256)); + } + @Test public void testCalcFingerprintString() { // these should pass From 1d002c9f083233a25b667da5d4658a00f502334b Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 26 Jan 2022 13:54:57 +0100 Subject: [PATCH 7/7] replace Utils.hashBytes with DigestUtils.md5Hex --- app/src/main/java/org/fdroid/fdroid/Utils.java | 17 +---------------- .../test/java/org/fdroid/fdroid/UtilsTest.java | 4 ++-- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index c1d13c538..816be3fb2 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -437,7 +437,7 @@ public final class Utils { * @see org.fdroid.fdroid.data.Apk#sig */ public static String getsig(byte[] rawCertBytes) { - return Utils.hashBytes(Hex.encodeHexString(rawCertBytes).getBytes(), "md5"); + return DigestUtils.md5Hex(Hex.encodeHexString(rawCertBytes).getBytes()); } /** @@ -494,21 +494,6 @@ public final class Utils { Glide.with(context).load(app.getIconUrl(iv.getContext())).apply(iconRequestOptions).into(iv); } - // this is all new stuff being added - public static String hashBytes(byte[] input, String algo) { - try { - MessageDigest md = MessageDigest.getInstance(algo); - byte[] hashBytes = md.digest(input); - String hash = Hex.encodeHexString(hashBytes); - - md.reset(); - return hash; - } catch (NoSuchAlgorithmException e) { - Log.e(TAG, "Device does not support " + algo + " MessageDisgest algorithm"); - return null; - } - } - /** * Get the checksum hash of the file {@code file} using the algorithm in {@code hashAlgo}. * {@code file} must exist on the filesystem and {@code hashAlgo} must be supported diff --git a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java index 3d1242aaa..81fe733c1 100644 --- a/app/src/test/java/org/fdroid/fdroid/UtilsTest.java +++ b/app/src/test/java/org/fdroid/fdroid/UtilsTest.java @@ -23,13 +23,13 @@ import java.util.TimeZone; import androidx.loader.content.CursorLoader; import androidx.test.core.app.ApplicationProvider; +import vendored.org.apache.commons.codec.digest.DigestUtils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static vendored.org.apache.commons.codec.digest.MessageDigestAlgorithms.MD5; import static vendored.org.apache.commons.codec.digest.MessageDigestAlgorithms.SHA_256; /** @@ -335,7 +335,7 @@ public class UtilsTest { d = v & 0xF; fdroidSig[j * 2 + 1] = (byte) (d >= 10 ? ('a' + d - 10) : ('0' + d)); } - String sig = Utils.hashBytes(fdroidSig, MD5); + String sig = DigestUtils.md5Hex(fdroidSig); assertEquals(sig, Utils.getsig(rawCertBytes)); PackageInfo packageInfo = new PackageInfo();