From 640c458fdbabc72c4d9e6b47936980b3d71140a4 Mon Sep 17 00:00:00 2001 From: Kevin Gorham Date: Sat, 19 Sep 2020 03:35:10 -0400 Subject: [PATCH] Add cryto files to use in the absence of Java crypto libs. --- .../z/ecc/android/crypto/FallbackProvider.kt | 66 +++++++++++++++ .../cash/z/ecc/android/crypto/Pbkdf2Sha512.kt | 80 +++++++++++++++++++ .../z/ecc/android/crypto/Pbkdf2Sha512Test.kt | 54 +++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 lib/src/main/java/cash/z/ecc/android/crypto/FallbackProvider.kt create mode 100644 lib/src/main/java/cash/z/ecc/android/crypto/Pbkdf2Sha512.kt create mode 100644 lib/src/test/java/cash/z/ecc/android/crypto/Pbkdf2Sha512Test.kt diff --git a/lib/src/main/java/cash/z/ecc/android/crypto/FallbackProvider.kt b/lib/src/main/java/cash/z/ecc/android/crypto/FallbackProvider.kt new file mode 100644 index 0000000..88a2e06 --- /dev/null +++ b/lib/src/main/java/cash/z/ecc/android/crypto/FallbackProvider.kt @@ -0,0 +1,66 @@ +package cash.z.ecc.android.crypto + +import java.security.Provider +import java.security.spec.KeySpec +import javax.crypto.SecretKey +import javax.crypto.SecretKeyFactory +import javax.crypto.SecretKeyFactorySpi +import javax.crypto.spec.PBEKeySpec +import javax.crypto.spec.SecretKeySpec + +/** + * A provider to use in the event that the necessary cryptographic algorithm is not available in the + * service provider. This provides a bridge to a commonly used Java implementation that has been + * moderately adapted to Kotlin. + */ +class FallbackProvider : Provider( + "FallbackProvider", + 1.0, + "Provides a bridge to a default implementation of the PBKDF2WithHmacSHA512 algorithm" + + " to use when one is not already available on the device." +) { + override fun getService(type: String?, algorithm: String?): Service? { + return ServiceProvider().takeIf { + SecretKeyFactory::class.java.simpleName.equals(type, true) + && Pbkdf2KeyFactory.algorithm.equals(algorithm, true) + } + } + + inner class ServiceProvider : Provider.Service( + this@FallbackProvider, + SecretKeyFactory::class.java.simpleName, + Pbkdf2KeyFactory.algorithm, + ServiceProvider::class.java.simpleName, + null, + null + ) { + override fun newInstance(unused: Any?): Any { + return Pbkdf2KeyFactory() + } + } +} + +/** + * A simple interface to bridge to a fallback algorithm implementation. + * + * This service provider interface supplies the implementation of a secret-key factory for + * the Password-Based Key Derivation Function 2 (PBKDF2) with Hash-based Message Authentication Code + * (HMAC) and Secure Hash Algorithm (SHA-512). Most modern devices already have this algorithm + * available so this is used to bridge to a popular java implementation simply as a fallback. + */ +class Pbkdf2KeyFactory : SecretKeyFactorySpi() { + + override fun engineGenerateSecret(keySpec: KeySpec): SecretKey { + return (keySpec as PBEKeySpec).run { + SecretKeySpec(Pbkdf2Sha512.derive(password, salt, iterationCount, keyLength), algorithm) + } + } + + override fun engineGetKeySpec(s: SecretKey, p: Class<*>) = throw UnsupportedOperationException() + override fun engineTranslateKey(s: SecretKey?) = throw UnsupportedOperationException() + + companion object { + const val algorithm = "PBKDF2WithHmacSHA512" + } +} + diff --git a/lib/src/main/java/cash/z/ecc/android/crypto/Pbkdf2Sha512.kt b/lib/src/main/java/cash/z/ecc/android/crypto/Pbkdf2Sha512.kt new file mode 100644 index 0000000..8c22767 --- /dev/null +++ b/lib/src/main/java/cash/z/ecc/android/crypto/Pbkdf2Sha512.kt @@ -0,0 +1,80 @@ +package cash.z.ecc.android.crypto + +import java.io.ByteArrayOutputStream +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec +import kotlin.experimental.xor +import kotlin.math.ceil + +/** + * + * This is a clean-room implementation of PBKDF2 using RFC 2898 as a reference. + * + * + * RFC 2898: http://tools.ietf.org/html/rfc2898#section-5.2 + * + * + * This code passes all RFC 6070 test vectors: http://tools.ietf.org/html/rfc6070 + * + * + * http://cryptofreek.org/2012/11/29/pbkdf2-pure-java-implementation/

+ * Modified to use SHA-512 - Ken Sedgwick ken@bonsai.com + * Modified to for Kotlin - Kevin Gorham anothergmale@gmail.com + */ +object Pbkdf2Sha512 { + + /** + * Generate a derived key from the given parameters. + * + * @param p the password + * @param s the salt + * @param c the iteration count + * @param dkLen the key length in bits + */ + fun derive(p: CharArray, s: ByteArray, c: Int, dkLen: Int): ByteArray { + ByteArrayOutputStream().use { baos -> + val dkLenBytes = dkLen/8 + val pBytes = p.foldIndexed(ByteArray(p.size)) { i, acc, c -> + acc.apply { this[i] = c.toByte() } + } + val hLen = 20.0 + // note: dropped length check because its redundant, given the size of an int in kotlin + val l = ceil(dkLenBytes / hLen).toInt() + for (i in 1..l) { + F(pBytes, s, c, i).let { Tn -> + baos.write(Tn) + } + } + return ByteArray(dkLenBytes).apply { + System.arraycopy(baos.toByteArray(), 0, this, 0, size) + } + } + } + + private fun F(p: ByteArray, s: ByteArray, c: Int, i: Int): ByteArray { + val key = SecretKeySpec(p, "HmacSHA512") + val mac = Mac.getInstance(key.algorithm).apply { init(key) } + val bU = ByteArray(s.size + 4) + + // concat s and i into array bU w/o additional allocations + System.arraycopy(s, 0, bU, 0, s.size) + repeat(4) { j -> + bU[s.size + j] = (i shr (24 - 8 * j)).toByte() + } + + var uXor = mac.doFinal(bU) + var uLast = uXor + mac.reset() + + repeat(c - 1) { + val baU = mac.doFinal(uLast) + mac.reset() + uXor.forEachIndexed { k, b -> + uXor[k] = (b.xor(baU[k])) + } + uLast = baU + } + return uXor + } + +} diff --git a/lib/src/test/java/cash/z/ecc/android/crypto/Pbkdf2Sha512Test.kt b/lib/src/test/java/cash/z/ecc/android/crypto/Pbkdf2Sha512Test.kt new file mode 100644 index 0000000..a8f02d1 --- /dev/null +++ b/lib/src/test/java/cash/z/ecc/android/crypto/Pbkdf2Sha512Test.kt @@ -0,0 +1,54 @@ +package cash.z.ecc.android.crypto + +import cash.z.ecc.android.crypto.Pbkdf2Sha512 +import cash.z.ecc.android.bip39.toHex +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.data.forAll +import io.kotest.data.row +import io.kotest.matchers.shouldBe + +class Pbkdf2Sha512Test : BehaviorSpec({ + + Given("The test vectors") { + When("each provided input is used to derive keys") { + Then("it should match the expected output") { + forAll( + // modified from https://stackoverflow.com/a/19898265/178433 + row("passDATAb00AB7YxDTT", "saltKEYbcTcXHCBxtjD", 1, 512, "cbe6088ad4359af42e603c2a33760ef9d4017a7b2aad10af46f992c660a0b461ecb0dc2a79c2570941bea6a08d15d6887e79f32b132e1c134e9525eeddd744fa"), + row("passDATAb00AB7YxDTTl", "saltKEYbcTcXHCBxtjD2", 3, 512, "3660a4d16e9f8c2d467a051d95444d33148fcb8e595767f05f554487a1f97426b8dad9a83538144539b14b9274a819a8bbe59267cc51073746eef67b6042ed9d"), + row("passDATAb00AB7YxDTTlR", "saltKEYbcTcXHCBxtjD2P", 5, 512, "cc2fe2ab0ba48720dc1db53e850219fca6c5eada37023cb952e7f26d4ab707bcf7e25360e28db6cc97df1d6bf5fa49b2e0b1282fd6b05fed5766dca7bb306a2c"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE5", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJe", 7, 512, "3cba5dc465030d7d883df2d2eba356c2daf047605d873be576385acf50d57a574d4ccc2f65cfd63d04c6746d553605c7a1eafbadb86fde0600c6a0fefab076c3"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJem", 11, 512, "22110678bafc2672df890e9e54f4a8ca4cd4b92894a36003f293de1209497e8c4b1ab7a0e5da5868e1398787a3d3dd7a54d3ef0912bcf2322dd0521cd342c156"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57U", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemk", 13, 512, "726a143f2843b01b3074351842db496f102ca333e3eef51ae262984812cc133e57d61c89d90f455d64555b38f7a8f5dbf74f2ab1f5e3bd30eda32103d76365cc"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57Un4u12D2YD7oOPpiEvCDYvntXEe4NNPLCnGGeJArbYDEu6xDoCfWH6kbuV6awi0", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemkURWoqHusIeVB8Il91NjiCGQacPUu9qTFaShLbKG0Yj4RCMV56WPj7E14EMpbxy", 17, 512, "b14ac88b22ca14b4a036159b9d671e542702ec07239108ea756040a7e189c6d4680e7875fc92849d853c93e9a89bf232a08fbd0d6e770a5b78ff6be5fd272d64"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57Un4u12D2YD7oOPpiEvCDYvntXEe4NNPLCnGGeJArbYDEu6xDoCfWH6kbuV6awi04", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemkURWoqHusIeVB8Il91NjiCGQacPUu9qTFaShLbKG0Yj4RCMV56WPj7E14EMpbxy6", 19, 512, "9f6b95569376f14c9b716741297b0f64e9ea46b837c7a4c2831d3c9f7a94633aa4eda057916ae03c09030aed2c6dd6203172e257ab08b98aa1ce60fe90a7a18f"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57Un4u12D2YD7oOPpiEvCDYvntXEe4NNPLCnGGeJArbYDEu6xDoCfWH6kbuV6awi04U", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemkURWoqHusIeVB8Il91NjiCGQacPUu9qTFaShLbKG0Yj4RCMV56WPj7E14EMpbxy6P", 23, 512, "92c92d9ffe4242ed0691a8b704834621e909e2a03c101b4f90e098909039b819f3bcb55f08058fa96412d17c2e2b0ad52b095f782fb2969c50ff1e1262844cf9"), + row("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", 29, 512, "5edc3d6649fa05c07622dede976997afe683f8b489d996509e2bf9421cd81f49b7bd38e78ad7ccad0a2a9070710ad451da7b6f5b207a0ee17c14ad2054bf492a"), + row("passDATAb00AB7YxDTT", "saltKEYbcTcXHCBxtjD", 31, 504, "15530800da88a0776a812937eb2afeea4a2e7ecad633a918f1024688f73c5721d8bfcaa87f253cf50b9181ab3bb28043e13b1ce859f71d002674806bab0547"), + row("passDATAb00AB7YxDTTl", "saltKEYbcTcXHCBxtjD2", 37, 504, "6a71211a3b59e4b76fe962e17c2db6232a84a10edc043807831992665ff9d0b9cc76c6f5dc84297050bdd026e05144e3e651b3f8b4108bb050e576ba0b9440"), + row("passDATAb00AB7YxDTTlR", "saltKEYbcTcXHCBxtjD2P", 41, 504, "613a19696be76eb92a705b9a2fe6eb12cab31086c9b2778b8b83fc7f40cc3a02b39b3b17cbd0c97938be2e6e8d6f6bf73afbe7dea8cffcdd4e4bc6853f4e40"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE5", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJe", 43, 504, "e0d5567fb5f45381284d7d67a3386f943e14b1af2766b675cdd988614e40ffc012b9f320b5e33d3f6aea8af3fcd2f1077b4082e70414750af2b1b3bbf5948b"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJem", 47, 504, "c92ce4dceb97d3411e7a6ccd21d143d6a0f830b3d6e2ebe8dd6ff5aaac8d879bb7cda02dd652471243cb30e3bef8213dc4b6e4e9e2623af9702d67c30ea8ed"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57U", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemk", 53, 504, "40de5197dc99cf3c788ba10ad93fe8213584cc2c9304fce1ad2c0df261e28aac6769bebf7b19f4ca5b9758ad97c193a31a89faa80f1a0e75c8347da134f4c6"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57Un4u12D2YD7oOPpiEvCDYvntXEe4NNPLCnGGeJArbYDEu6xDoCfWH6kbuV6awi0", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemkURWoqHusIeVB8Il91NjiCGQacPUu9qTFaShLbKG0Yj4RCMV56WPj7E14EMpbxy", 61, 504, "d2e627cbe497af8eba3c2d22bbb765c14b065259d3147f58e2d567236aebd0c9a2b00f4cf2d54f3f8235ee02d3541d4c6f7240f5fd9a47a94ce914a648c016"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57Un4u12D2YD7oOPpiEvCDYvntXEe4NNPLCnGGeJArbYDEu6xDoCfWH6kbuV6awi04", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemkURWoqHusIeVB8Il91NjiCGQacPUu9qTFaShLbKG0Yj4RCMV56WPj7E14EMpbxy6", 67, 504, "cc51a623e2a197a8b009815a6d4ebebaaa247beb7a0643ea2b77909ff10b4c6d85bf080e55f2954ae003a1cfb78fd940fedfd67fe8ed4849e2bc1ae42ec055"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57Un4u12D2YD7oOPpiEvCDYvntXEe4NNPLCnGGeJArbYDEu6xDoCfWH6kbuV6awi04U", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemkURWoqHusIeVB8Il91NjiCGQacPUu9qTFaShLbKG0Yj4RCMV56WPj7E14EMpbxy6P", 71, 504, "f8e55259f1d1d884d0ffbd1d22bc7894c53e2a313131499ba8f35faeda6208a80c16ac93f5c0b79c3a1575d92f6a833ae7036d52be926e6468637c28222cd7"), + row("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", 73, 504, "d5ef8859566cabceb37b6f4a91e54a36067084bed91d9ccbb4d1e65942764cc5ff45304a4788f1a181e4415df2104f299aaadaed25392b74d5ecdf1af09c10"), + row("passDATAb00AB7YxDTT", "saltKEYbcTcXHCBxtjD", 79, 520, "3b9359b5639de3f8ec4009491b5fafe764548794c87f44a9fd6a7b9364522bee36b6b71819b71e9130dc6df1db6eba29133393762d9d89f68dd2d5d9d61488937e"), + row("passDATAb00AB7YxDTTl", "saltKEYbcTcXHCBxtjD2", 83, 520, "8ec068d1a5ad8aec6ea95aab0b4545e86adeb940bfa71c9b6e8969cd70239ec60020137c8094cf466d2129f98bc55b53077e0befb72615f0fe38554cb22f2cf455"), + row("passDATAb00AB7YxDTTlR", "saltKEYbcTcXHCBxtjD2P", 89, 520, "c882fda77fec48a78af3393a27cdcda40f8392ac5997ed150e45cc501dbcb1a4fdb770556f12f6c7c22bd8d111051bb6a9a260cff821f2cb5902ea6a6536338cf9"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE5", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJe", 97, 520, "d641b7852c0f5ad0bba73b155722bd9f0a6142765d0719eaabc36d25d7dc0a10edb2511d463748e611349a6c71b9b7f5dea5e445c1c98afc209387d6786970d21c"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJem", 101, 520, "d44e4dae8a7a5223898f419c18645191d57748bfdeef5b0f49bc36b5efa3611c76e82f097de834de3294bbad9f0a8071c0a09587bf748ed04118706f384ee87679"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57U", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemk", 103, 520, "2dbd66db3e0b9de948e5a76821a246bb1b03d5da68d1c1c0f7c6dbc41d5716caff82ac844d8107febb96ae9bae3958ade57528e27e53dab024263701ea432b9ed4"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57Un4u12D2YD7oOPpiEvCDYvntXEe4NNPLCnGGeJArbYDEu6xDoCfWH6kbuV6awi0", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemkURWoqHusIeVB8Il91NjiCGQacPUu9qTFaShLbKG0Yj4RCMV56WPj7E14EMpbxy", 107, 520, "2186bb78640371f3912d8ce507a4323c903608ec54b85ecc43608eafe52ee1f403f3e68e09a150599ca9f70097bc51f232d6449586a70fc5b7b0a21c629110563d"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57Un4u12D2YD7oOPpiEvCDYvntXEe4NNPLCnGGeJArbYDEu6xDoCfWH6kbuV6awi04", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemkURWoqHusIeVB8Il91NjiCGQacPUu9qTFaShLbKG0Yj4RCMV56WPj7E14EMpbxy6", 109, 520, "c512aa138dd99b1a785c3ec048dddd4eb569d7f9eb7f206b0544746e266f214fdcc4f5d5ba0869140010bdce517a550c58b527439dc40463f9c7fba7e2cbbc8820"), + row("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57Un4u12D2YD7oOPpiEvCDYvntXEe4NNPLCnGGeJArbYDEu6xDoCfWH6kbuV6awi04U", "saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemkURWoqHusIeVB8Il91NjiCGQacPUu9qTFaShLbKG0Yj4RCMV56WPj7E14EMpbxy6P", 113, 520, "e4c2be8f5cad779f90f54bec52888d6a1684f55d5145103515981217cc6609a039a86a41b3d22bae22f9a6687a605ae5c9e9dc411d83ba892f69af608b37fb89e8"), + row("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", 127, 520, "bb344a5712d07c4c49dfb9f77e44c5b4c29406c78c84214b07defb36a7898ae7a96c6cfeaf8d753b4bde382c4e48f247a90c17df79726228e2fed11c40b98e2648") + ) { password: String, salt: String, count: Int, length: Int, expected: String -> + val result = Pbkdf2Sha512.derive(password.toCharArray(), salt.toByteArray(), count, length) + result.toHex() shouldBe expected + } + } + } + } +})