From 4f9ebc663a0b90be1bd75cb8b8a71edf2b178a2f Mon Sep 17 00:00:00 2001 From: Pavlos Tzianos Date: Mon, 18 Nov 2019 00:37:34 +0100 Subject: [PATCH] Refactor android client to use wallet libraries --- android-client/app/build.gradle | 42 +- .../main/java/org/hermes/StorageBackend.kt | 2 +- .../org/hermes/activities/MnemonicActivity.kt | 2 + .../activities/MnemonicSetupActivity.kt | 2 + .../main/java/org/hermes/collections/set.kt | 44 - .../java/org/hermes/crypto/ECDSASignature.kt | 84 - .../org/hermes/crypto/SecP256K1PrivKey.kt | 146 -- .../java/org/hermes/crypto/SecP256K1PubKey.kt | 70 - .../java/org/hermes/crypto/Secp256K1Curve.kt | 76 - .../main/java/org/hermes/crypto/algorithms.kt | 7 - .../src/main/java/org/hermes/crypto/hash.kt | 97 - .../main/java/org/hermes/di/HermesModule.kt | 13 +- .../src/main/java/org/hermes/di/IOTAModule.kt | 2 +- .../fragments/NavigationDrawerFragment.kt | 7 +- .../app/src/main/java/org/hermes/hd/BIP32.kt | 348 --- .../app/src/main/java/org/hermes/hd/BIP39.kt | 108 - .../app/src/main/java/org/hermes/hd/BIP44.kt | 105 - .../app/src/main/java/org/hermes/hd/Base58.kt | 124 - .../java/org/hermes/hd/BaseECDSASigner.kt | 44 - .../app/src/main/java/org/hermes/hd/SLIP44.kt | 583 ----- .../app/src/main/java/org/hermes/hd/btc.kt | 67 - .../app/src/main/java/org/hermes/hd/eth.kt | 40 - .../app/src/main/java/org/hermes/hd/iota.kt | 158 -- .../src/main/java/org/hermes/hd/wordlist.kt | 2061 ----------------- .../app/src/main/java/org/hermes/iota/seed.kt | 59 - .../src/main/java/org/hermes/iota/trytes.kt | 629 ----- .../java/org/hermes/ledgers/IOTAConnector.kt | 24 +- .../hermes/repositories/CryptoRepository.kt | 10 +- .../org/hermes/sensors/StressTestingSensor.kt | 2 +- .../src/main/java/org/hermes/utils/arrays.kt | 2 +- .../app/src/main/java/org/hermes/utils/ktx.kt | 2 +- .../src/main/java/org/hermes/utils/strings.kt | 31 +- .../src/main/java/org/hermes/utils/toolbar.kt | 2 +- 33 files changed, 39 insertions(+), 4954 deletions(-) create mode 100644 android-client/app/src/main/java/org/hermes/activities/MnemonicActivity.kt create mode 100644 android-client/app/src/main/java/org/hermes/activities/MnemonicSetupActivity.kt delete mode 100644 android-client/app/src/main/java/org/hermes/collections/set.kt delete mode 100644 android-client/app/src/main/java/org/hermes/crypto/ECDSASignature.kt delete mode 100644 android-client/app/src/main/java/org/hermes/crypto/SecP256K1PrivKey.kt delete mode 100644 android-client/app/src/main/java/org/hermes/crypto/SecP256K1PubKey.kt delete mode 100644 android-client/app/src/main/java/org/hermes/crypto/Secp256K1Curve.kt delete mode 100644 android-client/app/src/main/java/org/hermes/crypto/algorithms.kt delete mode 100644 android-client/app/src/main/java/org/hermes/crypto/hash.kt delete mode 100644 android-client/app/src/main/java/org/hermes/hd/BIP32.kt delete mode 100644 android-client/app/src/main/java/org/hermes/hd/BIP39.kt delete mode 100644 android-client/app/src/main/java/org/hermes/hd/BIP44.kt delete mode 100644 android-client/app/src/main/java/org/hermes/hd/Base58.kt delete mode 100644 android-client/app/src/main/java/org/hermes/hd/BaseECDSASigner.kt delete mode 100644 android-client/app/src/main/java/org/hermes/hd/SLIP44.kt delete mode 100644 android-client/app/src/main/java/org/hermes/hd/btc.kt delete mode 100644 android-client/app/src/main/java/org/hermes/hd/eth.kt delete mode 100644 android-client/app/src/main/java/org/hermes/hd/iota.kt delete mode 100644 android-client/app/src/main/java/org/hermes/hd/wordlist.kt delete mode 100644 android-client/app/src/main/java/org/hermes/iota/seed.kt delete mode 100644 android-client/app/src/main/java/org/hermes/iota/trytes.kt diff --git a/android-client/app/build.gradle b/android-client/app/build.gradle index 61a7512..61d6e80 100644 --- a/android-client/app/build.gradle +++ b/android-client/app/build.gradle @@ -17,9 +17,6 @@ android { lintOptions { abortOnError false } - kotlinOptions { - jvmTarget = '1.8' - } defaultConfig { applicationId "org.hermes" minSdkVersion 24 @@ -56,44 +53,9 @@ android { } } -//task buildJnis(type: Exec) { -// def res = commandLine '/bin/bash', '-c', -// 'git submodule update --init --recursive && ' + -// 'cd ../../entangled && ' + -// 'git status | head -n 1 | sed \'s/On branch //\'' -// -//// For the time being we just use the commit that the submodule is pointed to -//// we are not going to be updating the repo every time we build the JNI libs. -//// if (res.toString() != 'develop') { -//// println 'Not in develop branch' -//// commandLine '/bin/bash', '-c', 'cd ../../entangled && git reset --hard && git checkout develop' -//// } -// Properties properties = new Properties() -// properties.load(project.rootProject.file('local.properties').newDataInputStream()) -// def sdkDir = properties.getProperty('sdk.dir', '') -// def ndkDir = properties.getProperty('ndk.dir', '') -// def cpu_archs = 'armeabi-v7a,arm64-v8a,x86,x86_64' -// def bazel_env_vars = "ANDROID_HOME=${sdkDir} ANDROID_NDK_HOME=${ndkDir}" -// -// println """ -//============================= -// -//Android SDK Path: ${sdkDir} -//Android NDK Path: ${ndkDir} -//entangled repository is on branch: ${res.toString()} -// -//============================= -//""" -// -// commandLine '/bin/bash', '-c', 'cd ../../entangled && ' + -// "${bazel_env_vars} bazel build --fat_apk_cpu='${cpu_archs}' --copt=-Ofast //mobile/android:dummy && " + -// 'rm -rf ../android-client/build/jni &&' + -// 'echo A | unzip bazel-bin/mobile/android/dummy.apk -d ../android-client/build/jni && ' + -// 'cp -r ../android-client/build/jni/lib/ ../android-client/app/src/main/jniLibs' -//} - dependencies { // TODO: Split all the version numbers into the android-client/build.gradle + implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' @@ -124,7 +86,7 @@ dependencies { kapt 'com.google.dagger:dagger-android-support:2.22.1' kapt 'com.google.dagger:dagger-android-processor:2.22.1' kapt 'com.google.dagger:dagger-compiler:2.22.1' - max23Api files('libs/jota-android-legacy-1.0.0-beta5.jar') +// max23Api files('libs/jota-android-legacy-1.0.0-beta5.jar') min24Api 'com.github.iotaledger:iota-java:1.0.0-beta5' testImplementation 'androidx.arch.core:core-testing:2.1.0' testImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/android-client/app/src/main/java/org/hermes/StorageBackend.kt b/android-client/app/src/main/java/org/hermes/StorageBackend.kt index 1fb5bc2..aff69d4 100644 --- a/android-client/app/src/main/java/org/hermes/StorageBackend.kt +++ b/android-client/app/src/main/java/org/hermes/StorageBackend.kt @@ -3,4 +3,4 @@ package org.hermes enum class StorageBackend (val network: String) { IOTA("IOTA"), STORJ("STORJ"), -} \ No newline at end of file +} diff --git a/android-client/app/src/main/java/org/hermes/activities/MnemonicActivity.kt b/android-client/app/src/main/java/org/hermes/activities/MnemonicActivity.kt new file mode 100644 index 0000000..f2de173 --- /dev/null +++ b/android-client/app/src/main/java/org/hermes/activities/MnemonicActivity.kt @@ -0,0 +1,2 @@ +package org.hermes.activities + diff --git a/android-client/app/src/main/java/org/hermes/activities/MnemonicSetupActivity.kt b/android-client/app/src/main/java/org/hermes/activities/MnemonicSetupActivity.kt new file mode 100644 index 0000000..f2de173 --- /dev/null +++ b/android-client/app/src/main/java/org/hermes/activities/MnemonicSetupActivity.kt @@ -0,0 +1,2 @@ +package org.hermes.activities + diff --git a/android-client/app/src/main/java/org/hermes/collections/set.kt b/android-client/app/src/main/java/org/hermes/collections/set.kt deleted file mode 100644 index 9d1e1da..0000000 --- a/android-client/app/src/main/java/org/hermes/collections/set.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.hermes.collections - -/** - * A HashSet that once created can not be modified. - */ -open class ImmutableHashSet(collection: Collection) { - - private val hashSet: HashSet = HashSet(collection) - - fun contains(elem: E): Boolean { - return hashSet.contains(elem) - } - - fun asIterable(): Iterable { - return hashSet.asIterable() - } - - fun asSequence(): Sequence { - return hashSet.asSequence() - } - - operator fun iterator(): Iterator { - return this.hashSet.iterator() - } - - fun size(): Int { - return this.hashSet.size - } -} - -/** - * An immutable hash set that also allows the user to use it as an array. - */ -class OrderedImmutableHashSet(al: List): ImmutableHashSet(al) { - private val orderedList: List = al - - operator fun get(i: Int): E { - return orderedList[i] - } - - fun indexOf(elem: E): Int { - return orderedList.indexOf(elem) - } -} \ No newline at end of file diff --git a/android-client/app/src/main/java/org/hermes/crypto/ECDSASignature.kt b/android-client/app/src/main/java/org/hermes/crypto/ECDSASignature.kt deleted file mode 100644 index ae411d7..0000000 --- a/android-client/app/src/main/java/org/hermes/crypto/ECDSASignature.kt +++ /dev/null @@ -1,84 +0,0 @@ -package org.hermes.crypto - -import java.lang.Exception -import java.math.BigInteger -import org.bouncycastle.util.encoders.Base64 -import org.bouncycastle.util.encoders.Hex - - -class NoEvenBit: Exception() - -/** - * An ECDSA Signature. - * Code modified from: org.web3j.crypto.ECDSASignature - */ -class ECDSASignature(val r: BigInteger, val s: BigInteger, val rY: BigInteger? = null) { - - val TWO = BigInteger(byteArrayOf(2.toByte())) - val vb: Byte? - val rb: ByteArray - val sb: ByteArray - val canonicalSb: ByteArray - - init { - val zeroByte = 0.toByte() - val normalizeByteArray = fun(ar: ByteArray): ByteArray { - val offset = if (ar[0] == zeroByte && ar.size > 32) 1 else 0 - return ByteArray(32) { i -> - when { - i < ar.size - offset -> ar[i + offset] - else -> 0.toByte() - } - } - } - rb = normalizeByteArray(r.toByteArray()) - sb = normalizeByteArray(s.toByteArray()) - canonicalSb = if (s.multiply(TWO) < Secp256K1Curve.n) sb - else normalizeByteArray(Secp256K1Curve.X9ECParameters.n.subtract(s).toByteArray()) - if (rY == null) vb = null - else { - val yMod2 = rY.mod(TWO).toInt() - val lessThanHalf = if (s.multiply(TWO) < Secp256K1Curve.n) 0 else 1 - vb = (27 + yMod2.xor(lessThanHalf)).toByte() - } - } - - /** - * @return true if the S component is "low", that means it is below - * {@link Secp256K1Curve#halfCurveOrder}. See - * - * BIP62. - */ - fun isCanonical(): Boolean = s.compareTo(Secp256K1Curve.halfCurveOrder) <= 0 - - /** - * Puts both integers in one continuous byte array - */ - fun toBinaryFormat(): ByteArray { - val completeArray = ByteArray(64) - System.arraycopy(r.toByteArray(), 0, completeArray, 0, 32) - System.arraycopy(s.toByteArray(), 0, completeArray, 32, 32) - return completeArray - } - - /** - * Returns the ECDSA signature as a hexed string - */ - fun toHexedBinary(): String = Hex.toHexString(toBinaryFormat()) - //DER encoding - // - //For reference, here is how to encode signatures correctly in DER format. - // - //0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash-type] - // - // total-length: 1-byte length descriptor of everything that follows, excluding the sighash byte. - // R-length: 1-byte length descriptor of the R value that follows. - // R: arbitrary-length big-endian encoded R value. It cannot start with any 0x00 bytes, unless the first byte that follows is 0x80 or higher, in which case a single 0x00 is required. - // S-length: 1-byte length descriptor of the S value that follows. - // S: arbitrary-length big-endian encoded S value. The same rules apply as for R. - // sighash-type: 1-byte hashtype flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed). - // - //This is already enforced by the reference client as of version 0.8.0 (only as relay policy, not as a consensus rule). - // - //This rule, combined with the low S requirement above, results in S-length being at most 32 (and R-length at most 33), and the total signature size being at most 72 bytes (and on average 71.494 bytes). -} \ No newline at end of file diff --git a/android-client/app/src/main/java/org/hermes/crypto/SecP256K1PrivKey.kt b/android-client/app/src/main/java/org/hermes/crypto/SecP256K1PrivKey.kt deleted file mode 100644 index c3a5990..0000000 --- a/android-client/app/src/main/java/org/hermes/crypto/SecP256K1PrivKey.kt +++ /dev/null @@ -1,146 +0,0 @@ -package org.hermes.crypto - -import java.math.BigInteger -import java.security.MessageDigest -import java.security.PrivateKey -import java.security.SecureRandom -import java.security.interfaces.ECPrivateKey -import java.util.* - -import kotlinx.io.IOException -import kotlinx.io.StringWriter - -import org.bouncycastle.asn1.ASN1Object -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo -import org.bouncycastle.asn1.x509.AlgorithmIdentifier -import org.bouncycastle.asn1.x9.X9ObjectIdentifiers -import org.bouncycastle.crypto.Digest -import org.bouncycastle.crypto.digests.SHA256Digest -import org.bouncycastle.crypto.params.ECDomainParameters -import org.bouncycastle.crypto.signers.DSAKCalculator -import org.bouncycastle.crypto.signers.HMacDSAKCalculator -import org.bouncycastle.math.ec.ECMultiplier -import org.bouncycastle.math.ec.ECPoint -import org.bouncycastle.math.ec.FixedPointCombMultiplier -import org.bouncycastle.util.encoders.Hex -import org.bouncycastle.util.io.pem.PemObject -import org.bouncycastle.util.io.pem.PemWriter - -import org.hermes.hd.Base58 -import java.security.spec.ECParameterSpec - - -open class SecP256K1PrivKey : PrivateKey, ECPrivateKey { - companion object { - fun random(): SecP256K1PrivKey { - return SecP256K1PrivKey(key = BigInteger(256, SecureRandom.getInstance("SHA1PRNG"))) - } - - fun validateWIFCheckSum(wifStr: String): Boolean { - val extendedCheckSummedBytes = Base58.decode(wifStr) - val extendedChecksum = extendedCheckSummedBytes.dropLast(4).toByteArray() - val digest = MessageDigest.getInstance("SHA-256") - val twiceHashed = digest.digest(digest.digest(extendedChecksum)) - return Arrays.equals( - twiceHashed.take(4).toByteArray(), - extendedCheckSummedBytes.takeLast(4).toByteArray() - ) - } - } - - constructor(key: BigInteger) { - value = key.mod(Secp256K1Curve.X9ECParameters.n) - } - - constructor(hexStr: String) { - value = BigInteger(Hex.decode(if (hexStr.startsWith("0x")) hexStr.drop(2) else hexStr)) - } - - // Make sure that the value of the key is not larger than the order of the group - val value: BigInteger - - /** - * Signs a message according to RFC 6979 with a deterministic K. - * The message is not check-summed, whatever bytes are fed to the function will be the ones - * that will be signed. - * Modified version of: @see ECDSASigner.java - */ - fun rawSign(message: ByteArray): Triple { - val ec: ECDomainParameters = Secp256K1Curve.ecDomainParameters - val n: BigInteger = ec.n - val log2n = n.bitLength() - val messageBitLength = message.size * 8 - val G = ec.g - // Convert message to integer - var e = BigInteger(1, message) - if (log2n < messageBitLength) - e = e.shiftRight(messageBitLength - log2n) - val digest: Digest = SHA256Digest() - val kCalculator: DSAKCalculator = HMacDSAKCalculator(digest).apply { - init(n, value, message) - } - val basePointMultiplier: ECMultiplier = FixedPointCombMultiplier() - var r: BigInteger - var rY: BigInteger - var s: BigInteger - var k: BigInteger - do { // generate s - do { // generate r - k = kCalculator.nextK() - val p: ECPoint = basePointMultiplier.multiply(G, k).normalize() - r = p.affineXCoord.toBigInteger().mod(n) - rY = p.affineYCoord.toBigInteger().mod(n) - } - while (r.equals(BigInteger.ZERO)) - s = k.modInverse(n).multiply(e.add(value.multiply(r))).mod(n) - } - while (s.equals(BigInteger.ZERO)) - return Triple(r, s, rY) - } - - /** - * Produces a deterministic signature of the checksum based on RFC-6979. - * Default checksum algorithm is SHA-256. - */ - fun sign(message: ByteArray, digest: MessageDigest = MessageDigest.getInstance("SHA-256")): ECDSASignature { - val checksum = digest.digest(message) - val (r, s, rY) = rawSign(checksum) - return ECDSASignature(r, s, rY) - } - - override fun getAlgorithm(): String = "EC" - - override fun getEncoded(): ByteArray? { - val params = Secp256K1Curve.X962Parameters - val orderBitLength: Int = Secp256K1Curve.n.bitLength() - val keyStructure = org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, value, params) - val stringWriter = StringWriter() - val pemWriter = PemWriter(stringWriter) - return try { - val info = PrivateKeyInfo(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure) - val asn1Object = info.parsePrivateKey() as ASN1Object - pemWriter.writeObject(PemObject ("EC PRIVATE KEY", asn1Object.getEncoded("DER"))) - pemWriter.flush() - pemWriter.close() - stringWriter.toString().toByteArray() - } catch (e: IOException) { - null - } - } - - /** - * Export the private key using the Wallet Import Format. - */ - override fun toString(): String = Base58.toBase58String( - Hex.decode("80") + value.toByteArray(), - appendChecksum = true - ) - - override fun getFormat(): String = "PKCS#8" - - open val public: SecP256K1PubKey by lazy { SecP256K1PubKey(this) } - - override fun getParams(): ECParameterSpec = Secp256K1Curve.ecParameterSpec - - override fun getS(): BigInteger = value -} diff --git a/android-client/app/src/main/java/org/hermes/crypto/SecP256K1PubKey.kt b/android-client/app/src/main/java/org/hermes/crypto/SecP256K1PubKey.kt deleted file mode 100644 index 4d7c4b7..0000000 --- a/android-client/app/src/main/java/org/hermes/crypto/SecP256K1PubKey.kt +++ /dev/null @@ -1,70 +0,0 @@ -package org.hermes.crypto - -import java.math.BigInteger -import java.security.PublicKey -import java.security.interfaces.ECPublicKey -import java.security.spec.ECParameterSpec - -import org.bouncycastle.math.ec.ECPoint -import org.bouncycastle.math.ec.FixedPointCombMultiplier -import org.bouncycastle.util.encoders.Hex - - -open class SecP256K1PubKey(val x: BigInteger, val y: BigInteger): PublicKey, ECPublicKey { - companion object { - fun fromBitcoinPubKey(pubKey: String): SecP256K1PubKey { - if (!pubKey.startsWith("04")) throw Exception("Not a Bitcoin pub key hex") - return SecP256K1PubKey( - x = BigInteger(Hex.decode(pubKey.drop(2).dropLast(64))), - y = BigInteger(Hex.decode(pubKey.drop(2 + 64)))) - } - } - - // TODO: Make sure that X is on the SecP256K1 curve - - // Store public key in a form that is compatible with the standard Java libraries - override fun getW(): java.security.spec.ECPoint = java.security.spec.ECPoint(x, y) - - // Store public key in a form that is compatible with the Bouncy Castle internal data structures - val bcPoint: ECPoint = Secp256K1Curve.ecDomainParameters.curve.createPoint(x, y) - - // The encoded field as a Hex string - val encodedHex: String by lazy { Hex.toHexString(encoded) } - - constructor(publicKeyECPoint: ECPoint): this( - publicKeyECPoint.affineXCoord.toBigInteger(), - publicKeyECPoint.affineYCoord.toBigInteger()) - - constructor(privateKey: SecP256K1PrivKey): - this( - FixedPointCombMultiplier() - .multiply( - Secp256K1Curve.X9ECParameters.g, - privateKey.value) - .normalize() - ) - - override fun getAlgorithm(): String = "SECP256K1" - - /** - * Encodes the public key in a standard encoded form: - * one byte at the beginning (0x04), the affine x coordinate as - * a big endian integer byte array and the affine y coordinate - * as a big endian integer byte array. - */ - override fun getEncoded(): ByteArray { - val xB = x.toByteArray() - val yB = y.toByteArray() - val xOffset = if (xB[0] == 0.toByte()) 1 else 0 - val yOffset = if (yB[0] == 0.toByte()) 1 else 0 - val encodedByteArray = ByteArray(x.toByteArray().size + y.toByteArray().size + 1 - xOffset - yOffset) - encodedByteArray[0] = 4.toByte() - System.arraycopy(xB, 0 + xOffset, encodedByteArray, 1, xB.size - xOffset) - System.arraycopy(yB, 0 + yOffset, encodedByteArray, 1 + xB.size - xOffset, yB.size - yOffset) - return encodedByteArray - } - - override fun getFormat(): String = "ASN.1" - - override fun getParams(): ECParameterSpec = Secp256K1Curve.ecParameterSpec -} diff --git a/android-client/app/src/main/java/org/hermes/crypto/Secp256K1Curve.kt b/android-client/app/src/main/java/org/hermes/crypto/Secp256K1Curve.kt deleted file mode 100644 index b6d55f4..0000000 --- a/android-client/app/src/main/java/org/hermes/crypto/Secp256K1Curve.kt +++ /dev/null @@ -1,76 +0,0 @@ -package org.hermes.crypto - -import org.bouncycastle.asn1.x9.X962Parameters -import java.math.BigInteger -import java.security.spec.ECFieldFp -import java.security.spec.ECParameterSpec -import java.security.spec.ECPoint -import java.security.spec.EllipticCurve -import org.bouncycastle.asn1.x9.X9ECParameters -import org.bouncycastle.crypto.ec.CustomNamedCurves -import org.bouncycastle.crypto.params.ECDomainParameters -import org.bouncycastle.math.ec.FixedPointUtil -import org.bouncycastle.util.encoders.Hex - - -/** - * Object that contains all the important bits and pieces for generating Keypairs - * using the SecP256K1 ECC curve. - * Some code has been modified from these repos: - * @see DeterministicKey.java - * @see ECDSASignature.java - * @see Crypto.java - */ -object Secp256K1Curve { - - val X9ECParameters: X9ECParameters = CustomNamedCurves.getByName("secp256k1") - - val ecDomainParameters: ECDomainParameters = ECDomainParameters( - X9ECParameters.curve, X9ECParameters.g, X9ECParameters.n, X9ECParameters.h) - - val halfCurveOrder: BigInteger = X9ECParameters.n.shiftRight(1) - - val X962Parameters: X962Parameters = X962Parameters(X9ECParameters) - - init { - // Tell Bouncy Castle to pre-compute data that's needed during secp256k1 calculations. - FixedPointUtil.precompute(X9ECParameters.g) - } - - /** - * ECParameterSpec according to the official description of the SECP256K1 Curve from: - * @see - *

For the time being this cannot be used to generate a keypair in Android because - * neither BoringSSL not OpenSSL support the generation of this curve even though it - * can be used with any library that supports ECC.

- *

Also, the BouncyCastle provider has been deprecated in the latest versions of the - * AndroidAPI in favor of the AndroidOpenSSL provider which is built around BoringSSL. - * There does not seem to be any willingness to support this curve any time soon as - * evidenced by this issue: - * @see - * Should the curve become available from one of the standard Providers available in - * Android, this parameter specification will be useful to produce keys in a more standard - * way.

- */ - val a = BigInteger.ZERO - - val b = BigInteger.valueOf(7) - - val p = BigInteger(1, Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F")) - - val xOfG = BigInteger(1, Hex.decode("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")) - - val yOfG = BigInteger(1, Hex.decode("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8")) - - val G = ECPoint(xOfG, yOfG) - - val n = BigInteger(1, Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")) - - val h = 1 - - val ecField = ECFieldFp(p) - - val ellipticCurve = EllipticCurve(ecField, a, b) - - val ecParameterSpec = ECParameterSpec(ellipticCurve, G, n, h) -} \ No newline at end of file diff --git a/android-client/app/src/main/java/org/hermes/crypto/algorithms.kt b/android-client/app/src/main/java/org/hermes/crypto/algorithms.kt deleted file mode 100644 index 3a5455e..0000000 --- a/android-client/app/src/main/java/org/hermes/crypto/algorithms.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.hermes.crypto - -object CryptoAlgorithms { - const val HMAC_SHA512: String = "HmacSHA512" - const val PBKDF2_HMC_SHA1: String = "PBKDF2WithHmacSHA1" - const val PBKDF2_HMC_SHA512: String = "PBKDF2WithHmacSHA512" -} \ No newline at end of file diff --git a/android-client/app/src/main/java/org/hermes/crypto/hash.kt b/android-client/app/src/main/java/org/hermes/crypto/hash.kt deleted file mode 100644 index 350a8a4..0000000 --- a/android-client/app/src/main/java/org/hermes/crypto/hash.kt +++ /dev/null @@ -1,97 +0,0 @@ -package org.hermes.crypto - -import org.bouncycastle.jcajce.provider.digest.Keccak -import org.bouncycastle.jcajce.provider.digest.RIPEMD160 -import java.lang.Exception -import java.security.MessageDigest -import javax.crypto.SecretKeyFactory -import javax.crypto.spec.PBEKeySpec - -object PasswordHashingParameters { - var Iterations: Int = 1000 - var Salt: ByteArray = "fjggne0rg2sr24dlgsouug".toByteArray() - var KeySize: Int = 64 * 8 -} - -data class HashedPassword(val hashingAlgorithm: String = CryptoAlgorithms.PBKDF2_HMC_SHA1 , - val iterations: Int = PasswordHashingParameters.Iterations, - val salt: ByteArray = PasswordHashingParameters.Salt, - val hash: ByteArray) { - override fun toString(): String { - return hashingAlgorithm + "\$$iterations" + "\$${PasswordHasher.bytesToHex(salt)}" + - "\$${PasswordHasher.bytesToHex(hash)}" - } - - override fun equals(other: Any?): Boolean { - if (other !is HashedPassword) { - return false - } - return hashingAlgorithm == other.hashingAlgorithm && iterations == other.iterations && - salt.toString() == other.salt.toString() && hash.toString() == other.hash.toString() - } - - override fun hashCode(): Int { - return toString().hashCode() - } -} - -object PasswordHasher { - - class WrongHashFormatException: Exception() - - /** - * Unpacks the components of a hash from the string and returns them. - */ - fun unPackHash(hashLike: String): HashedPassword { - val subStrings = hashLike.split("\$") - if (subStrings.size != 4) { - throw WrongHashFormatException() - } - return HashedPassword( - subStrings[0], - subStrings[1].toInt(), - subStrings[2].toByteArray(), - subStrings[3].toByteArray() - ) - } - - fun hashPassword(password: CharArray, - salt: ByteArray = PasswordHashingParameters.Salt, - iterations: Int = PasswordHashingParameters.Iterations - ): HashedPassword { - val passwordBasedEncryptionSpec = PBEKeySpec( - password, - salt, - iterations, - PasswordHashingParameters.KeySize) - val secretKeyFactory = SecretKeyFactory.getInstance(CryptoAlgorithms.PBKDF2_HMC_SHA1) - val hash = secretKeyFactory.generateSecret(passwordBasedEncryptionSpec).encoded - return HashedPassword(hash = hash) - } - - fun bytesToHex(byteArray: ByteArray): String { - return byteArray - .map { b: Byte -> String.format("%02X", b) } - .fold("") { acc, i -> acc + i } - } - -} - -open class Hasher(val impl: MessageDigest) { - - fun hash(b: ByteArray): ByteArray { - return impl.digest(b) - } - - fun hashTwice(b: ByteArray): ByteArray { - return hash(hash(b)) - } -} - -object SHA256: Hasher(MessageDigest.getInstance("SHA-256")) - -object SHA512: Hasher(MessageDigest.getInstance("SHA-512")) - -object RIPEMD: Hasher(RIPEMD160.Digest()) - -object Keccak: Hasher(Keccak.Digest256()) \ No newline at end of file diff --git a/android-client/app/src/main/java/org/hermes/di/HermesModule.kt b/android-client/app/src/main/java/org/hermes/di/HermesModule.kt index 1300f12..ae307be 100644 --- a/android-client/app/src/main/java/org/hermes/di/HermesModule.kt +++ b/android-client/app/src/main/java/org/hermes/di/HermesModule.kt @@ -7,18 +7,16 @@ import androidx.room.Room import com.google.gson.GsonBuilder import dagger.Module import dagger.Provides -import okhttp3.OkHttpClient import java.security.KeyStore +import java.util.concurrent.TimeUnit import javax.inject.Named import javax.inject.Singleton -import retrofit2.Retrofit +import okhttp3.OkHttpClient import retrofit2.converter.gson.GsonConverterFactory -import java.util.concurrent.TimeUnit - +import retrofit2.Retrofit import org.hermes.HermesRoomDatabase import org.hermes.R - @Module class HermesModule { @@ -56,6 +54,7 @@ class HermesModule { .connectTimeout(20, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) - .build()) + .build() + ) } -} \ No newline at end of file +} diff --git a/android-client/app/src/main/java/org/hermes/di/IOTAModule.kt b/android-client/app/src/main/java/org/hermes/di/IOTAModule.kt index 69eeaa9..26155a0 100644 --- a/android-client/app/src/main/java/org/hermes/di/IOTAModule.kt +++ b/android-client/app/src/main/java/org/hermes/di/IOTAModule.kt @@ -14,4 +14,4 @@ class IOTAModule { .timeout(20) .build() } -} \ No newline at end of file +} diff --git a/android-client/app/src/main/java/org/hermes/fragments/NavigationDrawerFragment.kt b/android-client/app/src/main/java/org/hermes/fragments/NavigationDrawerFragment.kt index bc98f32..4b2b39f 100644 --- a/android-client/app/src/main/java/org/hermes/fragments/NavigationDrawerFragment.kt +++ b/android-client/app/src/main/java/org/hermes/fragments/NavigationDrawerFragment.kt @@ -2,9 +2,6 @@ package org.hermes.fragments import android.content.Context -import androidx.fragment.app.Fragment -import androidx.core.view.GravityCompat -import androidx.drawerlayout.widget.DrawerLayout import android.graphics.Bitmap import android.graphics.Color import android.graphics.PorterDuff @@ -21,9 +18,11 @@ import android.view.ViewGroup import android.widget.* import androidx.appcompat.widget.Toolbar import androidx.core.content.res.ResourcesCompat.getDrawable +import androidx.core.view.GravityCompat +import androidx.drawerlayout.widget.DrawerLayout +import androidx.fragment.app.Fragment import dagger.Module import javax.inject.Inject - import org.hermes.R diff --git a/android-client/app/src/main/java/org/hermes/hd/BIP32.kt b/android-client/app/src/main/java/org/hermes/hd/BIP32.kt deleted file mode 100644 index 67ec8aa..0000000 --- a/android-client/app/src/main/java/org/hermes/hd/BIP32.kt +++ /dev/null @@ -1,348 +0,0 @@ -package org.hermes.hd - -import java.lang.Exception -import java.math.BigInteger -import java.security.PrivateKey -import java.security.PublicKey - -import javax.crypto.Mac -import javax.crypto.spec.SecretKeySpec - -import org.bouncycastle.math.ec.ECPoint -import org.bouncycastle.math.ec.FixedPointCombMultiplier -import org.bouncycastle.pqc.math.linearalgebra.IntegerFunctions.pow - -import org.hermes.crypto.CryptoAlgorithms.HMAC_SHA512 -import org.hermes.crypto.* -import org.hermes.hd.BIP32.HARDENED_KEY_OFFSET -import org.hermes.hd.BIP32.normalizeToStr -import org.hermes.utils.endsWithAnyOf -import org.hermes.utils.extendOrReduceTo -import org.hermes.utils.toBigInt -import org.hermes.utils.toByteArray - - -object BIP32 { - - class InvalidPath: Exception() - - val HARDENED_BIT = pow(2.toLong(), 31) - val HARDENED_KEY_OFFSET = pow(2.toLong(), 31) - val MAX_KEY_INDEX = pow(2.toLong(), 32) - - /** - * Verifies a BIP32 compliant key path. - */ - fun verify(path: String) { - if (!Regex("^m(/[0-9]+['H]?)*\$").matches(path)) - throw InvalidPath() - for (fragment in path.split("/").drop(1)) { - val cleanFragment = when (fragment.endsWithAnyOf("'", "H")) { - true -> fragment.dropLast(1).toLong() + HARDENED_KEY_OFFSET - else -> fragment.toLong() - } - if ((cleanFragment >= MAX_KEY_INDEX) or (cleanFragment < 0)) - throw InvalidPath() - } - } - - /** - * Converts a path of BIP32 path into a list of indices. - */ - fun normalize(path: String): Iterable = path - .split("/") - .map { when { - it == "m" -> 0 - it.endsWithAnyOf("'", "H") -> it.dropLast(1).toLong() + HARDENED_KEY_OFFSET - else -> it.toLong() - } } - - fun normalizeToStr(path: String): String = when (path) { - "m" -> path - else -> "m/${normalize(path).drop(1).joinToString("/")}" - } - - fun verifyAndNormalize(path: String): Iterable { - verify(path) - return normalize(path) - } - - fun pathOfChild(currentPath: String, childIndex: Long): String = "${normalizeToStr(currentPath)}/$childIndex" -} - -interface BIP32Key: PrivateKey { - class InvalidIndex: Exception() - - class NoSibling: Exception() - - companion object { - /** - * Returns the index of the current key. - */ - fun currentIndex(path: String): Long { - val currentIndex = path.split("/").last() - return when { - currentIndex.endsWith("'") -> currentIndex.replace("'", "").toLong() + BIP32.HARDENED_KEY_OFFSET - currentIndex.endsWith("H") -> currentIndex.replace("H", "").toLong() + BIP32.HARDENED_KEY_OFFSET - currentIndex == "m" -> 0 - else -> currentIndex.toLong() - } - } - } - - val path: String - - val index: Long - get() = currentIndex(path) - - val parent: BIP32Key? - - val chainCode: ByteArray - - val hardened: Boolean - get() = index >= BIP32.HARDENED_KEY_OFFSET - - val depth: Int - get() = path.split("/").size - 1 - - val public: BIP32PubKey - - fun child(index: Long = 0): BIP32Key - - /** - * Returns the BIP32Key key that belongs in the same chain and has the next index. - * @throws NoSibling when it's the master node or the index is greater than 2^32 - 1. - */ - fun sibling(): BIP32Key = when { - index >= BIP32.MAX_KEY_INDEX - 1 -> throw NoSibling() - else -> parent?.child(index + 1) ?: throw NoSibling() - } -} - -interface BIP32PubKey: PublicKey { - - class NoPubKey: Exception() - - val path: String - - val index: Long - get() = BIP32Key.currentIndex(path) - - val parent: BIP32PubKey? - - val chainCode: ByteArray - - val depth: Int - get() = path.split("/").size - 1 - - val identifier: ByteArray - get() = RIPEMD.hash(SHA256.hash(encoded)) - - val fingerprint: ByteArray - get() = identifier.sliceArray(0 until 4) - - fun child(index: Long): BIP32PubKey - - val address: String -} - -class ExPrivKey( - override val path: String, - override val parent: BIP32Key?, - override val chainCode: ByteArray, - key: BigInteger, - val encoder: KeyEncoder = BTCKeyEncoder -) : BIP32Key, SecP256K1PrivKey(key) { - - init { BIP32.verify(path) } - - companion object { - fun CKDPriv( - index: Long, - key: ByteArray, - public: ByteArray, - chainCode: ByteArray - ): Pair { - if ((index < 0) or (index >= BIP32.MAX_KEY_INDEX)) - throw BIP32Key.InvalidIndex() - - val n = Secp256K1Curve.X9ECParameters.n - val digest = Mac.getInstance(HMAC_SHA512).apply { init(SecretKeySpec(chainCode, HMAC_SHA512)) } - val I = when (index >= HARDENED_KEY_OFFSET) { - true -> digest.doFinal( - key.extendOrReduceTo(33, padStart = true) + - index.toByteArray().extendOrReduceTo(4, padStart = true) - ) - else -> digest.doFinal( - public + index.toByteArray().extendOrReduceTo(4, padStart = true) - ) - } - return Pair( - I.sliceArray(32 until 64), - (I.sliceArray(0 until 32).toBigInt(positive = true) + BigInteger(key)).mod(n) - ) - } - } - - override fun child(index: Long): BIP32Key { - val (chainCode, key) = CKDPriv(index, value.toByteArray(), public.encoded, chainCode) - return ExPrivKey(BIP32.pathOfChild(path, index), this, chainCode, key) - } - - override val public: ExPubKey by lazy { ExPubKey(parent?.public, chainCode, path, this, encoder) } - - override fun toString(): String = encoder.encodePrivateKey(this, hashMapOf()) -} - -class ExPubKey( - override val parent: BIP32PubKey?, - override val chainCode: ByteArray, - override val path: String, - x :BigInteger, - y: BigInteger, - val encoder: KeyEncoder = BTCKeyEncoder -): - SecP256K1PubKey(x, y), BIP32PubKey { - - override fun getEncoded(): ByteArray = bcPoint.getEncoded(true) - - constructor(parent: BIP32PubKey?, chainCode: ByteArray, path: String, publicKeyECPoint: ECPoint, - encoder: KeyEncoder = BTCKeyEncoder): - this( - parent, chainCode, path, - publicKeyECPoint.affineXCoord.toBigInteger(), - publicKeyECPoint.affineYCoord.toBigInteger(), - encoder - ) - - constructor(parent: BIP32PubKey?, chainCode: ByteArray, path: String, rawKey: BigInteger): - this( - parent, chainCode, path, - FixedPointCombMultiplier() - .multiply(Secp256K1Curve.X9ECParameters.g, rawKey) - .normalize() - ) - - constructor(parent: BIP32PubKey?, chainCode: ByteArray, path: String, privateKey: SecP256K1PrivKey, - encoder: KeyEncoder = BTCKeyEncoder): - this(parent, chainCode, path, - FixedPointCombMultiplier() - .multiply(Secp256K1Curve.X9ECParameters.g, privateKey.value) - .normalize(), - encoder - ) - - override fun toString(): String = encoder.encodePublicKey(this, hashMapOf()) - - override fun child(index: Long): BIP32PubKey { - if ((index < 0) or (index >= BIP32.MAX_KEY_INDEX)) - throw BIP32Key.InvalidIndex() - - if (index >= HARDENED_KEY_OFFSET) - throw BIP32PubKey.NoPubKey() - - val I = Mac.getInstance(HMAC_SHA512) - .apply { init(SecretKeySpec(chainCode, HMAC_SHA512)) } - .doFinal(ByteArray(1) + encoded + index.toByteArray()) - - return ExPubKey( - this, - I.sliceArray(32 until 64), - BIP32.pathOfChild(path, index), - I.sliceArray(0 until 32).toBigInt() - ) - } - - override val address: String = encoder.address(this, hashMapOf()) -} - -interface KeyRegistry { - class DuplicateKey: Exception() - - /** - * Return the key that corresponds to this path. - * - * If the key does not exist, a null will be returned - */ - fun get(path: String): BIP32Key? - - /** - * Map a key to a path. - * - * If the key is already mapped, an exception will be thrown. - * Keys can not be overwritten! - */ - fun put(path: String, key: BIP32Key) -} - -abstract class KeyEncoder { - abstract fun encodePrivateKey(key: PrK, options: Map): String - - abstract fun encodePublicKey(key: PuK, options: Map): String - - abstract fun address(key: PuK, options: Map): String -} - -interface Signer { - fun sign(key: K, data: ByteArray): ByteArray -} - -/** - * Simplest possible implementation of a KeyRegistry that is backed by - * an in-memory HashMap. When an application is shut down, all keys are - * lost. - */ -class InMemoryKeyRegistry: KeyRegistry { - private val registry = HashMap() - - override fun get(path: String): BIP32Key? = registry[path] - - override fun put(path: String, key: BIP32Key) { - if (registry.containsKey(path)) - throw KeyRegistry.DuplicateKey() - registry[path] = key - } -} - -open class Wallet(seed: ByteArray, private val keyRegistry: KeyRegistry = InMemoryKeyRegistry()) { - - open operator fun get(path: String): BIP32Key { - BIP32.verify(path) - val normalizedPath = normalizeToStr(path) - val cachedKey = keyRegistry.get(normalizedPath) - if (cachedKey != null) return cachedKey - var key: BIP32Key = master - var partialPath = master.path - for (index in BIP32.normalize(path).drop(1)) { - partialPath = normalizeToStr(BIP32.pathOfChild(partialPath, index)) - val intermediateKey = keyRegistry.get(partialPath) - if (intermediateKey == null) { - key = key.child(index) - keyRegistry.put(key.path, key) - } else { - key = intermediateKey - } - } - return key - } - - /** - * Master node will always be a standard Extended Private Key. The other nodes might be of - * different kinds depending on the network they will be used for. - */ - val master: ExPrivKey - - init { - val I = Mac.getInstance(HMAC_SHA512) - .apply { init(SecretKeySpec("Bitcoin seed".toByteArray(), HMAC_SHA512)) } - .doFinal(seed) - - master = ExPrivKey( - path = "m", - parent = null, - chainCode = I.sliceArray(32 until 64), - key = I.sliceArray(0 until 32).toBigInt(positive = true)) - - keyRegistry.put("m", master) - } -} diff --git a/android-client/app/src/main/java/org/hermes/hd/BIP39.kt b/android-client/app/src/main/java/org/hermes/hd/BIP39.kt deleted file mode 100644 index aae52ca..0000000 --- a/android-client/app/src/main/java/org/hermes/hd/BIP39.kt +++ /dev/null @@ -1,108 +0,0 @@ -package org.hermes.hd - -import java.nio.ByteBuffer -import java.security.SecureRandom -import java.util.* -import javax.crypto.SecretKeyFactory -import javax.crypto.spec.PBEKeySpec -import kotlin.math.pow -import org.bouncycastle.util.encoders.Hex -import org.hermes.crypto.CryptoAlgorithms - -import org.hermes.crypto.SHA256 -import org.hermes.utils.* - -/** - * An implementation of the algorithm described in BIP 39 for generating a mnemonic sentence. - * @see{https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki} - */ -class Mnemonic(val entropy: ByteArray, val network: Network, checksum: Byte, - words: Array, passphrase: String = "") { - - enum class Entropy(val i: Int) { - SIZE_128(128), - SIZE_160(160), - SIZE_192(192), - SIZE_224(224), - SIZE_256(256), - } - - enum class Network(val b: ByteArray) { - MAINNET(Hex.decode("0488ADE4")), - TESTNET(Hex.decode("04358394")) - } - - class InvalidWord: Exception() - class InvalidChecksum: Exception() - - companion object { - fun new(size: Entropy, passphrase: String = ""): Mnemonic { - val cs = size.i / 32 - val ms = size.i + cs / 11 - val entropy = SecureRandom - .getInstance("SHA1PRNG") - .generateSeed(size.i) - val checksum = SHA256.impl - .digest(entropy).slice(0 until cs) - .toByteArray() - val finalEntropy = entropy + checksum - val extractWord = fun(i: Int): String { - val byteArray = finalEntropy - .slice((i * 11) until ((i + 1) * 11)) - .toByteArray() - val exp = ByteBuffer.wrap(byteArray).asIntBuffer()[0] - val index = (2.0.pow(exp) - 1).toInt() - return EnglishWordlist.items[index] - } - return Mnemonic(entropy, Network.MAINNET, checksum[0], Array(ms) { extractWord(it) }) - } - - /** - * Extracts the mnemonic from a list of words. - * - * The process is as follows: - * 1. Get the indices of all the words. - * 2. Convert the indices to bytes and then concatenate them in a bitset - * 3. Split the entropy from the checksum and convert them back into byte arrays - */ - @Throws(InvalidWord::class) - fun fromWordList(words: Array, passphrase: String = ""): Mnemonic { - words.forEach { if (!EnglishWordlist.index.containsKey(it)) throw InvalidWord() } - val entropyAndChecksumBitSize = words.size * 11 - val checksumBitSize = entropyAndChecksumBitSize % 32 - val entropyBitSize = entropyAndChecksumBitSize - checksumBitSize - - val bitSet = BitSet(entropyAndChecksumBitSize) - var bitSetIndex = 0 - for (word in words) { - val wordIndex = EnglishWordlist.index[word] ?: continue - bitSet.copyFromInt(wordIndex, bitSetIndex, 11) - bitSetIndex += 11 - } - val entropy = bitSet.asByteArray(0, entropyBitSize) - val checksum = bitSet.asByteArray(entropyBitSize, entropyAndChecksumBitSize)[0] - return Mnemonic(entropy, Network.MAINNET, checksum, words, passphrase) - } - } - - val seed: ByteArray - - init { - words.forEach { if (!EnglishWordlist.index.containsKey(it)) throw InvalidWord() } - - // Calculate checksum of the entropy - val expectedChecksum = SHA256.impl.digest(entropy) - val expectedChecksumBitNum = (words.size * 11) / 33 - val expectedChecksumBits: Byte = BitSet(expectedChecksumBitNum) - .apply { copyFromByte(expectedChecksum[0], 0, expectedChecksumBitNum, bitOffset = 8 - expectedChecksumBitNum) } - .asByteArray(0, expectedChecksumBitNum)[0] - if (checksum != expectedChecksumBits) throw InvalidChecksum() - - // Get seed - val keySpec = PBEKeySpec( - words.joinToString(" ").toCharArray(), - "mnemonic$passphrase".toByteArray(), 2048, 512) - val factory = SecretKeyFactory.getInstance(CryptoAlgorithms.PBKDF2_HMC_SHA512) - seed = factory.generateSecret(keySpec).encoded - } -} \ No newline at end of file diff --git a/android-client/app/src/main/java/org/hermes/hd/BIP44.kt b/android-client/app/src/main/java/org/hermes/hd/BIP44.kt deleted file mode 100644 index b19bc47..0000000 --- a/android-client/app/src/main/java/org/hermes/hd/BIP44.kt +++ /dev/null @@ -1,105 +0,0 @@ -package org.hermes.hd - -import org.hermes.utils.toTritArray -import java.lang.Exception - -class BIP44Wallet(seed: ByteArray, keyRegistry: KeyRegistry = InMemoryKeyRegistry()) { - - class InvalidCoinType(): Exception() - - /** - * ExtendedWallet is a class that extends the standard BIP32 compliant wallet so - * that it becomes compliant with BIP44. - * - * Specifically, it injects different keys in some paths according to the specification - * in SLIP44. - * The ExtendedWallet is seeded once and can then be re-used for multiple different - * currencies from the same application. - */ - class ExtendedWallet(seed: ByteArray, private val keyRegistry: KeyRegistry = InMemoryKeyRegistry()): - Wallet(seed, keyRegistry) - { - - companion object { - val BIP44Path = BIP32.normalizeToStr("m/44'") // "m/2147483692" // m/44' - } - - /** - * The get method will intercept all calls and will make sure to inject the correct keys - * wherever necessary. - * - * The implementation is lazy, meaning that the keys will be injected only when requested. - * It does not force the user to use just the BIP44 related paths. - */ - override operator fun get(path: String): BIP32Key { - BIP32.verify(path) - val normalizedPath = BIP32.normalize(path).drop(1) - val normalizedPathStr = BIP32.normalizeToStr(path) - if (normalizedPathStr.startsWith(BIP44Path) and (normalizedPath.size > 1)) { - val coinIndex = normalizedPath[1] - val coinEntry = SLIP44.byWalletIndex[coinIndex] ?: throw InvalidCoinType() - val childPath = BIP32.normalizeToStr("$BIP44Path/${coinEntry.walletIndex}") - // If the special paths have not been set, then put them in place - if ((keyRegistry.get(BIP44Path) == null) or (keyRegistry.get(childPath) == null)) { - val bip44Key = super.get(BIP44Path) - val childKey = if (coinEntry.symbol == "IOTA") { - // Special keys for IOTA - val (chainCode, key) = IOTAExPrivKey.CKDpriv( - coinEntry.walletIndex, - (bip44Key as ExPrivKey).value.toByteArray(), - bip44Key.public.encoded, - bip44Key.chainCode) - IOTAExPrivKey(childPath, bip44Key, chainCode, key.toTritArray().toTryteArray()) - } - else { - // Standard ExPrivKeys - val encoder = when (coinEntry.symbol) { - "ETH" -> ETHKeyEncoder - else -> BTCKeyEncoder - } - val (chainCode, key) = ExPrivKey.CKDPriv( - coinEntry.walletIndex, - (bip44Key as ExPrivKey).value.toByteArray(), - (bip44Key as ExPrivKey).public.encoded, - bip44Key.chainCode) - ExPrivKey(childPath, bip44Key, chainCode, key, encoder) - } - keyRegistry.put(childPath, childKey) - } - } - // One the keys have been put in place, proceed as normal - return super.get(path) - } - } - - class Context(val coinType: String, val account: Int, val change: Int = 0, private val wallet: Wallet) { - - private val coinPath = BIP32.normalizeToStr("m/44'/${SLIP44.bySymbol[coinType]!!.walletIndex}") - private val accountPath = "$account'/$change" - private val internalPath = "$coinPath/$accountPath" - - operator fun get(index: Long): BIP32Key { - if (index >= BIP32.HARDENED_KEY_OFFSET) - throw BIP32Key.InvalidIndex() - return wallet["$internalPath/$index"] - } - - operator fun get(path: String): BIP32Key { - return wallet["$internalPath/$path"] - } - - init { - if (!SLIP44.bySymbol.containsKey(coinType)) - throw InvalidCoinType() - } - } - - // Be careful accessing the wallet this way because you might cause some paths to be initialised. - val masterWallet: Wallet = ExtendedWallet(seed, keyRegistry = keyRegistry) - - fun getWalletForCoin(coinType: String, account: Int, change: Int = 0): Context = - Context(coinType, account, change, masterWallet).apply { - // Use this to initialize the path - get(0L) - } -} \ No newline at end of file diff --git a/android-client/app/src/main/java/org/hermes/hd/Base58.kt b/android-client/app/src/main/java/org/hermes/hd/Base58.kt deleted file mode 100644 index 8c86341..0000000 --- a/android-client/app/src/main/java/org/hermes/hd/Base58.kt +++ /dev/null @@ -1,124 +0,0 @@ -package org.hermes.hd - -import org.hermes.crypto.SHA256 - -/** - * Base58 encoder based on: - * @see{https://github.com/bitcoinj/bitcoinj/blob/master/core/src/main/java/org/bitcoinj/core/Base58.java} - */ -object Base58 { - - class InvalidFormat : Exception() - class InvalidChecksum : Exception() - - private val ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray() - private val INDICES = IntArray(128) - private val zeroChar = ALPHABET[0] - private const val zeroByte = 0.toByte() - - init { - for (i in 0 until ALPHABET.size) - INDICES[ALPHABET[i].toInt()] = i - } - - /** - * Encodes the given bytes as a base58 string (no checksum is appended). - */ - fun toBase58String(input: ByteArray, appendChecksum: Boolean = false): String { - if (input.isEmpty()) return "" - - // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters) - val inputClone = input.clone() + - (if (appendChecksum) SHA256.hashTwice(input).sliceArray(0 until 4) else ByteArray(0)) - - val encoded = CharArray(inputClone.size * 2) // upper bound - var outputStart = encoded.size - - // Count how many zeros there are in the beginning of the byte array - var zeros = 0 - for (i in 0 until inputClone.size) - if (inputClone[i] == zeroByte) zeros++ - else break - - var encodedZeros = 0 - for (i in zeros until inputClone.size) - while (inputClone[i] != zeroByte) { - encoded[--outputStart] = ALPHABET[divmod(inputClone, i, 256, 58)] - if (encoded[outputStart] == zeroChar) encodedZeros++ - else encodedZeros = 0 - } - - (zeros downTo 1).forEach { _ -> encoded[--outputStart] = zeroChar } - - return String(encoded, outputStart, encoded.size - outputStart) - } - - /** - * Decodes the given base58 string into the original data bytes. - * - * @param input the base58-encoded string to decode - * @return the decoded data bytes - */ - @Throws(InvalidFormat::class) - fun decode(input: String, verifyChecksum: Boolean = false): ByteArray { - if (input.isEmpty()) - return ByteArray(0) - - // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits). - val input58 = ByteArray(input.length) - for (i in 0 until input.length) - input58[i] = INDICES.getOrNull(input[i].toInt())?.toByte() ?: throw InvalidFormat() - - // Count leading zeros. - var zeros = 0 - while (zeros < input58.size && input58[zeros] == zeroByte) - ++zeros - - // Convert base-58 digits to base-256 digits. - val decoded = ByteArray(input.length) - var outputStart = decoded.size - for (inputStart in zeros until input58.size) { - if (input58[inputStart] == zeroByte) continue - decoded[--outputStart] = divmod(input58, inputStart, 58, 256).toByte() - } - // Ignore extra leading zeroes that were added during the calculation. - while (outputStart < decoded.size && decoded[outputStart] == zeroByte) - ++outputStart - - if (verifyChecksum) { - if (decoded.size < 4) throw InvalidFormat() - val checksum = decoded.sliceArray(decoded.size - 4 until decoded.size) - val expectedChecksum = SHA256 - .hashTwice(decoded.sliceArray(outputStart - zeros until decoded.size - 4)) - .sliceArray(0 until 4) - if (!checksum.contentEquals(expectedChecksum)) throw InvalidChecksum() - return decoded.sliceArray(outputStart - zeros until decoded.size - 4) - } - // Return decoded data (including original number of leading zeros). - return decoded.sliceArray(outputStart - zeros until decoded.size) - } - - /** - * Divides a number, represented as an array of bytes each containing a single digit - * in the specified base, by the given divisor. The given number is modified in-place - * to contain the quotient, and the return value is the remainder. - * - * @param number the number to divide - * @param firstDigit the index within the array of the first non-zero digit - * (this is used for optimization by skipping the leading zeros) - * @param base the base in which the number's digits are represented (up to 256) - * @param divisor the number to divide by (up to 256) - * @return the remainder of the division operation - */ - private fun divmod(number: ByteArray, firstDigit: Int, base: Int, divisor: Int): Int { - // this is just long division which accounts for the base of the input digits - var remainder = 0 - for (i in firstDigit until number.size) { - val digit = number[i].toInt() and 0xFF - val temp = remainder * base + digit - number[i] = (temp / divisor).toByte() - remainder = temp % divisor - } - return remainder - } -} diff --git a/android-client/app/src/main/java/org/hermes/hd/BaseECDSASigner.kt b/android-client/app/src/main/java/org/hermes/hd/BaseECDSASigner.kt deleted file mode 100644 index cc3ed44..0000000 --- a/android-client/app/src/main/java/org/hermes/hd/BaseECDSASigner.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.hermes.hd - -import java.security.MessageDigest - -import org.hermes.crypto.ECDSASignature -import org.hermes.crypto.SecP256K1PrivKey -import org.hermes.utils.toByteArray - -abstract class BaseECDSASigner: Signer { - - tailrec fun recDigest(msg: ByteArray, round: Int, digest: MessageDigest = MessageDigest.getInstance("SHA-256")): ByteArray { - val d = digest.digest(msg) - return when(round) { - 1 -> d - else -> recDigest(d, round - 1, digest) - } - } - - /** - * Base method for signing messages for various different blockchains, - * that loosely follow the Electrum style signatures. - */ - fun chainSign( - key: SecP256K1PrivKey, prefix: String, data: ByteArray, hashRounds: Int = 1, digest: MessageDigest - ): ECDSASignature { - val prefixLength = prefix.length - val messageLength = data.size - val messageLengthExtraByte = when { - messageLength < 253 -> ByteArray(0) - messageLength < 65536 -> 253.toByteArray() - messageLength < 4294967296 -> 254.toByteArray() - else -> 255.toByteArray() - } - - val msgBytes = prefixLength.toByteArray() + - prefix.toByteArray() + - messageLengthExtraByte + - messageLength.toByteArray().reversedArray() + - data - - val hashed = recDigest(msgBytes, hashRounds, digest) - return key.sign(hashed) - } -} \ No newline at end of file diff --git a/android-client/app/src/main/java/org/hermes/hd/SLIP44.kt b/android-client/app/src/main/java/org/hermes/hd/SLIP44.kt deleted file mode 100644 index fccfaf6..0000000 --- a/android-client/app/src/main/java/org/hermes/hd/SLIP44.kt +++ /dev/null @@ -1,583 +0,0 @@ -package org.hermes.hd - -import org.hermes.utils.FrozenHashMap - -object SLIP44 { - data class Entry(val index: Int, val hex: String, val symbol: String) { - val walletIndex: Long = index + BIP32.HARDENED_KEY_OFFSET - } - - val entries = arrayOf( - Entry(0,"0x80000000","BTC"), - Entry(2,"0x80000002","LTC"), - Entry(3,"0x80000003","DOGE"), - Entry(4,"0x80000004","RDD"), - Entry(5,"0x80000005","DASH"), - Entry(6,"0x80000006","PPC"), - Entry(7,"0x80000007","NMC"), - Entry(8,"0x80000008","FTC"), - Entry(9,"0x80000009","XCP"), - Entry(10,"0x8000000a","BLK"), - Entry(11,"0x8000000b","NSR"), - Entry(12,"0x8000000c","NBT"), - Entry(13,"0x8000000d","MZC"), - Entry(14,"0x8000000e","VIA"), - Entry(15,"0x8000000f","XCH"), - Entry(16,"0x80000010","RBY"), - Entry(17,"0x80000011","GRS"), - Entry(18,"0x80000012","DGC"), - Entry(19,"0x80000013","CCN"), - Entry(20,"0x80000014","DGB"), - Entry(22,"0x80000016","MONA"), - Entry(23,"0x80000017","CLAM"), - Entry(24,"0x80000018","XPM"), - Entry(25,"0x80000019","NEOS"), - Entry(26,"0x8000001a","JBS"), - Entry(27,"0x8000001b","ZRC"), - Entry(28,"0x8000001c","VTC"), - Entry(29,"0x8000001d","NXT"), - Entry(30,"0x8000001e","BURST"), - Entry(31,"0x8000001f","MUE"), - Entry(32,"0x80000020","ZOOM"), - Entry(33,"0x80000021","VASH"), - Entry(34,"0x80000022","CDN"), - Entry(35,"0x80000023","SDC"), - Entry(36,"0x80000024","PKB"), - Entry(37,"0x80000025","PND"), - Entry(38,"0x80000026","START"), - Entry(39,"0x80000027","MOIN"), - Entry(40,"0x80000028","EXP"), - Entry(41,"0x80000029","EMC2"), - Entry(42,"0x8000002a","DCR"), - Entry(43,"0x8000002b","XEM"), - Entry(44,"0x8000002c","PART"), - Entry(45,"0x8000002d","ARG"), - Entry(48,"0x80000030","SHR"), - Entry(49,"0x80000031","GCR"), - Entry(50,"0x80000032","NVC"), - Entry(51,"0x80000033","AC"), - Entry(52,"0x80000034","BTCD"), - Entry(53,"0x80000035","DOPE"), - Entry(54,"0x80000036","TPC"), - Entry(55,"0x80000037","AIB"), - Entry(56,"0x80000038","EDRC"), - Entry(57,"0x80000039","SYS"), - Entry(58,"0x8000003a","SLR"), - Entry(59,"0x8000003b","SMLY"), - Entry(60,"0x8000003c","ETH"), - Entry(61,"0x8000003d","ETC"), - Entry(62,"0x8000003e","PSB"), - Entry(63,"0x8000003f","LDCN"), - Entry(65,"0x80000041","XBC"), - Entry(66,"0x80000042","IOP"), - Entry(67,"0x80000043","NXS"), - Entry(68,"0x80000044","INSN"), - Entry(69,"0x80000045","OK"), - Entry(70,"0x80000046","BRIT"), - Entry(71,"0x80000047","CMP"), - Entry(72,"0x80000048","CRW"), - Entry(73,"0x80000049","BELA"), - Entry(74,"0x8000004a","ICX"), - Entry(75,"0x8000004b","FJC"), - Entry(76,"0x8000004c","MIX"), - Entry(77,"0x8000004d","XVG"), - Entry(78,"0x8000004e","EFL"), - Entry(79,"0x8000004f","CLUB"), - Entry(80,"0x80000050","RICHX"), - Entry(81,"0x80000051","POT"), - Entry(82,"0x80000052","QRK"), - Entry(83,"0x80000053","TRC"), - Entry(84,"0x80000054","GRC"), - Entry(85,"0x80000055","AUR"), - Entry(86,"0x80000056","IXC"), - Entry(87,"0x80000057","NLG"), - Entry(88,"0x80000058","BITB"), - Entry(89,"0x80000059","BTA"), - Entry(90,"0x8000005a","XMY"), - Entry(91,"0x8000005b","BSD"), - Entry(92,"0x8000005c","UNO"), - Entry(93,"0x8000005d","MTR"), - Entry(94,"0x8000005e","GB"), - Entry(95,"0x8000005f","SHM"), - Entry(96,"0x80000060","CRX"), - Entry(97,"0x80000061","BIQ"), - Entry(98,"0x80000062","EVO"), - Entry(99,"0x80000063","STO"), - Entry(100,"0x80000064","BIGUP"), - Entry(101,"0x80000065","GAME"), - Entry(102,"0x80000066","DLC"), - Entry(103,"0x80000067","ZYD"), - Entry(104,"0x80000068","DBIC"), - Entry(105,"0x80000069","STRAT"), - Entry(106,"0x8000006a","SH"), - Entry(107,"0x8000006b","MARS"), - Entry(108,"0x8000006c","UBQ"), - Entry(109,"0x8000006d","PTC"), - Entry(110,"0x8000006e","NRO"), - Entry(111,"0x8000006f","ARK"), - Entry(112,"0x80000070","USC"), - Entry(113,"0x80000071","THC"), - Entry(114,"0x80000072","LINX"), - Entry(115,"0x80000073","ECN"), - Entry(116,"0x80000074","DNR"), - Entry(117,"0x80000075","PINK"), - Entry(118,"0x80000076","ATOM"), - Entry(119,"0x80000077","PIVX"), - Entry(120,"0x80000078","FLASH"), - Entry(121,"0x80000079","ZEN"), - Entry(122,"0x8000007a","PUT"), - Entry(123,"0x8000007b","ZNY"), - Entry(124,"0x8000007c","UNIFY"), - Entry(125,"0x8000007d","XST"), - Entry(126,"0x8000007e","BRK"), - Entry(127,"0x8000007f","VC"), - Entry(128,"0x80000080","XMR"), - Entry(129,"0x80000081","VOX"), - Entry(130,"0x80000082","NAV"), - Entry(131,"0x80000083","FCT"), - Entry(132,"0x80000084","EC"), - Entry(133,"0x80000085","ZEC"), - Entry(134,"0x80000086","LSK"), - Entry(135,"0x80000087","STEEM"), - Entry(136,"0x80000088","XZC"), - Entry(137,"0x80000089","RBTC"), - Entry(139,"0x8000008b","RPT"), - Entry(140,"0x8000008c","LBC"), - Entry(141,"0x8000008d","KMD"), - Entry(142,"0x8000008e","BSQ"), - Entry(143,"0x8000008f","RIC"), - Entry(144,"0x80000090","XRP"), - Entry(145,"0x80000091","BCH"), - Entry(146,"0x80000092","NEBL"), - Entry(147,"0x80000093","ZCL"), - Entry(148,"0x80000094","XLM"), - Entry(149,"0x80000095","NLC2"), - Entry(150,"0x80000096","WHL"), - Entry(151,"0x80000097","ERC"), - Entry(152,"0x80000098","DMD"), - Entry(153,"0x80000099","BTM"), - Entry(154,"0x8000009a","BIO"), - Entry(155,"0x8000009b","XWC"), - Entry(156,"0x8000009c","BTG"), - Entry(157,"0x8000009d","BTC2X"), - Entry(158,"0x8000009e","SSN"), - Entry(159,"0x8000009f","TOA"), - Entry(160,"0x800000a0","BTX"), - Entry(161,"0x800000a1","ACC"), - Entry(162,"0x800000a2","BCO"), - Entry(163,"0x800000a3","ELLA"), - Entry(164,"0x800000a4","PIRL"), - Entry(165,"0x800000a5","XRB"), - Entry(166,"0x800000a6","VIVO"), - Entry(167,"0x800000a7","FRST"), - Entry(168,"0x800000a8","HNC"), - Entry(169,"0x800000a9","BUZZ"), - Entry(170,"0x800000aa","MBRS"), - Entry(171,"0x800000ab","HSR"), - Entry(172,"0x800000ac","HTML"), - Entry(173,"0x800000ad","ODN"), - Entry(174,"0x800000ae","ONX"), - Entry(175,"0x800000af","RVN"), - Entry(176,"0x800000b0","GBX"), - Entry(177,"0x800000b1","BTCZ"), - Entry(178,"0x800000b2","POA"), - Entry(179,"0x800000b3","NYC"), - Entry(180,"0x800000b4","MXT"), - Entry(181,"0x800000b5","WC"), - Entry(182,"0x800000b6","MNX"), - Entry(183,"0x800000b7","BTCP"), - Entry(184,"0x800000b8","MUSIC"), - Entry(185,"0x800000b9","BCA"), - Entry(186,"0x800000ba","CRAVE"), - Entry(187,"0x800000bb","STAK"), - Entry(188,"0x800000bc","WBTC"), - Entry(189,"0x800000bd","LCH"), - Entry(190,"0x800000be","EXCL"), - Entry(192,"0x800000c0","LCC"), - Entry(193,"0x800000c1","XFE"), - Entry(194,"0x800000c2","EOS"), - Entry(195,"0x800000c3","TRX"), - Entry(196,"0x800000c4","KOBO"), - Entry(197,"0x800000c5","HUSH"), - Entry(198,"0x800000c6","BANANO"), - Entry(199,"0x800000c7","ETF"), - Entry(200,"0x800000c8","OMNI"), - Entry(201,"0x800000c9","BIFI"), - Entry(202,"0x800000ca","UFO"), - Entry(203,"0x800000cb","CNMC"), - Entry(204,"0x800000cc","BCN"), - Entry(205,"0x800000cd","RIN"), - Entry(206,"0x800000ce","ATP"), - Entry(207,"0x800000cf","EVT"), - Entry(208,"0x800000d0","ATN"), - Entry(209,"0x800000d1","BIS"), - Entry(210,"0x800000d2","NEET"), - Entry(211,"0x800000d3","BOPO"), - Entry(212,"0x800000d4","OOT"), - Entry(213,"0x800000d5","XSPEC"), - Entry(214,"0x800000d5","MONK"), - Entry(215,"0x800000d7","BOXY"), - Entry(216,"0x800000d8","FLO"), - Entry(217,"0x800000d9","MEC"), - Entry(218,"0x800000da","BTDX"), - Entry(219,"0x800000db","XAX"), - Entry(220,"0x800000dc","ANON"), - Entry(221,"0x800000dd","LTZ"), - Entry(222,"0x800000de","BITG"), - Entry(223,"0x800000df","ASK"), - Entry(224,"0x800000e0","SMART"), - Entry(225,"0x800000e1","XUEZ"), - Entry(226,"0x800000e2","HLM"), - Entry(227,"0x800000e3","WEB"), - Entry(228,"0x800000e4","ACM"), - Entry(229,"0x800000e5","NOS"), - Entry(230,"0x800000e6","BITC"), - Entry(231,"0x800000e7","HTH"), - Entry(232,"0x800000e8","TZC"), - Entry(233,"0x800000e9","VAR"), - Entry(234,"0x800000ea","IOV"), - Entry(235,"0x800000eb","FIO"), - Entry(236,"0x800000ec","BSV"), - Entry(237,"0x800000ed","DXN"), - Entry(238,"0x800000ee","QRL"), - Entry(239,"0x800000ef","PCX"), - Entry(240,"0x800000f0","LOKI"), - Entry(242,"0x800000f2","NIM"), - Entry(243,"0x800000f3","SOV"), - Entry(244,"0x800000f4","JCT"), - Entry(245,"0x800000f5","SLP"), - Entry(246,"0x800000f6","EWT"), - Entry(247,"0x800000f7","UC"), - Entry(248,"0x800000f8","EXOS"), - Entry(249,"0x800000f9","ECA"), - Entry(250,"0x800000fa","SOOM"), - Entry(251,"0x800000fb","XRD"), - Entry(252,"0x800000fc","FREE"), - Entry(253,"0x800000fd","NPW"), - Entry(254,"0x800000fe","BST"), - Entry(256,"0x80000100","NANO"), - Entry(257,"0x80000101","BTCC"), - Entry(259,"0x80000103","ZEST"), - Entry(260,"0x80000104","ABT"), - Entry(261,"0x80000105","PION"), - Entry(262,"0x80000106","DT3"), - Entry(263,"0x80000107","ZBUX"), - Entry(264,"0x80000108","KPL"), - Entry(265,"0x80000109","TPAY"), - Entry(266,"0x8000010a","ZILLA"), - Entry(267,"0x8000010b","ANK"), - Entry(268,"0x8000010c","BCC"), - Entry(269,"0x8000010d","HPB"), - Entry(270,"0x8000010e","ONE"), - Entry(271,"0x8000010f","SBC"), - Entry(272,"0x80000110","IPC"), - Entry(273,"0x80000111","DMTC"), - Entry(274,"0x80000112","OGC"), - Entry(275,"0x80000113","SHIT"), - Entry(276,"0x80000114","ANDES"), - Entry(277,"0x80000115","AREPA"), - Entry(278,"0x80000116","BOLI"), - Entry(279,"0x80000117","RIL"), - Entry(280,"0x80000118","HTR"), - Entry(281,"0x80000119","FCTID"), - Entry(282,"0x8000011a","BRAVO"), - Entry(283,"0x8000011b","ALGO"), - Entry(284,"0x8000011c","BZX"), - Entry(285,"0x8000011d","GXX"), - Entry(286,"0x8000011e","HEAT"), - Entry(287,"0x8000011f","XDN"), - Entry(288,"0x80000120","FSN"), - Entry(289,"0x80000121","CPC"), - Entry(290,"0x80000122","BOLD"), - Entry(291,"0x80000123","IOST"), - Entry(292,"0x80000124","TKEY"), - Entry(293,"0x80000125","USE"), - Entry(294,"0x80000126","BCZ"), - Entry(295,"0x80000127","IOC"), - Entry(296,"0x80000128","ASF"), - Entry(297,"0x80000129","MASS"), - Entry(298,"0x8000012a","FAIR"), - Entry(299,"0x8000012b","NUKO"), - Entry(300,"0x8000012c","GNX"), - Entry(301,"0x8000012d","DIVI"), - Entry(302,"0x8000012e","CMT"), - Entry(303,"0x8000012f","EUNO"), - Entry(304,"0x80000130","IOTX"), - Entry(305,"0x80000131","ONION"), - Entry(306,"0x80000132","8BIT"), - Entry(307,"0x80000133","ATC"), - Entry(308,"0x80000134","BTS"), - Entry(309,"0x80000135","CKB"), - Entry(310,"0x80000136","UGAS"), - Entry(311,"0x80000137","ADS"), - Entry(312,"0x80000138","ARA"), - Entry(313,"0x80000139","ZIL"), - Entry(314,"0x8000013a","MOAC"), - Entry(315,"0x8000013b","SWTC"), - Entry(316,"0x8000013c","VNSC"), - Entry(317,"0x8000013d","PLUG"), - Entry(318,"0x8000013e","MAN"), - Entry(319,"0x8000013f","ECC"), - Entry(320,"0x80000140","RPD"), - Entry(321,"0x80000141","RAP"), - Entry(322,"0x80000142","GARD"), - Entry(323,"0x80000143","ZER"), - Entry(324,"0x80000144","EBST"), - Entry(325,"0x80000145","SHARD"), - Entry(326,"0x80000146","LINDA"), - Entry(327,"0x80000147","CMM"), - Entry(328,"0x80000148","BLOCK"), - Entry(329,"0x80000149","AUDAX"), - Entry(330,"0x8000014a","LUNA"), - Entry(331,"0x8000014b","ZPM"), - Entry(332,"0x8000014c","KUVA"), - Entry(333,"0x8000014d","MEM"), - Entry(334,"0x8000014e","CS"), - Entry(335,"0x8000014f","SWIFT"), - Entry(336,"0x80000150","FIX"), - Entry(337,"0x80000151","CPC"), - Entry(338,"0x80000152","VGO"), - Entry(339,"0x80000153","DVT"), - Entry(340,"0x80000154","N8V"), - Entry(341,"0x80000155","MTNS"), - Entry(342,"0x80000156","BLAST"), - Entry(343,"0x80000157","DCT"), - Entry(344,"0x80000158","AUX"), - Entry(345,"0x80000159","USDP"), - Entry(346,"0x8000015a","HTDF"), - Entry(347,"0x8000015b","YEC"), - Entry(348,"0x8000015c","QLC"), - Entry(349,"0x8000015d","TEA"), - Entry(350,"0x8000015e","ARW"), - Entry(351,"0x8000015f","MDM"), - Entry(352,"0x80000160","CYB"), - Entry(353,"0x80000161","LTO"), - Entry(354,"0x80000162","DOT"), - Entry(355,"0x80000163","AEON"), - Entry(356,"0x80000164","RES"), - Entry(357,"0x80000165","AYA"), - Entry(358,"0x80000166","DAPS"), - Entry(359,"0x80000167","CSC"), - Entry(360,"0x80000168","VSYS"), - Entry(361,"0x80000169","NOLLAR"), - Entry(362,"0x8000016a","XNOS"), - Entry(363,"0x8000016b","CPU"), - Entry(364,"0x8000016c","LAMB"), - Entry(365,"0x8000016d","VCT"), - Entry(366,"0x8000016e","CZR"), - Entry(367,"0x8000016f","ABBC"), - Entry(368,"0x80000170","HET"), - Entry(369,"0x80000171","XAS"), - Entry(370,"0x80000172","VDL"), - Entry(371,"0x80000173","MED"), - Entry(372,"0x80000174","ZVC"), - Entry(379,"0x8000017b","SOX"), - Entry(384,"0x80000180","XSN"), - Entry(392,"0x80000188","CENNZ"), - Entry(398,"0x8000018e","XPC"), - Entry(400,"0x80000190","NIX"), - Entry(404,"0x80000194","XBI"), - Entry(412,"0x8000019c","AIN"), - Entry(416,"0x800001a0","SLX"), - Entry(420,"0x800001a4","NODE"), - Entry(425,"0x800001a9","AION"), - Entry(426,"0x800001aa","BC"), - Entry(444,"0x800001bc","PHR"), - Entry(447,"0x800001bf","DIN"), - Entry(457,"0x800001c9","AE"), - Entry(464,"0x800001d0","ETI"), - Entry(488,"0x800001e8","VEO"), - Entry(500,"0x800001f4","THETA"), - Entry(510,"0x800001fe","KOTO"), - Entry(512,"0x80000200","XRD"), - Entry(516,"0x80000204","VEE"), - Entry(518,"0x80000206","LET"), - Entry(520,"0x80000208","BTCV"), - Entry(526,"0x8000020e","BU"), - Entry(528,"0x80000210","YAP"), - Entry(533,"0x80000215","PRJ"), - Entry(555,"0x8000022b","BCS"), - Entry(557,"0x8000022d","LKR"), - Entry(561,"0x80000231","NTY"), - Entry(600,"0x80000258","UTE"), - Entry(618,"0x8000026a","SSP"), - Entry(625,"0x80000271","EAST"), - Entry(663,"0x80000297","SFRX"), - Entry(666,"0x8000029a","ACT"), - Entry(667,"0x8000029b","PRKL"), - Entry(668,"0x8000029c","SSC"), - Entry(698,"0x800002ba","VEIL"), - Entry(700,"0x800002bc","XDAI"), - Entry(713,"0x800002c9","XTL"), - Entry(714,"0x800002ca","BNB"), - Entry(768,"0x80000300","BALLZ"), - Entry(777,"0x80000309","BTW"), - Entry(800,"0x80000320","BEET"), - Entry(801,"0x80000321","DST"), - Entry(808,"0x80000328","QVT"), - Entry(818,"0x80000332","VET"), - Entry(820,"0x80000334","CLO"), - Entry(831,"0x8000033f","CRUZ"), - Entry(886,"0x80000376","ADF"), - Entry(888,"0x80000378","NEO"), - Entry(889,"0x80000379","TOMO"), - Entry(890,"0x8000037a","XSEL"), - Entry(900,"0x80000384","LMO"), - Entry(916,"0x80000394","META"), - Entry(970,"0x800003ca","TWINS"), - Entry(996,"0x800003e4","OKP"), - Entry(997,"0x800003e5","SUM"), - Entry(998,"0x800003e6","LBTC"), - Entry(999,"0x800003e7","BCD"), - Entry(1000,"0x800003e8","BTN"), - Entry(1001,"0x800003e9","TT"), - Entry(1002,"0x800003ea","BKT"), - Entry(1024,"0x80000400","ONT"), - Entry(1111,"0x80000457","BBC"), - Entry(1120,"0x80000460","RISE"), - Entry(1122,"0x80000462","CMT"), - Entry(1128,"0x80000468","ETSC"), - Entry(1145,"0x80000479","CDY"), - Entry(1337,"0x80000539","DFC"), - Entry(1397,"0x80000575","HYC"), - Entry(1616,"0x80000650","ELF"), - Entry(1620,"0x80000654","ATH"), - Entry(1688,"0x80000698","BCX"), - Entry(1729,"0x800006c1","XTZ"), - Entry(1776,"0x800006f0","LBTC"), - Entry(1815,"0x80000717","ADA"), - Entry(1856,"0x80000743","TES"), - Entry(1901,"0x8000076d","CLC"), - Entry(1919,"0x8000077f","VIPS"), - Entry(1926,"0x80000786","CITY"), - Entry(1977,"0x800007b9","XMX"), - Entry(1984,"0x800007c0","TRTL"), - Entry(1987,"0x800007c3","EGEM"), - Entry(1989,"0x800007c5","HODL"), - Entry(1990,"0x800007c6","PHL"), - Entry(1997,"0x800007cd","POLIS"), - Entry(1998,"0x800007ce","XMCC"), - Entry(1999,"0x800007cf","COLX"), - Entry(2000,"0x800007d0","GIN"), - Entry(2001,"0x800007d1","MNP"), - Entry(2017,"0x800007e1","KIN"), - Entry(2018,"0x800007e2","EOSC"), - Entry(2019,"0x800007e3","GBT"), - Entry(2020,"0x800007e4","PKC"), - Entry(2048,"0x80000800","MCASH"), - Entry(2049,"0x80000801","TRUE"), - Entry(2112,"0x80000840","IoTE"), - Entry(2221,"0x800008ad","ASK"), - Entry(2301,"0x800008fd","QTUM"), - Entry(2302,"0x800008fe","ETP"), - Entry(2303,"0x800008ff","GXC"), - Entry(2304,"0x80000900","CRP"), - Entry(2305,"0x80000901","ELA"), - Entry(2338,"0x80000922","SNOW"), - Entry(2570,"0x80000a0a","AOA"), - Entry(2894,"0x80000b4e","REOSC"), - Entry(3003,"0x80000bbb","LUX"), - Entry(3030,"0x80000bd6","XHB"), - Entry(3381,"0x80000d35","DYN"), - Entry(3383,"0x80000d37","SEQ"), - Entry(3552,"0x80000de0","DEO"), - Entry(3564,"0x80000dec","DST"), - Entry(2718,"0x80000a9e","NAS"), - Entry(2941,"0x80000b7d","BND"), - Entry(3276,"0x80000ccc","CCC"), - Entry(3377,"0x80000d31","ROI"), - Entry(4218,"0x8000107a","IOTA"), - Entry(4242,"0x80001092","AXE"), - Entry(5248,"0x00001480","FIC"), - Entry(5353,"0x000014e9","HNS"), - Entry(5920,"0x80001720","SLU"), - Entry(6060,"0x800017ac","GO"), - Entry(6666,"0x80001a0a","BPA"), - Entry(6688,"0x80001a20","SAFE"), - Entry(6969,"0x80001b39","ROGER"), - Entry(7777,"0x80001e61","BTV"), - Entry(8339,"0x80002093","BTQ"), - Entry(8888,"0x800022b8","SBTC"), - Entry(8964,"0x80002304","NULS"), - Entry(8999,"0x80002327","BTP"), - Entry(9797,"0x80002645","NRG"), - Entry(9888,"0x800026a0","BTF"), - Entry(9999,"0x8000270f","GOD"), - Entry(10000,"0x80002710","FO"), - Entry(10291,"0x80002833","BTR"), - Entry(11111,"0x80002B67","ESS"), - Entry(12345,"0x80003039","IPOS"), - Entry(13107,"0x80003333","BTY"), - Entry(13108,"0x80003334","YCC"), - Entry(15845,"0x80003de5","SDGO"), - Entry(16754,"0x80004172","ARDR"), - Entry(19165,"0x80004add","SAFE"), - Entry(19167,"0x80004adf","ZEL"), - Entry(19169,"0x80004ae1","RITO"), - Entry(20036,"0x80004e44","XND"), - Entry(22504,"0x800057e8","PWR"), - Entry(25252,"0x800062a4","BELL"), - Entry(25718,"0x80006476","CHX"), - Entry(31102,"0x8000797e","ESN"), - Entry(33416,"0x80008288","TEO"), - Entry(33878,"0x80008456","BTCS"), - Entry(34952,"0x80008888","BTT"), - Entry(37992,"0x80009468","FXTC"), - Entry(39321,"0x80009999","AMA"), - Entry(49344,"0x0000C0C0","STASH"), - Entry(65536,"0x80010000","KETH"), - Entry(88888,"0x80015b38","RYO"), - Entry(99999,"0x8001869f","WICC"), - Entry(200625,"0x80030fb1","AKA"), - Entry(200665,"0x80011000","GENOM"), - Entry(246529,"0x8003C301","ATS"), - Entry(424242,"0x80067932","X42"), - Entry(666666,"0x800a2c2a","VITE"), - Entry(1171337,"0x8011df89","ILT"), - Entry(1313114,"0x8014095a","ETHO"), - Entry(1712144,"0x801a2010","LAX"), - Entry(5249353,"0x80501949","BCO"), - Entry(5249354,"0x8050194a","BHD"), - Entry(5264462,"0x8050544e","PTN"), - Entry(5718350,"0x8057414e","WAN"), - Entry(5741564,"0x80579bfc","WAVES"), - Entry(7562605,"0x8073656d","SEM"), - Entry(7567736,"0x80737978","ION"), - Entry(7825266,"0x80776772","WGR"), - Entry(7825267,"0x80776773","OBSR"), - Entry(61717561,"0x83adbc39","AQUA"), - Entry(91927009,"0x857ab1e1","kUSD"), - Entry(99999998,"0x85f5e0fe","FLUID"), - Entry(99999999,"0x85f5e0ff","QKC") - ) - - val byIndex: Map - - val byWalletIndex: Map - - val bySymbol: Map - - val byHex: Map - - init { - byIndex = FrozenHashMap { - val map = it - entries.forEach { map[it.index] = it } - } - - byWalletIndex = FrozenHashMap { - val map = it - entries.forEach { map[it.walletIndex] = it } - } - - bySymbol = FrozenHashMap { - val map = it - entries.forEach { map[it.symbol] = it } - } - - byHex = FrozenHashMap { - val map = it - entries.forEach { map[it.hex] = it } - } - } -} diff --git a/android-client/app/src/main/java/org/hermes/hd/btc.kt b/android-client/app/src/main/java/org/hermes/hd/btc.kt deleted file mode 100644 index 918ae7e..0000000 --- a/android-client/app/src/main/java/org/hermes/hd/btc.kt +++ /dev/null @@ -1,67 +0,0 @@ -package org.hermes.hd - -import java.security.MessageDigest - -import org.bouncycastle.util.encoders.Base64 -import org.bouncycastle.util.encoders.Hex - -import org.hermes.crypto.NoEvenBit -import org.hermes.crypto.SecP256K1PrivKey -import org.hermes.utils.extendOrReduceTo -import org.hermes.utils.toByteArray - -enum class BTCKey(val prefix: String) { - PUBLIC_MAIN_NET("0488b21e"), - PRIVATE_MAIN_NET("0488ade4"), - PUBLIC_TEST_NET("043587cf"), - PRIVATE_TEST_NET("04358394") -} - -object BTCKeyEncoder: KeyEncoder() { - // TODO: Find a way to remove the options map - override fun encodePrivateKey(key: ExPrivKey, options: Map): String = - Base58.toBase58String( - Hex.decode(BTCKey.PRIVATE_MAIN_NET.prefix) + - key.depth.toByteArray() + - (key.parent?.public?.fingerprint ?: Hex.decode("00000000")) + - key.index.toByteArray().extendOrReduceTo(4, padStart = true) + - key.chainCode + - key.value.toByteArray().extendOrReduceTo(33, padStart = true), - appendChecksum = true - ) - - override fun encodePublicKey(key: ExPubKey, options: Map): String = - Base58.toBase58String( - Hex.decode(BTCKey.PUBLIC_MAIN_NET.prefix) + - key.depth.toByteArray() + - (key.parent?.fingerprint ?: Hex.decode("00000000")) + - key.index.toByteArray().extendOrReduceTo(4, padStart = true) + - key.chainCode + - key.encoded, - appendChecksum = true - ) - - override fun address(key: ExPubKey, options: Map): String = - Base58.toBase58String(ByteArray(1) + key.identifier, appendChecksum = true) -} - - -object BTCSigner: BaseECDSASigner() { - - override fun sign(key: SecP256K1PrivKey, data: ByteArray): ByteArray{ - val signature = chainSign( - key = key, - prefix = "Bitcoin Signed Message:\n", - data = data, - hashRounds = 2, - digest = MessageDigest.getInstance("SHA-256") - ) - - if (signature.vb == null) throw NoEvenBit() - val completeArray = ByteArray(65) - completeArray[0] = signature.vb - System.arraycopy(signature.rb, 0, completeArray, 1, 32) - System.arraycopy(signature.canonicalSb, 0, completeArray, 33, 32) - return Base64.encode(completeArray) - } -} diff --git a/android-client/app/src/main/java/org/hermes/hd/eth.kt b/android-client/app/src/main/java/org/hermes/hd/eth.kt deleted file mode 100644 index a62af24..0000000 --- a/android-client/app/src/main/java/org/hermes/hd/eth.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.hermes.hd - -import org.bouncycastle.util.encoders.Hex - -import org.hermes.crypto.NoEvenBit -import org.hermes.crypto.Keccak -import org.hermes.crypto.SecP256K1PrivKey - - -object ETHKeyEncoder: KeyEncoder() { - override fun encodePrivateKey(key: ExPrivKey, options: Map): String = - // Drop the 0x00 byte at the beginning - Hex.toHexString(key.value.toByteArray().drop(1).toByteArray()) - - override fun encodePublicKey(key: ExPubKey, options: Map): String = - // Drop the 0x04 byte at the beginning - Hex.toHexString(key.bcPoint.getEncoded(false).drop(1).toByteArray()) - - override fun address(key: ExPubKey, options: Map): String = - Hex.toHexString(Keccak.hash(key.bcPoint.getEncoded(false).drop(1).toByteArray())) - .drop(24) -} - -object ETHSigner: BaseECDSASigner() { - override fun sign(key: SecP256K1PrivKey, data: ByteArray): ByteArray { - val signature = chainSign( - key = key, - prefix = "Ethereum Signed Message:\n", - data = data, - digest = org.bouncycastle.jcajce.provider.digest.Keccak.Digest256() - ) - - if (signature.vb == null) throw NoEvenBit() - val completeArray = ByteArray(65) - System.arraycopy(signature.rb, 0, completeArray, 0, 32) - System.arraycopy(signature.sb, 0, completeArray, 32, 32) - completeArray[64] = signature.vb - return completeArray - } -} diff --git a/android-client/app/src/main/java/org/hermes/hd/iota.kt b/android-client/app/src/main/java/org/hermes/hd/iota.kt deleted file mode 100644 index 8661406..0000000 --- a/android-client/app/src/main/java/org/hermes/hd/iota.kt +++ /dev/null @@ -1,158 +0,0 @@ -package org.hermes.hd - -import java.security.NoSuchAlgorithmException -import javax.crypto.SecretKeyFactory -import javax.crypto.spec.PBEKeySpec - -import org.iota.jota.pow.SpongeFactory -import org.iota.jota.utils.Checksum -import org.iota.jota.utils.Converter -import org.iota.jota.utils.Signing - -import org.hermes.crypto.CryptoAlgorithms -import org.hermes.iota.Seed -import org.hermes.iota.TryteArray -import org.hermes.utils.extendOrReduceTo -import org.hermes.utils.toByteArray -import org.hermes.utils.toTritArray -import org.hermes.utils.toTryteArray - - -class IOTAExPrivKey( - override val path: String, - override val parent: BIP32Key?, - override val chainCode: ByteArray, - val value: TryteArray -) : BIP32Key { - - companion object { - - /** - * Produces a child key for the IOTA network. - * - * Produces a child key, from a parent, private or public, depending - * on whether or not the the child is hardened. - * The chaincode is combined with the key and then fed into a PBKDF2 - * function that will stretch it to the appropriate length. This is - * necessary because the key might be from a non-IOTA parent and hence - * will not be at the proper size. - * Once the key has been produced it is split into two chunks, a - * 32-byte chaincode and a 42-byte IOTA seed. The IOTA seed is then - * used in combination with the index to produce a private key for the - * child. This way the seed for a chain of keys can be exported and - * fed to the standard Trinity wallet and allow that wallet to recreate - * this chain of keys and thus track the addresses used. - */ - fun CKDpriv( - childIndex: Long, - key: ByteArray, - publicKey: ByteArray, - chainCode: ByteArray - ): Pair { - val I = BIP32Seed(childIndex, key, publicKey, chainCode) - val seed = I.sliceArray(0 until 41) - .toTryteArray() - .toTritIntArray() - .sliceArray(0 until Seed.MAX_SEED_LENGTH * 3) - val newKey = PrivKey(seed, childIndex) - return Pair(I.sliceArray(42 until 74), newKey) - } - - fun PrivKey(seed: IntArray, childIndex: Long): IntArray { - val signing = Signing(SpongeFactory.create(SpongeFactory.Mode.KERL)) - // Indices can be reused because the hardened key seed will be different than the non-hardened one. - // This is done because IOTA can not handle indices that are too large. - val index = when (childIndex >= BIP32.HARDENED_KEY_OFFSET) { - true -> childIndex - BIP32.HARDENED_KEY_OFFSET - else -> childIndex - }.toInt() - return signing.key(seed, index, Seed.DEFAULT_SEED_SECURITY) - } - - /** - * Produces a seed in a way that resembles the BIP32 spec. The parent key can be - * either another IOTAExPrivKey or any BIP32 key. - */ - fun BIP32Seed( - childIndex: Long, - key: ByteArray, - publicKey: ByteArray, - chainCode: ByteArray - ): ByteArray { - if ((childIndex < 0) or (childIndex >= BIP32.MAX_KEY_INDEX)) - throw BIP32Key.InvalidIndex() - - val spec = PBEKeySpec( - when (childIndex >= BIP32.HARDENED_KEY_OFFSET) { - true -> key + childIndex.toByteArray().extendOrReduceTo(4, padStart = true) - else -> publicKey + childIndex.toByteArray().extendOrReduceTo(4, padStart = true) - } - .map { it.toChar() } - .toCharArray(), - chainCode, - 10000, - 74 * 8 - ) - return try { - SecretKeyFactory.getInstance(CryptoAlgorithms.PBKDF2_HMC_SHA512) - } catch (e: NoSuchAlgorithmException) { - // TODO: Do some proper exception handling here - throw e - }.generateSecret(spec).encoded - } - } - - override fun child(index: Long): BIP32Key { - val (newChainCode, newKey) = CKDpriv(index, value.toByteArray(), public.encoded, chainCode) - - return IOTAExPrivKey( - BIP32.pathOfChild(path, index), - this, - newChainCode, - newKey.toTritArray().toTryteArray() - ) - } - - override val public: BIP32PubKey by lazy { - val pubKeyTrits = Signing(SpongeFactory.create(SpongeFactory.Mode.KERL)).digests(value.toTritIntArray()) - IOTAExPubKey(parent?.public, chainCode, path, pubKeyTrits.toTritArray().toTryteArray()) - } - - override fun getAlgorithm(): String = "Kerl" - - override fun getEncoded(): ByteArray = value.toByteArray() - - override fun getFormat(): String = "ASN.1" -} - -class IOTAExPubKey( - override val parent: BIP32PubKey?, - override val chainCode: ByteArray, - override val path: String, - val value: TryteArray -) : BIP32PubKey { - - /** - * The address without any checksum - */ - val addressTrits: IntArray by lazy { - val signing = Signing(SpongeFactory.create(SpongeFactory.Mode.KERL)) - signing.address(value.toTritIntArray()) - } - - /** - * Returns an IOTA compatible address with the checksum appended to the trits - */ - override val address: String by lazy { Checksum.addChecksum(Converter.trytes(addressTrits)) } - - override fun child(index: Long): BIP32PubKey { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getAlgorithm(): String = "Kerl" - - override fun getEncoded(): ByteArray = value.toByteArray() - - // TODO: fix this - override fun getFormat(): String = "ASN.1" -} diff --git a/android-client/app/src/main/java/org/hermes/hd/wordlist.kt b/android-client/app/src/main/java/org/hermes/hd/wordlist.kt deleted file mode 100644 index 35a3ccd..0000000 --- a/android-client/app/src/main/java/org/hermes/hd/wordlist.kt +++ /dev/null @@ -1,2061 +0,0 @@ -package org.hermes.hd - -object EnglishWordlist { - val items = arrayOf( - "abandon", - "ability", - "able", - "about", - "above", - "absent", - "absorb", - "abstract", - "absurd", - "abuse", - "access", - "accident", - "account", - "accuse", - "achieve", - "acid", - "acoustic", - "acquire", - "across", - "act", - "action", - "actor", - "actress", - "actual", - "adapt", - "add", - "addict", - "address", - "adjust", - "admit", - "adult", - "advance", - "advice", - "aerobic", - "affair", - "afford", - "afraid", - "again", - "age", - "agent", - "agree", - "ahead", - "aim", - "air", - "airport", - "aisle", - "alarm", - "album", - "alcohol", - "alert", - "alien", - "all", - "alley", - "allow", - "almost", - "alone", - "alpha", - "already", - "also", - "alter", - "always", - "amateur", - "amazing", - "among", - "amount", - "amused", - "analyst", - "anchor", - "ancient", - "anger", - "angle", - "angry", - "animal", - "ankle", - "announce", - "annual", - "another", - "answer", - "antenna", - "antique", - "anxiety", - "any", - "apart", - "apology", - "appear", - "apple", - "approve", - "april", - "arch", - "arctic", - "area", - "arena", - "argue", - "arm", - "armed", - "armor", - "army", - "around", - "arrange", - "arrest", - "arrive", - "arrow", - "art", - "artefact", - "artist", - "artwork", - "ask", - "aspect", - "assault", - "asset", - "assist", - "assume", - "asthma", - "athlete", - "atom", - "attack", - "attend", - "attitude", - "attract", - "auction", - "audit", - "august", - "aunt", - "author", - "auto", - "autumn", - "average", - "avocado", - "avoid", - "awake", - "aware", - "away", - "awesome", - "awful", - "awkward", - "axis", - "baby", - "bachelor", - "bacon", - "badge", - "bag", - "balance", - "balcony", - "ball", - "bamboo", - "banana", - "banner", - "bar", - "barely", - "bargain", - "barrel", - "base", - "basic", - "basket", - "battle", - "beach", - "bean", - "beauty", - "because", - "become", - "beef", - "before", - "begin", - "behave", - "behind", - "believe", - "below", - "belt", - "bench", - "benefit", - "best", - "betray", - "better", - "between", - "beyond", - "bicycle", - "bid", - "bike", - "bind", - "biology", - "bird", - "birth", - "bitter", - "black", - "blade", - "blame", - "blanket", - "blast", - "bleak", - "bless", - "blind", - "blood", - "blossom", - "blouse", - "blue", - "blur", - "blush", - "board", - "boat", - "body", - "boil", - "bomb", - "bone", - "bonus", - "book", - "boost", - "border", - "boring", - "borrow", - "boss", - "bottom", - "bounce", - "box", - "boy", - "bracket", - "brain", - "brand", - "brass", - "brave", - "bread", - "breeze", - "brick", - "bridge", - "brief", - "bright", - "bring", - "brisk", - "broccoli", - "broken", - "bronze", - "broom", - "brother", - "brown", - "brush", - "bubble", - "buddy", - "budget", - "buffalo", - "build", - "bulb", - "bulk", - "bullet", - "bundle", - "bunker", - "burden", - "burger", - "burst", - "bus", - "business", - "busy", - "butter", - "buyer", - "buzz", - "cabbage", - "cabin", - "cable", - "cactus", - "cage", - "cake", - "call", - "calm", - "camera", - "camp", - "can", - "canal", - "cancel", - "candy", - "cannon", - "canoe", - "canvas", - "canyon", - "capable", - "capital", - "captain", - "car", - "carbon", - "card", - "cargo", - "carpet", - "carry", - "cart", - "case", - "cash", - "casino", - "castle", - "casual", - "cat", - "catalog", - "catch", - "category", - "cattle", - "caught", - "cause", - "caution", - "cave", - "ceiling", - "celery", - "cement", - "census", - "century", - "cereal", - "certain", - "chair", - "chalk", - "champion", - "change", - "chaos", - "chapter", - "charge", - "chase", - "chat", - "cheap", - "check", - "cheese", - "chef", - "cherry", - "chest", - "chicken", - "chief", - "child", - "chimney", - "choice", - "choose", - "chronic", - "chuckle", - "chunk", - "churn", - "cigar", - "cinnamon", - "circle", - "citizen", - "city", - "civil", - "claim", - "clap", - "clarify", - "claw", - "clay", - "clean", - "clerk", - "clever", - "click", - "client", - "cliff", - "climb", - "clinic", - "clip", - "clock", - "clog", - "close", - "cloth", - "cloud", - "clown", - "club", - "clump", - "cluster", - "clutch", - "coach", - "coast", - "coconut", - "code", - "coffee", - "coil", - "coin", - "collect", - "color", - "column", - "combine", - "come", - "comfort", - "comic", - "common", - "company", - "concert", - "conduct", - "confirm", - "congress", - "connect", - "consider", - "control", - "convince", - "cook", - "cool", - "copper", - "copy", - "coral", - "core", - "corn", - "correct", - "cost", - "cotton", - "couch", - "country", - "couple", - "course", - "cousin", - "cover", - "coyote", - "crack", - "cradle", - "craft", - "cram", - "crane", - "crash", - "crater", - "crawl", - "crazy", - "cream", - "credit", - "creek", - "crew", - "cricket", - "crime", - "crisp", - "critic", - "crop", - "cross", - "crouch", - "crowd", - "crucial", - "cruel", - "cruise", - "crumble", - "crunch", - "crush", - "cry", - "crystal", - "cube", - "culture", - "cup", - "cupboard", - "curious", - "current", - "curtain", - "curve", - "cushion", - "custom", - "cute", - "cycle", - "dad", - "damage", - "damp", - "dance", - "danger", - "daring", - "dash", - "daughter", - "dawn", - "day", - "deal", - "debate", - "debris", - "decade", - "december", - "decide", - "decline", - "decorate", - "decrease", - "deer", - "defense", - "define", - "defy", - "degree", - "delay", - "deliver", - "demand", - "demise", - "denial", - "dentist", - "deny", - "depart", - "depend", - "deposit", - "depth", - "deputy", - "derive", - "describe", - "desert", - "design", - "desk", - "despair", - "destroy", - "detail", - "detect", - "develop", - "device", - "devote", - "diagram", - "dial", - "diamond", - "diary", - "dice", - "diesel", - "diet", - "differ", - "digital", - "dignity", - "dilemma", - "dinner", - "dinosaur", - "direct", - "dirt", - "disagree", - "discover", - "disease", - "dish", - "dismiss", - "disorder", - "display", - "distance", - "divert", - "divide", - "divorce", - "dizzy", - "doctor", - "document", - "dog", - "doll", - "dolphin", - "domain", - "donate", - "donkey", - "donor", - "door", - "dose", - "double", - "dove", - "draft", - "dragon", - "drama", - "drastic", - "draw", - "dream", - "dress", - "drift", - "drill", - "drink", - "drip", - "drive", - "drop", - "drum", - "dry", - "duck", - "dumb", - "dune", - "during", - "dust", - "dutch", - "duty", - "dwarf", - "dynamic", - "eager", - "eagle", - "early", - "earn", - "earth", - "easily", - "east", - "easy", - "echo", - "ecology", - "economy", - "edge", - "edit", - "educate", - "effort", - "egg", - "eight", - "either", - "elbow", - "elder", - "electric", - "elegant", - "element", - "elephant", - "elevator", - "elite", - "else", - "embark", - "embody", - "embrace", - "emerge", - "emotion", - "employ", - "empower", - "empty", - "enable", - "enact", - "end", - "endless", - "endorse", - "enemy", - "energy", - "enforce", - "engage", - "engine", - "enhance", - "enjoy", - "enlist", - "enough", - "enrich", - "enroll", - "ensure", - "enter", - "entire", - "entry", - "envelope", - "episode", - "equal", - "equip", - "era", - "erase", - "erode", - "erosion", - "error", - "erupt", - "escape", - "essay", - "essence", - "estate", - "eternal", - "ethics", - "evidence", - "evil", - "evoke", - "evolve", - "exact", - "example", - "excess", - "exchange", - "excite", - "exclude", - "excuse", - "execute", - "exercise", - "exhaust", - "exhibit", - "exile", - "exist", - "exit", - "exotic", - "expand", - "expect", - "expire", - "explain", - "expose", - "express", - "extend", - "extra", - "eye", - "eyebrow", - "fabric", - "face", - "faculty", - "fade", - "faint", - "faith", - "fall", - "false", - "fame", - "family", - "famous", - "fan", - "fancy", - "fantasy", - "farm", - "fashion", - "fat", - "fatal", - "father", - "fatigue", - "fault", - "favorite", - "feature", - "february", - "federal", - "fee", - "feed", - "feel", - "female", - "fence", - "festival", - "fetch", - "fever", - "few", - "fiber", - "fiction", - "field", - "figure", - "file", - "film", - "filter", - "final", - "find", - "fine", - "finger", - "finish", - "fire", - "firm", - "first", - "fiscal", - "fish", - "fit", - "fitness", - "fix", - "flag", - "flame", - "flash", - "flat", - "flavor", - "flee", - "flight", - "flip", - "float", - "flock", - "floor", - "flower", - "fluid", - "flush", - "fly", - "foam", - "focus", - "fog", - "foil", - "fold", - "follow", - "food", - "foot", - "force", - "forest", - "forget", - "fork", - "fortune", - "forum", - "forward", - "fossil", - "foster", - "found", - "fox", - "fragile", - "frame", - "frequent", - "fresh", - "friend", - "fringe", - "frog", - "front", - "frost", - "frown", - "frozen", - "fruit", - "fuel", - "fun", - "funny", - "furnace", - "fury", - "future", - "gadget", - "gain", - "galaxy", - "gallery", - "game", - "gap", - "garage", - "garbage", - "garden", - "garlic", - "garment", - "gas", - "gasp", - "gate", - "gather", - "gauge", - "gaze", - "general", - "genius", - "genre", - "gentle", - "genuine", - "gesture", - "ghost", - "giant", - "gift", - "giggle", - "ginger", - "giraffe", - "girl", - "give", - "glad", - "glance", - "glare", - "glass", - "glide", - "glimpse", - "globe", - "gloom", - "glory", - "glove", - "glow", - "glue", - "goat", - "goddess", - "gold", - "good", - "goose", - "gorilla", - "gospel", - "gossip", - "govern", - "gown", - "grab", - "grace", - "grain", - "grant", - "grape", - "grass", - "gravity", - "great", - "green", - "grid", - "grief", - "grit", - "grocery", - "group", - "grow", - "grunt", - "guard", - "guess", - "guide", - "guilt", - "guitar", - "gun", - "gym", - "habit", - "hair", - "half", - "hammer", - "hamster", - "hand", - "happy", - "harbor", - "hard", - "harsh", - "harvest", - "hat", - "have", - "hawk", - "hazard", - "head", - "health", - "heart", - "heavy", - "hedgehog", - "height", - "hello", - "helmet", - "help", - "hen", - "hero", - "hidden", - "high", - "hill", - "hint", - "hip", - "hire", - "history", - "hobby", - "hockey", - "hold", - "hole", - "holiday", - "hollow", - "home", - "honey", - "hood", - "hope", - "horn", - "horror", - "horse", - "hospital", - "host", - "hotel", - "hour", - "hover", - "hub", - "huge", - "human", - "humble", - "humor", - "hundred", - "hungry", - "hunt", - "hurdle", - "hurry", - "hurt", - "husband", - "hybrid", - "ice", - "icon", - "idea", - "identify", - "idle", - "ignore", - "ill", - "illegal", - "illness", - "image", - "imitate", - "immense", - "immune", - "impact", - "impose", - "improve", - "impulse", - "inch", - "include", - "income", - "increase", - "index", - "indicate", - "indoor", - "industry", - "infant", - "inflict", - "inform", - "inhale", - "inherit", - "initial", - "inject", - "injury", - "inmate", - "inner", - "innocent", - "input", - "inquiry", - "insane", - "insect", - "inside", - "inspire", - "install", - "intact", - "interest", - "into", - "invest", - "invite", - "involve", - "iron", - "island", - "isolate", - "issue", - "item", - "ivory", - "jacket", - "jaguar", - "jar", - "jazz", - "jealous", - "jeans", - "jelly", - "jewel", - "job", - "join", - "joke", - "journey", - "joy", - "judge", - "juice", - "jump", - "jungle", - "junior", - "junk", - "just", - "kangaroo", - "keen", - "keep", - "ketchup", - "key", - "kick", - "kid", - "kidney", - "kind", - "kingdom", - "kiss", - "kit", - "kitchen", - "kite", - "kitten", - "kiwi", - "knee", - "knife", - "knock", - "know", - "lab", - "label", - "labor", - "ladder", - "lady", - "lake", - "lamp", - "language", - "laptop", - "large", - "later", - "latin", - "laugh", - "laundry", - "lava", - "law", - "lawn", - "lawsuit", - "layer", - "lazy", - "leader", - "leaf", - "learn", - "leave", - "lecture", - "left", - "leg", - "legal", - "legend", - "leisure", - "lemon", - "lend", - "length", - "lens", - "leopard", - "lesson", - "letter", - "level", - "liar", - "liberty", - "library", - "license", - "life", - "lift", - "light", - "like", - "limb", - "limit", - "link", - "lion", - "liquid", - "list", - "little", - "live", - "lizard", - "load", - "loan", - "lobster", - "local", - "lock", - "logic", - "lonely", - "long", - "loop", - "lottery", - "loud", - "lounge", - "love", - "loyal", - "lucky", - "luggage", - "lumber", - "lunar", - "lunch", - "luxury", - "lyrics", - "machine", - "mad", - "magic", - "magnet", - "maid", - "mail", - "main", - "major", - "make", - "mammal", - "man", - "manage", - "mandate", - "mango", - "mansion", - "manual", - "maple", - "marble", - "march", - "margin", - "marine", - "market", - "marriage", - "mask", - "mass", - "master", - "match", - "material", - "math", - "matrix", - "matter", - "maximum", - "maze", - "meadow", - "mean", - "measure", - "meat", - "mechanic", - "medal", - "media", - "melody", - "melt", - "member", - "memory", - "mention", - "menu", - "mercy", - "merge", - "merit", - "merry", - "mesh", - "message", - "metal", - "method", - "middle", - "midnight", - "milk", - "million", - "mimic", - "mind", - "minimum", - "minor", - "minute", - "miracle", - "mirror", - "misery", - "miss", - "mistake", - "mix", - "mixed", - "mixture", - "mobile", - "model", - "modify", - "mom", - "moment", - "monitor", - "monkey", - "monster", - "month", - "moon", - "moral", - "more", - "morning", - "mosquito", - "mother", - "motion", - "motor", - "mountain", - "mouse", - "move", - "movie", - "much", - "muffin", - "mule", - "multiply", - "muscle", - "museum", - "mushroom", - "music", - "must", - "mutual", - "myself", - "mystery", - "myth", - "naive", - "name", - "napkin", - "narrow", - "nasty", - "nation", - "nature", - "near", - "neck", - "need", - "negative", - "neglect", - "neither", - "nephew", - "nerve", - "nest", - "net", - "network", - "neutral", - "never", - "news", - "next", - "nice", - "night", - "noble", - "noise", - "nominee", - "noodle", - "normal", - "north", - "nose", - "notable", - "note", - "nothing", - "notice", - "novel", - "now", - "nuclear", - "number", - "nurse", - "nut", - "oak", - "obey", - "object", - "oblige", - "obscure", - "observe", - "obtain", - "obvious", - "occur", - "ocean", - "october", - "odor", - "off", - "offer", - "office", - "often", - "oil", - "okay", - "old", - "olive", - "olympic", - "omit", - "once", - "one", - "onion", - "online", - "only", - "open", - "opera", - "opinion", - "oppose", - "option", - "orange", - "orbit", - "orchard", - "order", - "ordinary", - "organ", - "orient", - "original", - "orphan", - "ostrich", - "other", - "outdoor", - "outer", - "output", - "outside", - "oval", - "oven", - "over", - "own", - "owner", - "oxygen", - "oyster", - "ozone", - "pact", - "paddle", - "page", - "pair", - "palace", - "palm", - "panda", - "panel", - "panic", - "panther", - "paper", - "parade", - "parent", - "park", - "parrot", - "party", - "pass", - "patch", - "path", - "patient", - "patrol", - "pattern", - "pause", - "pave", - "payment", - "peace", - "peanut", - "pear", - "peasant", - "pelican", - "pen", - "penalty", - "pencil", - "people", - "pepper", - "perfect", - "permit", - "person", - "pet", - "phone", - "photo", - "phrase", - "physical", - "piano", - "picnic", - "picture", - "piece", - "pig", - "pigeon", - "pill", - "pilot", - "pink", - "pioneer", - "pipe", - "pistol", - "pitch", - "pizza", - "place", - "planet", - "plastic", - "plate", - "play", - "please", - "pledge", - "pluck", - "plug", - "plunge", - "poem", - "poet", - "point", - "polar", - "pole", - "police", - "pond", - "pony", - "pool", - "popular", - "portion", - "position", - "possible", - "post", - "potato", - "pottery", - "poverty", - "powder", - "power", - "practice", - "praise", - "predict", - "prefer", - "prepare", - "present", - "pretty", - "prevent", - "price", - "pride", - "primary", - "print", - "priority", - "prison", - "private", - "prize", - "problem", - "process", - "produce", - "profit", - "program", - "project", - "promote", - "proof", - "property", - "prosper", - "protect", - "proud", - "provide", - "public", - "pudding", - "pull", - "pulp", - "pulse", - "pumpkin", - "punch", - "pupil", - "puppy", - "purchase", - "purity", - "purpose", - "purse", - "push", - "put", - "puzzle", - "pyramid", - "quality", - "quantum", - "quarter", - "question", - "quick", - "quit", - "quiz", - "quote", - "rabbit", - "raccoon", - "race", - "rack", - "radar", - "radio", - "rail", - "rain", - "raise", - "rally", - "ramp", - "ranch", - "random", - "range", - "rapid", - "rare", - "rate", - "rather", - "raven", - "raw", - "razor", - "ready", - "real", - "reason", - "rebel", - "rebuild", - "recall", - "receive", - "recipe", - "record", - "recycle", - "reduce", - "reflect", - "reform", - "refuse", - "region", - "regret", - "regular", - "reject", - "relax", - "release", - "relief", - "rely", - "remain", - "remember", - "remind", - "remove", - "render", - "renew", - "rent", - "reopen", - "repair", - "repeat", - "replace", - "report", - "require", - "rescue", - "resemble", - "resist", - "resource", - "response", - "result", - "retire", - "retreat", - "return", - "reunion", - "reveal", - "review", - "reward", - "rhythm", - "rib", - "ribbon", - "rice", - "rich", - "ride", - "ridge", - "rifle", - "right", - "rigid", - "ring", - "riot", - "ripple", - "risk", - "ritual", - "rival", - "river", - "road", - "roast", - "robot", - "robust", - "rocket", - "romance", - "roof", - "rookie", - "room", - "rose", - "rotate", - "rough", - "round", - "route", - "royal", - "rubber", - "rude", - "rug", - "rule", - "run", - "runway", - "rural", - "sad", - "saddle", - "sadness", - "safe", - "sail", - "salad", - "salmon", - "salon", - "salt", - "salute", - "same", - "sample", - "sand", - "satisfy", - "satoshi", - "sauce", - "sausage", - "save", - "say", - "scale", - "scan", - "scare", - "scatter", - "scene", - "scheme", - "school", - "science", - "scissors", - "scorpion", - "scout", - "scrap", - "screen", - "script", - "scrub", - "sea", - "search", - "season", - "seat", - "second", - "secret", - "section", - "security", - "seed", - "seek", - "segment", - "select", - "sell", - "seminar", - "senior", - "sense", - "sentence", - "series", - "service", - "session", - "settle", - "setup", - "seven", - "shadow", - "shaft", - "shallow", - "share", - "shed", - "shell", - "sheriff", - "shield", - "shift", - "shine", - "ship", - "shiver", - "shock", - "shoe", - "shoot", - "shop", - "short", - "shoulder", - "shove", - "shrimp", - "shrug", - "shuffle", - "shy", - "sibling", - "sick", - "side", - "siege", - "sight", - "sign", - "silent", - "silk", - "silly", - "silver", - "similar", - "simple", - "since", - "sing", - "siren", - "sister", - "situate", - "six", - "size", - "skate", - "sketch", - "ski", - "skill", - "skin", - "skirt", - "skull", - "slab", - "slam", - "sleep", - "slender", - "slice", - "slide", - "slight", - "slim", - "slogan", - "slot", - "slow", - "slush", - "small", - "smart", - "smile", - "smoke", - "smooth", - "snack", - "snake", - "snap", - "sniff", - "snow", - "soap", - "soccer", - "social", - "sock", - "soda", - "soft", - "solar", - "soldier", - "solid", - "solution", - "solve", - "someone", - "song", - "soon", - "sorry", - "sort", - "soul", - "sound", - "soup", - "source", - "south", - "space", - "spare", - "spatial", - "spawn", - "speak", - "special", - "speed", - "spell", - "spend", - "sphere", - "spice", - "spider", - "spike", - "spin", - "spirit", - "split", - "spoil", - "sponsor", - "spoon", - "sport", - "spot", - "spray", - "spread", - "spring", - "spy", - "square", - "squeeze", - "squirrel", - "stable", - "stadium", - "staff", - "stage", - "stairs", - "stamp", - "stand", - "start", - "state", - "stay", - "steak", - "steel", - "stem", - "step", - "stereo", - "stick", - "still", - "sting", - "stock", - "stomach", - "stone", - "stool", - "story", - "stove", - "strategy", - "street", - "strike", - "strong", - "struggle", - "student", - "stuff", - "stumble", - "style", - "subject", - "submit", - "subway", - "success", - "such", - "sudden", - "suffer", - "sugar", - "suggest", - "suit", - "summer", - "sun", - "sunny", - "sunset", - "super", - "supply", - "supreme", - "sure", - "surface", - "surge", - "surprise", - "surround", - "survey", - "suspect", - "sustain", - "swallow", - "swamp", - "swap", - "swarm", - "swear", - "sweet", - "swift", - "swim", - "swing", - "switch", - "sword", - "symbol", - "symptom", - "syrup", - "system", - "table", - "tackle", - "tag", - "tail", - "talent", - "talk", - "tank", - "tape", - "target", - "task", - "taste", - "tattoo", - "taxi", - "teach", - "team", - "tell", - "ten", - "tenant", - "tennis", - "tent", - "term", - "test", - "text", - "thank", - "that", - "theme", - "then", - "theory", - "there", - "they", - "thing", - "this", - "thought", - "three", - "thrive", - "throw", - "thumb", - "thunder", - "ticket", - "tide", - "tiger", - "tilt", - "timber", - "time", - "tiny", - "tip", - "tired", - "tissue", - "title", - "toast", - "tobacco", - "today", - "toddler", - "toe", - "together", - "toilet", - "token", - "tomato", - "tomorrow", - "tone", - "tongue", - "tonight", - "tool", - "tooth", - "top", - "topic", - "topple", - "torch", - "tornado", - "tortoise", - "toss", - "total", - "tourist", - "toward", - "tower", - "town", - "toy", - "track", - "trade", - "traffic", - "tragic", - "train", - "transfer", - "trap", - "trash", - "travel", - "tray", - "treat", - "tree", - "trend", - "trial", - "tribe", - "trick", - "trigger", - "trim", - "trip", - "trophy", - "trouble", - "truck", - "true", - "truly", - "trumpet", - "trust", - "truth", - "try", - "tube", - "tuition", - "tumble", - "tuna", - "tunnel", - "turkey", - "turn", - "turtle", - "twelve", - "twenty", - "twice", - "twin", - "twist", - "two", - "type", - "typical", - "ugly", - "umbrella", - "unable", - "unaware", - "uncle", - "uncover", - "under", - "undo", - "unfair", - "unfold", - "unhappy", - "uniform", - "unique", - "unit", - "universe", - "unknown", - "unlock", - "until", - "unusual", - "unveil", - "update", - "upgrade", - "uphold", - "upon", - "upper", - "upset", - "urban", - "urge", - "usage", - "use", - "used", - "useful", - "useless", - "usual", - "utility", - "vacant", - "vacuum", - "vague", - "valid", - "valley", - "valve", - "van", - "vanish", - "vapor", - "various", - "vast", - "vault", - "vehicle", - "velvet", - "vendor", - "venture", - "venue", - "verb", - "verify", - "version", - "very", - "vessel", - "veteran", - "viable", - "vibrant", - "vicious", - "victory", - "video", - "view", - "village", - "vintage", - "violin", - "virtual", - "virus", - "visa", - "visit", - "visual", - "vital", - "vivid", - "vocal", - "voice", - "void", - "volcano", - "volume", - "vote", - "voyage", - "wage", - "wagon", - "wait", - "walk", - "wall", - "walnut", - "want", - "warfare", - "warm", - "warrior", - "wash", - "wasp", - "waste", - "water", - "wave", - "way", - "wealth", - "weapon", - "wear", - "weasel", - "weather", - "web", - "wedding", - "weekend", - "weird", - "welcome", - "west", - "wet", - "whale", - "what", - "wheat", - "wheel", - "when", - "where", - "whip", - "whisper", - "wide", - "width", - "wife", - "wild", - "will", - "win", - "window", - "wine", - "wing", - "wink", - "winner", - "winter", - "wire", - "wisdom", - "wise", - "wish", - "witness", - "wolf", - "woman", - "wonder", - "wood", - "wool", - "word", - "work", - "world", - "worry", - "worth", - "wrap", - "wreck", - "wrestle", - "wrist", - "write", - "wrong", - "yard", - "year", - "yellow", - "you", - "young", - "youth", - "zebra", - "zero", - "zone", - "zoo" - ) - - val index: HashMap - - init { - index = HashMap(items.size) - items.forEachIndexed { i: Int, word: String -> index[word] = i } - } -} \ No newline at end of file diff --git a/android-client/app/src/main/java/org/hermes/iota/seed.kt b/android-client/app/src/main/java/org/hermes/iota/seed.kt deleted file mode 100644 index 085406f..0000000 --- a/android-client/app/src/main/java/org/hermes/iota/seed.kt +++ /dev/null @@ -1,59 +0,0 @@ -package org.hermes.iota - -import org.iota.jota.utils.Converter - -class InvalidSeedException: Exception() - -/** - * Re-implementation of some cryptographic utilities used by the IOTA - * Trinity wallet for producing a seed. - * - * @see https://medium.com/@abmushi/iota-signature-and-validation-b95b3f9ec534 - * - * Based on the article mentioned above, the seed is 81 trytes long. - * It is combined with another value and then hashed 27 * security_level - * to produce a private key - */ - -class Seed(val value: CharArray) { - - companion object { - - const val DEFAULT_SEED_SECURITY = 2 - - const val MAX_SEED_LENGTH = 81 - - const val ADDRESS_LENGTH = 81 - - const val ADDRESS_AND_CHECKSUM_LENGTH = 90 - - const val VALID_SEED_REGEX = "/^[A-Z9]+$/" - - fun new(): Seed { - val charArray = CharArray(MAX_SEED_LENGTH) - - for (i: Int in 0 until MAX_SEED_LENGTH) { - charArray[i] = Tryte.random().char - } - return Seed(charArray) - } - } - - fun validate() { - if (value.size != MAX_SEED_LENGTH) throw InvalidSeedException() - for (c: Char in value) { - try { Tryte(c) } - catch (t: InvalidTryte) { throw InvalidSeedException() } - } - } - - fun extractPrivateKey(): CharArray = CharArray(0) { 'C' } - - override fun toString(): String = value.joinToString(separator = "") - - /** - * Converts the seed into an integer array in a way that is compatible with the way - * IOTA converts chars to trytes. - */ - fun toIntArray(): IntArray = Converter.trits(toString()) -} diff --git a/android-client/app/src/main/java/org/hermes/iota/trytes.kt b/android-client/app/src/main/java/org/hermes/iota/trytes.kt deleted file mode 100644 index 86a99e4..0000000 --- a/android-client/app/src/main/java/org/hermes/iota/trytes.kt +++ /dev/null @@ -1,629 +0,0 @@ -package org.hermes.iota - -import java.math.BigInteger -import java.security.SecureRandom -import java.util.* - -import kotlin.collections.ArrayList -import kotlin.collections.HashMap - -import org.bouncycastle.pqc.math.linearalgebra.IntegerFunctions.pow - -import org.hermes.collections.ImmutableHashSet -import org.hermes.collections.OrderedImmutableHashSet - -class InvalidTrit: Exception() -class InvalidTryte: Exception() -class OutOfTernaryBounds: Exception() - - -/** - * General properties of the Balanced Trinary system used inside IOTA. - */ -object BalancedTernary { - - val BYTE_RADIX: IntArray = intArrayOf(1, 3, 9, 27, 81) - - const val UNSIGNED_BYTE_MAX_VAL: Int = 242 - - const val TRYTE_ALPHABET = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ" - - const val TRYTE_SPACE = TRYTE_ALPHABET.length - const val BYTE_SPACE = 243 - - val TRYTE_CHAR_SET: ImmutableHashSet - val TRYTES: OrderedImmutableHashSet - - const val TRYTE_VALUE_MAX = 13 - const val BYTE_VALUE_MAX = 121 - - const val TRYTE_VALUE_MIN = -13 - const val BYTE_VALUE_MIN = -121 - - // Number of trits in a tryte - const val TRYTE_WIDTH = 3 - // Minimum number of bits to represent a tryte - const val BYTE_WIDTH = 5 - - private val byTrits: HashMap, Tryte> = HashMap() - private val byCharacter: HashMap = HashMap() - - init { - // Create an immutable set with all the different characters that can encode trytes - val tryteCharHashSet = HashSet() - for (c: Char in TRYTE_ALPHABET) - tryteCharHashSet.add(c) - TRYTE_CHAR_SET = ImmutableHashSet(tryteCharHashSet) - - // Create a mapping from integers in range -13..13 to tryte objects - val orderedTrytes = ArrayList() - val minimumTryte = Tryte(Trit(-1), Trit(-1), Trit(-1)) - orderedTrytes.add(minimumTryte) - for (i: Int in -12..13) - orderedTrytes.add(orderedTrytes.last().inc()) - TRYTES = OrderedImmutableHashSet(orderedTrytes) - - // Create a mapping from sequences of bytes to trytes. Not all possible bytes - // are included in this mapping, just those that map to trytes. - for (tryte in TRYTES) { - byCharacter[tryte.char] = tryte - byTrits[tryte.trits] = tryte - } - } - - /** - * Return the tryte that directly maps to an integer from -13 to 13. - * For conversion from arbitrary decimal values to balanced trinary, use the - * `toTrinary` method. - * - * @throws OutOfTernaryBounds when the integer provider is outside -13 to 13. - */ - fun tryteFromDecimal(i: Int): Tryte { - if (i < -13 || i > 13) { - throw OutOfTernaryBounds() - } - return TRYTES[i] - } - - /** - * Convert a decimal integer to the balanced ternary system. - * - * TODO: Add tests for this - */ - fun toTrinary(i: Int): TritArray { - val isNegative = i < 0 - var value = when { - isNegative -> -1 * i - else -> i - } - var intermediate = TritArray(0) { Trit(0) } - var pow = 0 - while (value > 0) { - // No, I don't mean LSD as in the drug but Least Significant Decimal. - val lsd = value % 10 - value = (value / 10) - intermediate += TRYTES[lsd + 13].toTritArray() * Tryte.ten.toTritArray().toPowerOf(pow) - pow += 1 - } - intermediate = intermediate.fill() - return when { - isNegative -> -intermediate - else -> intermediate - } - } - - fun fromChar(char: Char): Tryte = byCharacter[char] ?: throw OutOfTernaryBounds() - - fun fromTrits(triple: Triple): Tryte = byTrits[triple] ?: throw OutOfTernaryBounds() -} - - -/** - * A class that represents a single trit of the balanced trinary system. - */ -class Trit(i: Int) { - - val i: Int - - init { - if (!arrayOf(-1, 0, 1).contains(i)) { - throw InvalidTrit() - } - this.i = i - } - - /** - * Adds this trit to the other trit and - */ - fun plus(other: Trit, curry: Int = 0): Pair { - /** - * Adds the values of two trits and returns the sum and the curry. - * - * @return a Pair where the first value is the curry and the - * second is the sum. - */ - fun addTrits(a: Int, b: Int): Pair { - return when { - a == 1 && b == 1 -> Pair(1, -1) - a == -1 && b == -1 -> Pair(-1, 1) - else -> Pair(0, a + b) - } - } - val (curry1, sum1) = addTrits(other.i, this.i) - val (curry2, sum2) = addTrits(sum1, curry) - // There is no combination that can give a two digit curry, so we can safely ignore it. - val (_, finalCurry) = addTrits(curry1, curry2) - return Pair(Trit(finalCurry), Trit(sum2)) - } - - operator fun times(other: Trit): Trit = Trit(this.i * other.i) - - operator fun unaryMinus(): Trit = Trit(this.i * -1) - - override operator fun equals(other: Any?): Boolean { - if (other is Trit) { - return i == other.i - } - return false - } - - override fun toString(): String = when(i) { - -1 -> "T" - else -> i.toString() - } -} - - -/** - * A class that represents a sequence of trits. It does not necessarily mean that the trits - * contained can be neatly split into trytes. - * - * It is the underlying class that is used when performing any mathematical operations using Trytes. - */ -class TritArray { - - private val tritArray: Array - - companion object { - fun empty(): TritArray = TritArray(emptyArray()) - - fun emptyArray(): Array = Array(0) { Trit(0) } - - fun zeroedArray(i: Int = 0): TritArray = TritArray(i) { Trit(0) } - } - - constructor(size: Int) { - tritArray = Array(size) { Trit(0) } - } - - constructor(size: Int, f: (Int) -> Trit) { - tritArray = Array(size, f) - } - - constructor(tritArray: Array) { - this.tritArray = tritArray - } - - constructor(trits: Triple) { - tritArray = emptyArray() - .plus(trits.first) - .plus(trits.second) - .plus(trits.third) - } - - fun conc(otherArray: TritArray): TritArray { - return TritArray(size + otherArray.size) { - i: Int -> - when { - i < size -> tritArray[i] - else -> otherArray.tritArray[i - size] - } - } - } - - /** - * Combines two arrays into a list of pairs of trits. - * - * If one of the lists has more elements, the missing trits will be - * replaced with 0s. - */ - fun zip(otherArray: TritArray): List> { - val zipped = LinkedList>() - for (i: Int in 0 until Math.max(size, otherArray.size)) { - when{ - i >= size -> zipped.add(Pair(Trit(0), otherArray.tritArray[i])) - i >= otherArray.size -> zipped.add(Pair(tritArray[i], Trit(0))) - else -> zipped.add(Pair(tritArray[i], otherArray.tritArray[i])) - } - } - return zipped - } - - val size: Int - get() = tritArray.size - - operator fun get(i: Int): Trit = tritArray[i] - - operator fun set(i: Int, t: Trit) = tritArray.set(i, t) - - operator fun plus(otherArray: TritArray): TritArray { - var curry = Trit(0) - var resultArray: Array = emptyArray() - for ((trit1, trit2) in zip(otherArray)) { - // Add the two trits - val sumRes = trit1.plus(trit2) - // Add the curry to the result of the previous sum - val res = sumRes.second.plus(curry) - // Keep the curry for the next sum - curry = sumRes.first.plus(res.first).second - resultArray = resultArray.plus(res.second) - } - resultArray = resultArray.plus(curry) - return TritArray(resultArray) - } - - operator fun iterator(): Iterator = tritArray.iterator() - - operator fun times(otherArray: TritArray): TritArray { - var sum = TritArray.empty() - val intermediateSum = TritArray.zeroedArray(size + otherArray.size + 1) - for (i: Int in 0 until otherArray.size) { - for (j: Int in 0 until size) - intermediateSum[i + j] = this[j] * otherArray[i] - sum += intermediateSum - intermediateSum.makeZero() - } - return sum.trim() - } - - operator fun unaryMinus(): TritArray = TritArray(tritArray.map { trit -> -trit }.toTypedArray()) - - /** - * Return the index of the first trit that is not zero. - * - * If the array is empty return -1. - */ - fun firstNonZeroIndex(): Int { - for (i: Int in (tritArray.size - 1) downTo 0) { - if (tritArray[i].i != 0) { - return i - } - } - return -1 - } - - /** - * Remove all the zeros from the most significant trits. - */ - fun trim(): TritArray { - val zerosEnd = firstNonZeroIndex() - if (zerosEnd != -1) - return TritArray(tritArray.sliceArray(0..zerosEnd)) - return this - } - - /** - * If length is less than 3 add a couple of 0's - */ - fun fill(): TritArray { - if (size < 3) { - var newTritArray = this.tritArray.clone() - while (newTritArray.size < 3) { - newTritArray = newTritArray.plus(Trit(0)) - } - return TritArray(newTritArray) - } - return this - } - - /** - * Trim the trit array to a size that can be easily converted into trytes. - * - * Each trytes has three trits. If the array has a size large than three - * but is not an exact multiple of three it will be trimmed. The trimming - * will start with the zeros and if it's still not in the correct size it - * will be further trimmed. If the array is less than three trits long then - * zeros will be added to reach the size of at least one tryte. - */ - fun trimToTryteSize(): TritArray { - if (size <= 3) { - var normalized = tritArray.clone() - while (size < 3) { - normalized = normalized.plus(Trit(0)) - } - return TritArray(normalized) - } - var zerosEnd = firstNonZeroIndex() - if (zerosEnd == -1) { - zerosEnd = size - 1 - } - return if (zerosEnd % 3 == 0) { - this - } else { - zerosEnd -= (zerosEnd % 3) - TritArray(tritArray.sliceArray(0..zerosEnd)) - } - } - - fun toArray(): Array = tritArray.clone() - - /** - * Raise the trit array to the power. - * - * The power is in decimal. - */ - fun toPowerOf(decimalPower: Int): TritArray = - when (decimalPower) { - 0 -> TritArray(1) { Trit(1) } - 1 -> this - else -> { - var res = this - for (i in 2..decimalPower) - res *= res - res - } - } - - /** - * Returns this array as a Tryte. - * - * If the array contains more than three trits, they are ignored and - * no error or exception is thrown. - */ - fun toTryte(): Tryte = Tryte(safeGet(0), safeGet(1), safeGet(2)) - - /** - * Returns the i-th element or 0 if i exceeds the size of the array - */ - fun safeGet(i: Int): Trit = - if (tritArray.size > i) tritArray[i] - else Trit(0) - - /** - * Returns this array as an array of trytes. - * - * If the size of the array is not a multiple of - */ - fun toTryteArray(): TryteArray { - val tryteArraySize = tritArray.size / 3 + when { - tritArray.size % 3 == 0 -> 0 - else -> 1 - } - val array: Array = Array(tryteArraySize) { Tryte.zero } - for (i in 0 until tryteArraySize) { - array[i] = Tryte( - safeGet(i*3), - safeGet(i*3 + 1), - safeGet(i*3 + 2) - ) - } - return TryteArray(*array) - } - - /** - * Zero out all the elements of the trit array - */ - fun makeZero() = tritArray.fill(Trit(0), 0) - - /** - * Returns the trits as an array of ints (-1, 0, 1) in little endian. - */ - fun toTritIntArray(): IntArray = tritArray.map { it.i }.toIntArray() - - fun toDecimal(): Int = tritArray - .mapIndexed { i, trit -> pow(3, i) * trit.i } - .reduce { acc, i -> acc + i } - - override fun toString(): String = tritArray.joinToString("") -} - - -class Tryte { - - val char: Char - val decimalValue: Int - val trits: Triple - - companion object { - /** - * Return a random tryte. - */ - fun random(): Tryte { - val randomLetter = SecureRandom().nextInt(BalancedTernary.TRYTE_ALPHABET.length) - return Tryte(BalancedTernary.TRYTE_ALPHABET[randomLetter]) - } - - val zero: Tryte - get() = Tryte(Trit(0), Trit(0), Trit(0)) - - val one: Tryte - get() = Tryte(Trit(1), Trit(0), Trit(0)) - - val ten: Tryte - get() = Tryte(Trit(1), Trit(0), Trit(1)) - } - - /** - * Wrap the individual trits into one tryte. - */ - constructor(first: Trit, second: Trit, third: Trit) { - trits = Triple(first, second, third) - decimalValue = first.i + 3 * second.i + 9 * third.i - char = BalancedTernary.TRYTE_ALPHABET[decimalValue + 13] - } - - /** - * Constructs a Tryte out of a character. - * - * The character must belong to the balanced trinary alphabet. - */ - constructor(char: Char) { - val upperCaseChar: Char = if (char.isLowerCase()) char.toUpperCase() else char - val charIndex = BalancedTernary.TRYTE_ALPHABET.indexOf(upperCaseChar) - if (charIndex == -1) { - throw InvalidTryte() - } - this.char = upperCaseChar - decimalValue = charIndex - 13 - val tritArray = BalancedTernary.toTrinary(decimalValue) - this.trits = Triple(tritArray[0], tritArray[1], tritArray[2]) - } - - /** - * Constructs a Tryte out of an integer. - * - * The integer must be between -13 and 13. - */ - constructor(i: Int) { - if (i < -13 || i > 13) { - throw InvalidTryte() - } - this.char = BalancedTernary.TRYTE_ALPHABET[i + 13] - decimalValue = i - val tritArray = BalancedTernary.toTrinary(decimalValue) - this.trits = Triple(tritArray[0], tritArray[1], tritArray[2]) - } - - /** - * Return this tryte as an array of trits. - */ - fun toTritArray(): TritArray = TritArray(trits) - - operator fun unaryPlus(): Tryte = this - - operator fun unaryMinus(): Tryte { - val negativeTritSequence: Triple = Triple( - Trit(-1 * trits.first.i), - Trit(-1 * trits.second.i), - Trit(-1 * trits.third.i) - ) - return BalancedTernary.fromTrits(negativeTritSequence) - } - - /** - * Increment Tryte by one. - * - * If the Tryte overflows that's not something that will be handled - * by this method. - */ - operator fun inc(): Tryte = this + one - - /** - * Adds b to this Tryte. - * - * It throws away the curry if there is an overflow. - */ - operator fun plus(b: Tryte): Tryte { - val newTritArray = toTritArray() + b.toTritArray() - return Tryte(newTritArray[0], newTritArray[1], newTritArray[2]) - } - - operator fun times(b: Tryte): Tryte = (toTritArray() * b.toTritArray()).toTryte() - - /** - * Raise the tryte to a power. - * - * The result can be of arbitrary length, so a TryteArray will be - * returned instead of a simple Tryte. - */ - fun toPowerOf(decimalPower: Int): TryteArray = toTritArray().toPowerOf(decimalPower).toTryteArray() - - override operator fun equals(other: Any?): Boolean = - when (other is Tryte) { - true -> trits.first == other.trits.first && - trits.second == other.trits.second && - trits.third == other.trits.third - else -> false - } -} - - -/** - * A class representing an array of trytes. - */ -class TryteArray { - - private val trytes: Array - - val size: Int - get() = trytes.size - - companion object { - /** - * Return an array of random trytes. - */ - fun random(size: Int): TryteArray { - return TryteArray(*(1..size) - .map { Tryte.random() } - .toTypedArray() - ) - } - } - - /** - * Constructs a TryteArray from the characters in the character array. - * - * Every character must belong to the balanced trinary alphabet. - */ - constructor(charArray: CharArray) { - trytes = Array(charArray.size) { i -> Tryte(charArray[i]) } - } - - constructor(byteArray: ByteArray) { - val temp = LinkedList() - val fourTeen = BigInteger("14") - var b = BigInteger(byteArray) - while (b != BigInteger.ZERO) { - val dividerAndRemainder = b.divideAndRemainder(fourTeen) - temp.add(Tryte(dividerAndRemainder[1].toInt())) - b = dividerAndRemainder[0] - } - trytes = temp.toTypedArray() - } - - constructor(vararg trytes: Tryte) { - this.trytes = Array(trytes.size) { i -> trytes[i] } - } - - fun toCharArray(): CharArray = trytes - .map { it.char } - .toCharArray() - - fun toTritArray(): TritArray = trytes - .map { tryte -> tryte.toTritArray() } - .reduce { tritArray1, tritArray2 -> tritArray1.conc(tritArray2) } - - fun sliceArray(indices: IntRange): TryteArray = TryteArray(*trytes.sliceArray(indices)) - - override fun toString(): String = toCharArray().joinToString("") - - fun plus(tryte: Tryte): TryteArray = TryteArray(*trytes.clone().plus(tryte)) - - operator fun plus(otherArray: TryteArray): TryteArray = - (this.toTritArray() + otherArray.toTritArray()).toTryteArray() - - /** - * Convert the tryte array into an integer. - * - * Handle with care, because the conversion process could cause an overflow. - */ - fun toDecimal(): Int = trytes - .mapIndexed { i, tryte -> pow(14, i) * tryte.decimalValue } - .reduce { acc, i -> acc + i } - - /** - * Converts the tryte array into a BigInteger. - */ - fun toBigDecimal(): BigInteger = trytes - .mapIndexed { i, tryte -> (pow(14L, i) * tryte.decimalValue).toBigInteger() } - .reduce { acc, i -> acc + i } - - /** - * Returns this as an array of integers with trits - */ - fun toTritIntArray(): IntArray = trytes - .flatMap { listOf(it.trits.first.i, it.trits.second.i, it.trits.third.i) } - .toIntArray() - - fun toByteArray(): ByteArray = toBigDecimal().toByteArray() -} \ No newline at end of file diff --git a/android-client/app/src/main/java/org/hermes/ledgers/IOTAConnector.kt b/android-client/app/src/main/java/org/hermes/ledgers/IOTAConnector.kt index fe4e7da..c8c31d1 100644 --- a/android-client/app/src/main/java/org/hermes/ledgers/IOTAConnector.kt +++ b/android-client/app/src/main/java/org/hermes/ledgers/IOTAConnector.kt @@ -3,33 +3,33 @@ package org.hermes.ledgers import android.content.SharedPreferences import android.util.Log - import java.lang.Exception import java.lang.StringBuilder import javax.inject.Inject import javax.inject.Named - import kotlinx.coroutines.* - import org.apache.commons.lang3.StringUtils - +import org.hermes.BACKGROUND +import org.hermes.HermesClientApp +import org.hermes.HermesRoomDatabase import org.iota.jota.IotaAPI import org.iota.jota.model.Bundle import org.iota.jota.model.Transaction import org.iota.jota.utils.Constants import org.iota.jota.utils.InputValidator - -import org.threeten.bp.OffsetDateTime - -import org.hermes.* import org.hermes.crypto.SecP256K1PrivKey import org.hermes.entities.Event -import org.hermes.iota.IOTA -import org.hermes.iota.Seed +import org.hermes.IOTA +import org.hermes.Metric20 +import org.hermes.bip44.iota.Seed import org.hermes.repositories.MetadataRepository import org.hermes.repositories.SensorRepository -import org.hermes.utils.* - +import org.hermes.ternary.toTrytes +import org.hermes.utils.SQLiteTypeConverter +import org.hermes.utils.prepend +import org.hermes.utils.sign +import org.hermes.utils.splitInChunks +import org.threeten.bp.OffsetDateTime class IOTAConnector(val seed: Seed, private val privateKey: SecP256K1PrivKey, app: HermesClientApp) { diff --git a/android-client/app/src/main/java/org/hermes/repositories/CryptoRepository.kt b/android-client/app/src/main/java/org/hermes/repositories/CryptoRepository.kt index 0746d2b..ea95b77 100644 --- a/android-client/app/src/main/java/org/hermes/repositories/CryptoRepository.kt +++ b/android-client/app/src/main/java/org/hermes/repositories/CryptoRepository.kt @@ -3,24 +3,20 @@ package org.hermes.repositories import android.app.Application import android.content.SharedPreferences import android.util.Log - import java.security.* import javax.inject.Inject import javax.inject.Named import javax.inject.Singleton - import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jcajce.provider.digest.SHA3 import org.bouncycastle.util.encoders.Hex - import org.hermes.HermesRoomDatabase import org.hermes.R +import org.hermes.bip32.BTCSigner +import org.hermes.bip44.iota.Seed import org.hermes.crypto.PasswordHasher import org.hermes.crypto.SecP256K1PrivKey import org.hermes.crypto.SecP256K1PubKey -import org.hermes.hd.BTCSigner -import org.hermes.iota.Seed - @Singleton class CryptoRepository @Inject constructor( @@ -197,4 +193,4 @@ class CryptoRepository @Inject constructor( fun IOTASeed(): Seed = seed fun privateKey(): SecP256K1PrivKey? = privateKey -} \ No newline at end of file +} diff --git a/android-client/app/src/main/java/org/hermes/sensors/StressTestingSensor.kt b/android-client/app/src/main/java/org/hermes/sensors/StressTestingSensor.kt index a98c43b..991d2cf 100644 --- a/android-client/app/src/main/java/org/hermes/sensors/StressTestingSensor.kt +++ b/android-client/app/src/main/java/org/hermes/sensors/StressTestingSensor.kt @@ -12,7 +12,7 @@ import kotlinx.coroutines.launch import org.hermes.BACKGROUND import org.hermes.LedgerService import org.hermes.entities.Sensor -import org.hermes.iota.IOTA +import org.hermes.IOTA import org.hermes.repositories.SensorRepository /** diff --git a/android-client/app/src/main/java/org/hermes/utils/arrays.kt b/android-client/app/src/main/java/org/hermes/utils/arrays.kt index 83c2228..f317964 100644 --- a/android-client/app/src/main/java/org/hermes/utils/arrays.kt +++ b/android-client/app/src/main/java/org/hermes/utils/arrays.kt @@ -4,4 +4,4 @@ fun Array.applyIfNotEmpty(f: (Array) -> Unit) { if (isNotEmpty()) f(this) } -fun Array.mapIfNotEmpty(f: (Array) -> Array): Array = if (isNotEmpty()) f(this) else this \ No newline at end of file +fun Array.mapIfNotEmpty(f: (Array) -> Array): Array = if (isNotEmpty()) f(this) else this diff --git a/android-client/app/src/main/java/org/hermes/utils/ktx.kt b/android-client/app/src/main/java/org/hermes/utils/ktx.kt index 19ec6a5..b6107b1 100644 --- a/android-client/app/src/main/java/org/hermes/utils/ktx.kt +++ b/android-client/app/src/main/java/org/hermes/utils/ktx.kt @@ -10,4 +10,4 @@ fun EditText.afterTextChanged(f: (s: Editable?) -> Unit) { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { } }) -} \ No newline at end of file +} diff --git a/android-client/app/src/main/java/org/hermes/utils/strings.kt b/android-client/app/src/main/java/org/hermes/utils/strings.kt index 7a5b452..df14c99 100644 --- a/android-client/app/src/main/java/org/hermes/utils/strings.kt +++ b/android-client/app/src/main/java/org/hermes/utils/strings.kt @@ -1,12 +1,9 @@ package org.hermes.utils import android.util.Log -import org.bouncycastle.util.encoders.Hex +import java.lang.StringBuilder import org.hermes.crypto.SecP256K1PrivKey import org.iota.jota.utils.TrytesConverter -import java.lang.StringBuilder -import java.security.PrivateKey -import java.security.Signature /** * Split a string into strings of maximum size equal to chunkSize. @@ -43,17 +40,9 @@ fun String.splitInChunks(chunkSize: Int): Array { /** * Convert ASCII to trytes. */ -fun String.toTrytes(): String { - return TrytesConverter.asciiToTrytes(this) -} - -fun String.stripPaddingOfTrytes(): String { - return this.trimEnd('9') -} +fun String.toTrytes(): String = TrytesConverter.asciiToTrytes(this) -fun String.prepend(header: String): String { - return header + this -} +fun String.prepend(header: String): String = header + this fun String.sign(header: String = "", privateKey: SecP256K1PrivKey, separator: String = ""): String { @@ -69,17 +58,3 @@ fun String.sign(header: String = "", privateKey: SecP256K1PrivKey, this } } - -fun String.endsWithAnyOf(vararg s: String): Boolean { - for (_s in s) - if (this.endsWith(_s)) - return true - return false -} - -fun String.startsWithAnyOf(collection: Collection): Boolean { - for (prefix in collection) - if (this.startsWith(prefix)) - return true - return false -} diff --git a/android-client/app/src/main/java/org/hermes/utils/toolbar.kt b/android-client/app/src/main/java/org/hermes/utils/toolbar.kt index 1d39cbb..69fb152 100644 --- a/android-client/app/src/main/java/org/hermes/utils/toolbar.kt +++ b/android-client/app/src/main/java/org/hermes/utils/toolbar.kt @@ -22,4 +22,4 @@ fun addBackButton(toolbar: Toolbar, resources: Resources, callback: (View) -> Un newDrawable.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP) toolbar.navigationIcon = newDrawable toolbar.setNavigationOnClickListener(callback) -} \ No newline at end of file +}