diff --git a/.idea/misc.xml b/.idea/misc.xml
index 703e5d4b89..cd2f3a654d 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -5,6 +5,41 @@
+
+
+
+
+
+
diff --git a/app/build.gradle b/app/build.gradle
index c0da9a2b7a..5facfb67ff 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -54,6 +54,7 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation 'androidx.biometric:biometric:1.0.0-alpha03'
+ implementation 'co.nstant.in:cbor:0.8'
implementation 'com.google.android.material:material:1.1.0-alpha02'
implementation 'com.google.dagger:dagger:2.21'
kapt 'com.google.dagger:dagger-compiler:2.21'
@@ -65,6 +66,7 @@ dependencies {
implementation 'com.scottyab:rootbeer-lib:0.0.7'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
implementation 'com.skyfishjy.ripplebackground:library:1.0.1'
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
diff --git a/app/src/main/java/com/tangem/data/Blockchain.java b/app/src/main/java/com/tangem/data/Blockchain.java
index 9e95661c57..11a9ec45dd 100644
--- a/app/src/main/java/com/tangem/data/Blockchain.java
+++ b/app/src/main/java/com/tangem/data/Blockchain.java
@@ -16,7 +16,8 @@ public enum Blockchain {
BitcoinCash("BCH", "BCH", 100000000.0, R.drawable.ic_logo_bitcoin_cash, "Bitcoin Cash"),
Litecoin("LTC", "LTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Litecoin"),
Rootstock("RSK", "RBTC", 1.0, R.drawable.ic_logo_bitcoin, "Rootstock"),
- RootstockToken("Token", "RBTC", 1.0, R.drawable.ic_logo_bat_token, "Rootstock");
+ RootstockToken("Token", "RBTC", 1.0, R.drawable.ic_logo_bat_token, "Rootstock"),
+ Cardano("CARDANO", "ADA", 1000000.0,R.drawable.ic_logo_bitcoin, "Cardano");
Blockchain(String ID, String currency, double multiplier, int imageResource, String officialName) {
mID = ID;
diff --git a/app/src/main/java/com/tangem/data/network/AdaliteApi.java b/app/src/main/java/com/tangem/data/network/AdaliteApi.java
new file mode 100644
index 0000000000..8af94a92c5
--- /dev/null
+++ b/app/src/main/java/com/tangem/data/network/AdaliteApi.java
@@ -0,0 +1,30 @@
+package com.tangem.data.network;
+
+import com.tangem.data.network.model.AdaliteBody;
+import com.tangem.data.network.model.AdaliteResponse;
+import com.tangem.data.network.model.AdaliteResponseUtxo;
+
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.http.Body;
+import retrofit2.http.GET;
+import retrofit2.http.Headers;
+import retrofit2.http.POST;
+import retrofit2.http.Path;
+
+public interface AdaliteApi {
+ @GET(ServerApiAdalite.ADALITE_ADDRESS)
+ Call adaliteAddress(@Path("address") String address);
+
+ @Headers("Content-Type: application/json")
+ @POST(ServerApiAdalite.ADALITE_UNSPENT_OUTPUTS)
+ Call adaliteUnspent(@Body String address);
+
+// @GET(ServerApiAdalite.ADALITE_TRANSACTION)
+// Call adaliteTransaction(@Path("txId") String txId);
+
+ @Headers("Content-Type: application/json")
+ @POST(ServerApiAdalite.ADALITE_SEND)
+ Call adaliteSend(@Body AdaliteBody adaliteBody);
+}
diff --git a/app/src/main/java/com/tangem/data/network/ServerApiAdalite.java b/app/src/main/java/com/tangem/data/network/ServerApiAdalite.java
new file mode 100644
index 0000000000..b057787494
--- /dev/null
+++ b/app/src/main/java/com/tangem/data/network/ServerApiAdalite.java
@@ -0,0 +1,144 @@
+package com.tangem.data.network;
+
+import android.util.Log;
+
+import com.tangem.data.network.model.AdaliteBody;
+import com.tangem.data.network.model.AdaliteResponse;
+import com.tangem.data.network.model.AdaliteResponseUtxo;
+
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+import retrofit2.converter.scalars.ScalarsConverterFactory;
+
+public class ServerApiAdalite {
+ private static String TAG = ServerApiAdalite.class.getSimpleName();
+
+ public static final String ADALITE_ADDRESS = "/api/addresses/summary/{address}";
+ public static final String ADALITE_UNSPENT_OUTPUTS = "/api/bulk/addresses/utxo";
+ // public static final String ADALITE_TRANSACTION = "/api/txs/raw/{txId}}";
+ public static final String ADALITE_SEND = "/api/v2/txs/signed";
+
+ private int requestsCount = 0;
+
+ public static String lastNode;
+
+ public boolean isRequestsSequenceCompleted() {
+ Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
+ return requestsCount <= 0;
+ }
+
+ private ResponseListener responseListener;
+
+ public interface ResponseListener {
+ void onSuccess(String method, AdaliteResponse adaliteResponse);
+
+ void onSuccess(String method, AdaliteResponseUtxo adaliteResponseUtxo);
+
+ void onSuccess(String method, List listResponse);
+
+ void onFail(String method, String message);
+ }
+
+ public void setResponseListener(ResponseListener listener) {
+ responseListener = listener;
+ }
+
+ public void requestData(String method, String wallet, String tx) {
+ requestsCount++;
+ String adaliteURL = "https://explorer2.adalite.io"; //TODO: make random selection
+ this.lastNode = adaliteURL; //TODO: show node instead of URL
+
+ Retrofit retrofitAdalite = new Retrofit.Builder()
+ .baseUrl(adaliteURL)
+ .addConverterFactory(ScalarsConverterFactory.create())
+ .addConverterFactory(GsonConverterFactory.create())
+ .build();
+
+ AdaliteApi adaliteApi = retrofitAdalite.create(AdaliteApi.class);
+
+ switch (method) {
+ case ADALITE_ADDRESS:
+
+ Call addressCall = adaliteApi.adaliteAddress(wallet);
+ addressCall.enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.code() == 200) {
+ requestsCount--;
+ responseListener.onSuccess(method, response.body());
+ Log.i(TAG, "requestData " + method + " onResponse " + response.code());
+ } else {
+ responseListener.onFail(method, String.valueOf(response.code()));
+ Log.e(TAG, "requestData " + method + " onResponse " + response.code());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ responseListener.onFail(method, String.valueOf(t.getMessage()));
+ Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
+ }
+ });
+ break;
+
+ case ADALITE_UNSPENT_OUTPUTS:
+ Call outputsCall = adaliteApi.adaliteUnspent("[\"" + wallet + "\"]");
+ outputsCall.enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.code() == 200) {
+ requestsCount--;
+ responseListener.onSuccess(method, response.body());
+ Log.i(TAG, "requestData " + method + " onResponse " + response.code());
+ } else {
+ responseListener.onFail(method, String.valueOf(response.code()));
+ Log.e(TAG, "requestData " + method + " onResponse " + response.code());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ responseListener.onFail(method, String.valueOf(t.getMessage()));
+ Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
+ }
+ });
+ break;
+
+ case ADALITE_SEND:
+ Call sendCall = adaliteApi.adaliteSend(new AdaliteBody(tx));
+ sendCall.enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.code() == 200) {
+ requestsCount--;
+ responseListener.onSuccess(method, response.body());
+ Log.i(TAG, "requestData " + method + " onResponse " + response.code());
+ } else {
+ responseListener.onFail(method, String.valueOf(response.code()));
+ Log.e(TAG, "requestData " + method + " onResponse " + response.code());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ responseListener.onFail(method, String.valueOf(t.getMessage()));
+ Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
+ }
+ });
+ break;
+
+ default:
+ responseListener.onFail(method, "undeclared method");
+ Log.e(TAG, "requestData " + method + " onFailure - undeclared method");
+ break;
+ }
+
+ }
+}
+
diff --git a/app/src/main/java/com/tangem/data/network/model/AdaliteBody.java b/app/src/main/java/com/tangem/data/network/model/AdaliteBody.java
new file mode 100644
index 0000000000..a8208d01cf
--- /dev/null
+++ b/app/src/main/java/com/tangem/data/network/model/AdaliteBody.java
@@ -0,0 +1,9 @@
+package com.tangem.data.network.model;
+
+public class AdaliteBody {
+ private String signedTx;
+
+ public AdaliteBody(String signedTx) {
+ this.signedTx = signedTx;
+ }
+}
diff --git a/app/src/main/java/com/tangem/data/network/model/AdaliteResponse.kt b/app/src/main/java/com/tangem/data/network/model/AdaliteResponse.kt
new file mode 100644
index 0000000000..f0a3a1ca44
--- /dev/null
+++ b/app/src/main/java/com/tangem/data/network/model/AdaliteResponse.kt
@@ -0,0 +1,37 @@
+package com.tangem.data.network.model
+
+import com.google.gson.annotations.SerializedName
+
+data class AdaliteResponse(
+ @SerializedName("Right")
+ var right: AddressData? = null
+)
+
+data class AdaliteResponseUtxo(
+ @SerializedName("Right")
+ var right: List
+)
+
+data class AddressData(
+ @SerializedName("caAddress")
+ var caAddress: String? = null,
+
+ @SerializedName("caBalance")
+ var caBalance: AdaliteCoins? = null
+)
+
+data class AdaliteCoins(
+ @SerializedName("getCoin")
+ var getCoin: Long? = null
+)
+
+data class UtxoData(
+ @SerializedName("cuId")
+ var cuId: String? = null,
+
+ @SerializedName("cuOutIndex")
+ var cuOutIndex: Int? = null,
+
+ @SerializedName("cuCoins")
+ var cuCoins: AdaliteCoins? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinData.java b/app/src/main/java/com/tangem/domain/wallet/CoinData.java
index 9447ee6935..9ec1663bdb 100644
--- a/app/src/main/java/com/tangem/domain/wallet/CoinData.java
+++ b/app/src/main/java/com/tangem/domain/wallet/CoinData.java
@@ -143,7 +143,7 @@ public abstract class CoinData {
public void clearInfo() {
setIsBalanceEqual(false);
- setBalanceReceived(false); // TODO check
+ setBalanceReceived(false);
setValidationNodeDescription("");
minFee=null;
maxFee=null;
diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java
index 19fc78fe8c..caa4efd4f2 100644
--- a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java
+++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java
@@ -6,6 +6,7 @@ import android.text.InputFilter;
import com.tangem.tangemcommon.reader.CardProtocol;
import com.tangem.tangemcommon.tasks.SignTask;
+import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
@@ -15,6 +16,8 @@ import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
+import co.nstant.in.cbor.CborException;
+
/**
* Created by Ilia on 15.02.2018.
*/
@@ -189,7 +192,6 @@ public abstract class CoinEngine {
public abstract boolean checkNewTransactionAmount(Amount amount);
- // TODO - move minFeeInInternalUnits to CoinData
public abstract boolean checkNewTransactionAmountAndFee(Amount amount, Amount fee, Boolean isFeeIncluded);
public abstract boolean validateBalance(BalanceValidator balanceValidator);
@@ -214,7 +216,7 @@ public abstract class CoinEngine {
public abstract boolean validateAddress(String address);
- public abstract String calculateAddress(byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException;
+ public abstract String calculateAddress(byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException, CborException, IOException;
public abstract Amount convertToAmount(InternalAmount internalAmount) throws Exception;
diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt b/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt
index 9d99f0121f..00e26bc4cb 100644
--- a/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt
+++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt
@@ -7,6 +7,7 @@ import com.tangem.domain.wallet.eth.EthEngine
import com.tangem.domain.wallet.token.TokenEngine
import com.tangem.domain.wallet.bch.BtcCashEngine
import com.tangem.data.Blockchain
+import com.tangem.domain.wallet.cardano.CardanoEngine
import com.tangem.domain.wallet.ltc.LtcEngine
import com.tangem.domain.wallet.nftToken.NftTokenEngine
import com.tangem.domain.wallet.rsk.RskEngine
@@ -33,6 +34,7 @@ object CoinEngineFactory {
Blockchain.Litecoin -> LtcEngine()
Blockchain.Rootstock -> RskEngine()
Blockchain.RootstockToken -> RskTokenEngine()
+ Blockchain.Cardano -> CardanoEngine()
else -> null
}
}
@@ -57,6 +59,8 @@ object CoinEngineFactory {
RskEngine(context)
else if (Blockchain.RootstockToken == context.blockchain)
RskTokenEngine(context)
+ else if (Blockchain.Cardano == context.blockchain)
+ CardanoEngine(context)
else
return null
} catch (e: Exception) {
diff --git a/app/src/main/java/com/tangem/domain/wallet/bch/BitcoinCashNode.kt b/app/src/main/java/com/tangem/domain/wallet/bch/BitcoinCashNode.kt
index 7123f17dd2..66eedee951 100644
--- a/app/src/main/java/com/tangem/domain/wallet/bch/BitcoinCashNode.kt
+++ b/app/src/main/java/com/tangem/domain/wallet/bch/BitcoinCashNode.kt
@@ -8,8 +8,7 @@ enum class BitcoinCashNode(val host: String, val port: Int, val proto: String) {
N_005("bch.crypto.mldlabs.com", 50002, "ssl"),
N_006("electron.coinucopia.io", 50002, "ssl"),
N_007("blackie.c3-soft.com", 50002, "ssl"),
- N_008("electroncash.ueo.ch", 51002, "ssl"),
- N_009("electrum.imaginary.cash", 50002, "ssl"),
- N_010("bitcoincash.quangld.com", 50002, "ssl"),
- N_011("bch.stitthappens.com", 50002, "ssl"),
+ N_008("electrum.imaginary.cash", 50002, "ssl"),
+ N_009("bitcoincash.quangld.com", 50002, "ssl"),
+ N_010("bch.stitthappens.com", 50002, "ssl"),
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/domain/wallet/btc/BitcoinNode.kt b/app/src/main/java/com/tangem/domain/wallet/btc/BitcoinNode.kt
index 6a0c66d540..2af5648db2 100644
--- a/app/src/main/java/com/tangem/domain/wallet/btc/BitcoinNode.kt
+++ b/app/src/main/java/com/tangem/domain/wallet/btc/BitcoinNode.kt
@@ -2,44 +2,31 @@ package com.tangem.domain.wallet.btc
enum class BitcoinNode(val host: String, val port: Int, val proto: String) {
N_001("electrum.anduck.net", 50001, "tcp"),
- N_002("ip119.ip-54-37-91.eu", 50001, "tcp"),
- N_003("electrum.qtornado.com", 50001, "tcp"),
- N_004("ip239.ip-54-36-234.eu", 50001, "tcp"),
- N_005("electrum-server.ninja", 50001, "tcp"),
- N_006("vps.hsmiths.com", 8080, "tcp"),
- N_007("ip120.ip-54-37-91.eu", 50001, "tcp"),
- N_008("ip101.ip-54-37-91.eu", 50001, "tcp"),
- N_009("e-x.not.fyi", 50001, "tcp"),
- N_010("electrum.vom-stausee.de", 50001, "tcp"),
- N_011("electrum2.eff.ro", 50001, "tcp"),
- N_012("electrum.coinucopia.io", 50001, "tcp"),
- N_013("b.ooze.cc", 50002, "ssl"),
- N_014("electrum.nute.net", 50002, "ssl"),
- N_015("electrum.coinop.cc", 50002, "ssl"),
- N_016("electrum.vom-stausee.de", 50002, "ssl"),
- N_017("ip119.ip-54-37-91.eu", 50002, "ssl"),
- N_018("ip101.ip-54-37-91.eu", 50002, "ssl"),
- N_019("dedi.jochen-hoenicke.de", 50002, "ssl"),
- N_020("ip239.ip-54-36-234.eu", 50002, "ssl"),
- N_021("e-x.not.fyi", 50002, "ssl"),
- N_022("ip120.ip-54-37-91.eu", 50002, "ssl"),
- N_023("electrum.villocq.com", 50002, "ssl"),
- N_024("electrum.anduck.net", 50012, "ssl"),
- N_025("technetium.network", 50002, "ssl"),
- N_026("electrum.coinucopia.io", 50002, "ssl"),
- N_027("dimon.trimon.de", 50002, "ssl"),
- N_028("btc.gravitech.net", 50002, "ssl"),
- N_029("hetzner01.fischl-online.de", 50002, "ssl"),
- N_030("fn.48.org", 50002, "ssl"),
- N_031("185.64.116.15", 50002, "ssl"),
- N_032("vps.hsmiths.com", 50002, "ssl"),
- N_033("104.250.141.242", 50002, "ssl"),
- N_034("electrum.qtornado.com", 50002, "ssl"),
- N_035("electrum-server.ninja", 50002, "ssl"),
- N_036("electrum2.eff.ro", 50002, "ssl"),
- N_037("electrum.hsmiths.com", 995, "ssl"),
- N_038("electrum.hsmiths.com", 50002, "ssl"),
- N_039("139.162.14.142", 50002, "ssl"),
- N_040("tardis.bauerj.eu", 50002, "ssl"),
- N_041("such.ninja", 50002, "ssl"),
+ N_002("electrum.qtornado.com", 50001, "tcp"),
+ N_003("e-x.not.fyi", 50001, "tcp"),
+ N_004("electrum.vom-stausee.de", 50001, "tcp"),
+ N_005("electrum2.eff.ro", 50001, "tcp"),
+ N_006("electrum.coinucopia.io", 50001, "tcp"),
+ N_007("electrum.nute.net", 50002, "ssl"),
+ N_008("electrum.coinop.cc", 50002, "ssl"),
+ N_009("electrum.vom-stausee.de", 50002, "ssl"),
+ N_010("dedi.jochen-hoenicke.de", 50002, "ssl"),
+ N_011("e-x.not.fyi", 50002, "ssl"),
+ N_012("ip120.ip-54-37-91.eu", 50002, "ssl"),
+ N_013("electrum.villocq.com", 50002, "ssl"),
+ N_014("electrum.anduck.net", 50012, "ssl"),
+ N_015("technetium.network", 50002, "ssl"),
+ N_016("electrum.coinucopia.io", 50002, "ssl"),
+ N_017("dimon.trimon.de", 50002, "ssl"),
+ N_018("btc.gravitech.net", 50002, "ssl"),
+ N_019("hetzner01.fischl-online.de", 50002, "ssl"),
+ N_020("fn.48.org", 50002, "ssl"),
+ N_021("vps.hsmiths.com", 50002, "ssl"),
+ N_022("electrum.qtornado.com", 50002, "ssl"),
+ N_023("electrum-server.ninja", 50002, "ssl"),
+ N_024("electrum2.eff.ro", 50002, "ssl"),
+ N_025("electrum.hsmiths.com", 995, "ssl"),
+ N_026("electrum.hsmiths.com", 50002, "ssl"),
+ N_027("139.162.14.142", 50002, "ssl"),
+ N_028("tardis.bauerj.eu", 50002, "ssl"),
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java
index b2809112fe..8c63bb608d 100644
--- a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java
+++ b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java
@@ -195,7 +195,6 @@ public class BtcEngine extends CoinEngine {
@Override
public Uri getShareWalletUri() {
- byte[] x = new byte[] {0,0,0,0,0,0,0,0};
if (ctx.getCard().getDenomination() != null && !ctx.getCard().getDenominationText().equals("0.00")) {
return Uri.parse("bitcoin:" + ctx.getCoinData().getWallet() + "?amount=" + convertToAmount(convertToInternalAmount(ctx.getCard().getDenomination())).toValueString(8));
} else {
diff --git a/app/src/main/java/com/tangem/domain/wallet/cardano/Blake2b.java b/app/src/main/java/com/tangem/domain/wallet/cardano/Blake2b.java
new file mode 100644
index 0000000000..b50c045435
--- /dev/null
+++ b/app/src/main/java/com/tangem/domain/wallet/cardano/Blake2b.java
@@ -0,0 +1,2098 @@
+/* !!! Doost !!! */
+
+/*
+ A Java implementation of BLAKE2B cryptographic digest algorithm.
+
+ Joubin Mohammad Houshyar
+ bushwick, nyc
+ 02-14-2014
+
+ --
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see .
+*/
+
+package com.tangem.domain.wallet.cardano;
+
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec; // JCE not supported / anticipated ..
+import java.util.Arrays;
+
+import static com.tangem.domain.wallet.cardano.Blake2b.Engine.Assert.assertFail;
+import static com.tangem.domain.wallet.cardano.Blake2b.Engine.Assert.exclusiveLowerBound;
+import static com.tangem.domain.wallet.cardano.Blake2b.Engine.Assert.inclusiveLowerBound;
+import static com.tangem.domain.wallet.cardano.Blake2b.Engine.Assert.inclusiveUpperBound;
+import static com.tangem.domain.wallet.cardano.Blake2b.Engine.LittleEndian.readInt;
+import static com.tangem.domain.wallet.cardano.Blake2b.Engine.LittleEndian.readLong;
+import static com.tangem.domain.wallet.cardano.Blake2b.Engine.LittleEndian.writeInt;
+import static com.tangem.domain.wallet.cardano.Blake2b.Engine.LittleEndian.writeLong;
+
+
+
+/** */
+public interface Blake2b {
+ // ---------------------------------------------------------------------
+ // Specification
+ // ---------------------------------------------------------------------
+ public interface Spec {
+ /** pblock size of blake2b */
+ int param_bytes = 64;
+
+ /** pblock size of blake2b */
+ int block_bytes = 128;
+
+ /** maximum digest size */
+ int max_digest_bytes = 64;
+
+ /** maximum key sie */
+ int max_key_bytes = 64;
+
+ /** maximum salt size */
+ int max_salt_bytes = 16;
+
+ /** maximum personalization string size */
+ int max_personalization_bytes = 16;
+
+ /** length of h space vector array */
+ int state_space_len = 8;
+
+ /** max tree fanout value */
+ int max_tree_fantout = 0xFF;
+
+ /** max tree depth value */
+ int max_tree_depth = 0xFF;
+
+ /** max tree leaf length value.Note that this has uint32 semantics
+ and thus 0xFFFFFFFF is used as max value limit. */
+ int max_tree_leaf_length = 0xFFFFFFFF;
+
+ /** max node offset value. Note that this has uint64 semantics
+ and thus 0xFFFFFFFFFFFFFFFFL is used as max value limit. */
+ long max_node_offset = 0xFFFFFFFFFFFFFFFFL;
+
+ /** max tree inner length value */
+ int max_tree_inner_length = 0xFF;
+
+ /** initialization values map ref-Spec IV[i] -> slice iv[i*8:i*8+7] */
+ long[] IV = {
+ 0x6a09e667f3bcc908L,
+ 0xbb67ae8584caa73bL,
+ 0x3c6ef372fe94f82bL,
+ 0xa54ff53a5f1d36f1L,
+ 0x510e527fade682d1L,
+ 0x9b05688c2b3e6c1fL,
+ 0x1f83d9abfb41bd6bL,
+ 0x5be0cd19137e2179L
+ };
+ }
+
+ // ---------------------------------------------------------------------
+ // API
+ // ---------------------------------------------------------------------
+ // TODO add ByteBuffer variants
+
+ /**
+ * A serializable / JSON-izable object usable for pausing a hash-in-process
+ * which can then be resumed with the same Parameter the original digest
+ * was constructed with, and fed additional bytes to conclude the hash.
+ */
+ public static final class ResumeHandle implements Serializable {
+ /** per spec */
+ public long[] h = new long [ 8 ];
+ /** per spec */
+ public long[] t = new long [ 2 ];
+ /** per spec */
+ public long[] f = new long [ 2 ];
+ /** per spec (tree) */
+ public boolean last_node = false;
+ /** pulled up 2b optimal */
+ public long[] m = new long [16];
+ /** pulled up 2b optimal */
+ public long[] v = new long [16];
+
+ /** compressor cache buffer */
+ public byte[] buffer;
+ /** compressor cache buffer offset/cached data length */
+ public int buflen;
+
+ /** digest length from init param - copied here on init */
+ public int outlen;
+
+ public int type;
+
+ /**
+ * Create a reconstituted Black2b digest from
+ *
+ * @param param
+ * @return
+ */
+ public Blake2b resume(Param param) {
+ assert this.buffer != null && this.buffer.length == Spec.block_bytes;
+ assert this.h != null && this.h.length == 8
+ && this.t != null && this.t.length == 2
+ && this.f != null && this.f.length == 2
+ && this.m != null && this.m.length == 16
+ && this.v != null && this.v.length == 16 : "Data is corrupted";
+ assert this.outlen == param.getDigestLength() : "Not originally initialized from this param";
+ assert this.type == 1 || this.type == 2 : "Unknown type " + this.type;
+ Engine.State state = new Engine.State(outlen, this.type == 1);
+ System.arraycopy(this.h, 0, state.h, 0, state.h.length);
+ System.arraycopy(this.t, 0, state.t, 0, state.t.length);
+ System.arraycopy(this.f, 0, state.f, 0, state.f.length);
+ System.arraycopy(this.m, 0, m, 0, m.length);
+ System.arraycopy(this.v, 0, v, 0, v.length);
+ System.arraycopy(this.buffer, 0, state.buffer, 0, state.buffer.length);
+ state.buflen = buflen;
+ return type == 1 ? new Mac(param, state) : new Digest(param, state);
+ }
+ }
+
+ /** */
+ void update(byte[] input) ;
+
+ /** */
+ void update(byte input) ;
+
+ /** */
+ void update(byte[] input, int offset, int len) ;
+
+ /** */
+ byte[] digest() ;
+
+ /** */
+ byte[] digest(byte[] input) ;
+
+ /** */
+ void digest(byte[] output, int offset, int len) ;
+
+ /** */
+ void reset() ;
+
+ ResumeHandle state();
+ // ---------------------------------------------------------------------
+ // Blake2b Message Digest
+ // ---------------------------------------------------------------------
+
+ /** Generalized Blake2b digest. */
+ public static class Digest extends Engine implements Blake2b {
+ private Digest (final Param p) { super (p); }
+ private Digest () { super (); }
+ private Digest(Param p, State state) {
+ super(state, p);
+ }
+
+ public static Digest newInstance () {
+ return new Digest ();
+ }
+ public static Digest newInstance (final int digestLength) {
+ return new Digest (new Param().setDigestLength(digestLength));
+ }
+ public static Digest newInstance (Param p) {
+ return new Digest (p);
+ }
+ public static Digest newInstance (Param p, State state) {
+ return new Digest(p, state);
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ // Blake2b Message Authentication Code
+ // ---------------------------------------------------------------------
+
+ /** Message Authentication Code (MAC) digest. */
+ public static class Mac extends Engine implements Blake2b {
+ private Mac (final Param p, State state) { super (state, p); }
+ private Mac (final Param p) { super (p); }
+ private Mac () { super (); }
+
+ /** Blake2b.MAC 512 - using default Blake2b.Spec settings with given key */
+ public static Mac newInstance (final byte[] key) {
+ return new Mac (new Param().setKey(key));
+ }
+ /** Blake2b.MAC - using default Blake2b.Spec settings with given key, with given digest length */
+ public static Mac newInstance (final byte[] key, final int digestLength) {
+ return new Mac (new Param().setKey(key).setDigestLength(digestLength));
+ }
+ /** Blake2b.MAC - using default Blake2b.Spec settings with given java.security.Key, with given digest length */
+ public static Mac newInstance (final Key key, final int digestLength) {
+ return new Mac (new Param().setKey(key).setDigestLength(digestLength));
+ }
+ /** Blake2b.MAC - using the specified Parameters.
+ * @param p asserted valid configured Param with key */
+ public static Mac newInstance (Param p) {
+ assert p != null : "Param (p) is null";
+ assert p.hasKey() : "Param (p) not configured with a key";
+ return new Mac (p);
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ // Blake2b Incremental Message Digest (Tree)
+ // ---------------------------------------------------------------------
+
+ /**
+ * Note that Tree is just a convenience class; incremental hash (tree)
+ * can be done directly with the Digest class.
+ *
+ * Further node, that tree does NOT accumulate the leaf hashes --
+ * you need to do that
+ */
+ public static class Tree {
+
+ final int depth;
+ final int fanout;
+ final int leaf_length;
+ final int inner_length;
+ final int digest_length;
+
+ /**
+ *
+ * @param fanout
+ * @param depth
+ * @param leaf_length size of data input for leaf nodes.
+ * @param inner_length note this is used also as digest-length for non-root nodes.
+ * @param digest_length final hash out digest-length for the tree
+ */
+ public Tree (
+ final int depth,
+ final int fanout,
+ final int leaf_length,
+ final int inner_length,
+ final int digest_length
+ ) {
+ this.fanout = fanout;
+ this.depth = depth;
+ this.leaf_length = leaf_length;
+ this.inner_length = inner_length;
+ this.digest_length = digest_length;
+ }
+ private Param treeParam() {
+ return new Param().
+ setDepth(depth).setFanout(fanout).setLeafLength(leaf_length).setInnerLength(inner_length);
+ }
+ /** returns the Digest for tree node @ (depth, offset) */
+ public final Digest getNode (final int depth, final int offset) {
+ final Param nodeParam = treeParam().setNodeDepth(depth).setNodeOffset(offset).setDigestLength(inner_length);
+ return Digest.newInstance(nodeParam);
+ }
+ /** returns the Digest for root node */
+ public final Digest getRoot () {
+ final int depth = this.depth - 1;
+ final Param rootParam = treeParam().setNodeDepth(depth).setNodeOffset(0L).setDigestLength(digest_length);
+ return Digest.newInstance(rootParam);
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ // Engine
+ // ---------------------------------------------------------------------
+ static class Engine implements Blake2b {
+
+ // ---------------------------------------------------------------------
+ // Blake2b State(+) per reference implementation
+ // ---------------------------------------------------------------------
+ // REVU: address last_node TODO part of the Tree/incremental
+ static final class State {
+ /** per spec */
+ private final long[] h = new long [ 8 ];
+ /** per spec */
+ private final long[] t = new long [ 2 ];
+ /** per spec */
+ private final long[] f = new long [ 2 ];
+ /** per spec (tree) */
+ private boolean last_node = false;
+ /** pulled up 2b optimal */
+ private final long[] m = new long [16];
+ /** pulled up 2b optimal */
+ private final long[] v = new long [16];
+
+ /** compressor cache buffer */
+ private final byte[] buffer;
+ /** compressor cache buffer offset/cached data length */
+ private int buflen;
+
+ /** digest length from init param - copied here on init */
+ private final int outlen;
+
+ private final int digestType;
+
+ State(int digestLength, boolean isMac) {
+ this.buffer = new byte [ Spec.block_bytes ];
+ this.outlen = digestLength;
+ // do not use zero, so we can detect serialization errors
+ this.digestType = isMac ? 1 : 2;
+ }
+
+ public ResumeHandle toResumableForm() {
+ ResumeHandle state = new ResumeHandle();
+ state.h = h;
+ state.t = t;
+ state.f = f;
+ state.last_node = last_node;
+ state.m = m;
+ state.v = v;
+ state.buffer = buffer;
+ state.buflen = buflen;
+ state.outlen = outlen;
+ state.type = digestType;
+ return state;
+ }
+ }
+
+ private State state;
+ /** configuration params */
+ private final Param param;
+
+ /** read only */
+ private static byte[] zeropad = new byte [ Spec.block_bytes ];
+
+ /** a little bit of semantics */
+ interface flag {
+ int last_block = 0;
+ int last_node = 1;
+ }
+ /** to support update(byte) */
+ private final byte[] oneByte = new byte[1];
+
+ // ---------------------------------------------------------------------
+ // Ctor & Initialization
+ // ---------------------------------------------------------------------
+
+ /** Basic use constructor pending (TODO) JCA/JCE compliance */
+ Engine () {
+ this( new Param() );
+ }
+
+ Engine(State state, Param param) {
+ assert state != null : "state is null";
+ assert param != null : "param is null";
+ this.state = state;
+ this.param = param;
+ }
+
+ /** User provided Param for custom configurations */
+ Engine (final Param param) {
+ assert param != null : "param is null";
+ this.param = param;
+ state = new State(param.getDigestLength(), this instanceof Mac);
+ if ( param.getDepth() > Param.Default.depth ) {
+ final int ndepth = param.getNodeDepth();
+ final long nxoff = param.getNodeOffset();
+ if (ndepth == param.getDepth() - 1) {
+ state.last_node = true;
+ assert nxoff == 0 : "root must have offset of zero";
+ } else if ( nxoff == param.getFanout() - 1) {
+ this.state.last_node = true;
+ }
+ }
+
+ initialize();
+ }
+
+ public ResumeHandle state() {
+ return state.toResumableForm();
+ }
+
+ private void initialize () {
+ // state vector h - copy values to address reset() requests
+ System.arraycopy( param.initialized_H(), 0, this.state.h, 0, Spec.state_space_len);
+
+ // if we have a key update initial block
+ // Note param has zero padded key_bytes to Spec.max_key_bytes
+ if(param.hasKey){
+ this.update (param.key_bytes, 0, Spec.block_bytes);
+ }
+ }
+
+ public static void main(String... args) {
+ Blake2b mac = Mac.newInstance("LOVE".getBytes());
+ final byte[] hash = mac.digest("Salaam!".getBytes());
+// Debug.dumpBuffer(System.out, "-- mac hash --", hash);
+ }
+
+ // ---------------------------------------------------------------------
+ // interface: Blake2b API
+ // ---------------------------------------------------------------------
+
+ /** {@inheritDoc} */
+ @Override final public void reset () {
+ // reset cache
+ this.state.buflen = 0;
+ for(int i=0; i < state.buffer.length; i++){
+ state.buffer[ i ] = (byte) 0;
+ }
+
+ // reset flags
+ this.state.f[ 0 ] = 0L;
+ this.state.f[ 1 ] = 0L;
+
+ // reset counters
+ this.state.t[ 0 ] = 0L;
+ this.state.t[ 1 ] = 0L;
+
+ // reset state vector
+ // NOTE: keep as last stmt as init calls update0 for MACs.
+ initialize();
+ }
+
+ /** {@inheritDoc} */
+ @Override final public void update (final byte[] b, int off, int len) {
+ if (b == null) {
+ throw new IllegalArgumentException("input buffer (b) is null");
+ }
+ /* zero or more calls to compress */
+ final long[] t = state.t;
+ final byte[] buffer = state.buffer;
+ while (len > 0) {
+ if ( state.buflen == 0) {
+ /* try compressing direct from input ? */
+ while ( len > Spec.block_bytes ) {
+ t[0] += Spec.block_bytes;
+ t[1] += (t[0] < 0 && state.buflen > -t[0]) ? 1 : 0;
+ compress( b, off);
+ len -= Spec.block_bytes;
+ off += Spec.block_bytes;
+ }
+ } else if ( state.buflen == Spec.block_bytes ) {
+ /* flush */
+ t[0] += Spec.block_bytes;
+ t[1] += t[0] == 0 ? 1 : 0;
+ compress( buffer, 0 );
+ state.buflen = 0;
+ continue;
+ }
+
+ // "are we there yet?"
+ if( len == 0 ) return;
+
+ final int cap = Spec.block_bytes - state.buflen;
+ final int fill = len > cap ? cap : len;
+ System.arraycopy( b, off, buffer, state.buflen, fill );
+ state.buflen += fill;
+ len -= fill;
+ off += fill;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override final public void update (byte b) {
+ oneByte[0] = b;
+ update (oneByte, 0, 1);
+ }
+
+ /** {@inheritDoc} */
+ @Override final public void update(byte[] input) {
+ update (input, 0, input.length);
+ }
+
+ /** {@inheritDoc} */
+ @Override final public void digest(byte[] output, int off, int len) {
+ // zero pad last block; set last block flags; and compress
+ System.arraycopy( zeropad, 0, state.buffer, state.buflen, Spec.block_bytes - state.buflen);
+ if(state.buflen > 0) {
+ this.state.t[0] += state.buflen;
+ this.state.t[1] += this.state.t[0] == 0 ? 1 : 0;
+ }
+
+ this.state.f[ flag.last_block ] = 0xFFFFFFFFFFFFFFFFL;
+ this.state.f[ flag.last_node ] = this.state.last_node ? 0xFFFFFFFFFFFFFFFFL : 0x0L;
+
+ // compres and write final out (truncated to len) to output
+ compress( state.buffer, 0 );
+ hashout( output, off, len );
+
+ reset();
+ }
+
+ /** {@inheritDoc} */
+ @Override final public byte[] digest () throws IllegalArgumentException {
+ final byte[] out = new byte [state.outlen];
+ digest ( out, 0, state.outlen );
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override final public byte[] digest (byte[] input) {
+ update(input, 0, input.length);
+ return digest();
+ }
+
+ // ---------------------------------------------------------------------
+ // Internal Ops
+ // ---------------------------------------------------------------------
+
+ /**
+ * write out the digest output from the 'h' registers.
+ * truncate full output if necessary.
+ */
+ private void hashout (final byte[] out, final int offset, final int hashlen) {
+ // write max number of whole longs
+ final int lcnt = hashlen >>> 3;
+ long v = 0;
+ int i = offset;
+ final long[] h = state.h;
+ for (int w = 0; w < lcnt; w++) {
+ v = h [ w ];
+ out [ i ] = (byte) v; v >>>= 8;
+ out [ i+1 ] = (byte) v; v >>>= 8;
+ out [ i+2 ] = (byte) v; v >>>= 8;
+ out [ i+3 ] = (byte) v; v >>>= 8;
+ out [ i+4 ] = (byte) v; v >>>= 8;
+ out [ i+5 ] = (byte) v; v >>>= 8;
+ out [ i+6 ] = (byte) v; v >>>= 8;
+ out [ i+7 ] = (byte) v;
+ i+=8;
+ }
+
+ // basta?
+ if( hashlen == Spec.max_digest_bytes) return;
+
+ // write the remaining bytes of a partial long value
+ v = h [ lcnt ];
+ i = lcnt << 3;
+ while( i < hashlen ) {
+ out [ offset + i ] = (byte) v; v >>>= 8; ++i;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ /// Compression Kernel /////////////////////////////////////////// BEGIN
+ ////////////////////////////////////////////////////////////////////////
+
+ /** compress Spec.block_bytes data from b, from offset */
+ private void compress (final byte[] b, final int offset) {
+
+ // set m registers
+ final long[] m = state.m;
+ m[ 0] = LittleEndian.readLong(b, offset);
+ m[ 1] = LittleEndian.readLong(b, offset + 8);
+ m[ 2] = LittleEndian.readLong(b, offset + 16);
+ m[ 3] = LittleEndian.readLong(b, offset + 24);
+ m[ 4] = LittleEndian.readLong(b, offset + 32);
+ m[ 5] = LittleEndian.readLong(b, offset + 40);
+ m[ 6] = LittleEndian.readLong(b, offset + 48);
+ m[ 7] = LittleEndian.readLong(b, offset + 56);
+ m[ 8] = LittleEndian.readLong(b, offset + 64);
+ m[ 9] = LittleEndian.readLong(b, offset + 72);
+ m[10] = LittleEndian.readLong(b, offset + 80);
+ m[11] = LittleEndian.readLong(b, offset + 88);
+ m[12] = LittleEndian.readLong(b, offset + 96);
+ m[13] = LittleEndian.readLong(b, offset + 104);
+ m[14] = LittleEndian.readLong(b, offset + 112);
+ m[15] = LittleEndian.readLong(b, offset + 120);
+
+ // set v registers
+ final long[] v = state.v;
+ final long[] h = state.h;
+ final long[] t = state.t;
+ final long[] f = state.f;
+ v[ 0] = h[0];
+ v[ 1] = h[1];
+ v[ 2] = h[2];
+ v[ 3] = h[3];
+ v[ 4] = h[4];
+ v[ 5] = h[5];
+ v[ 6] = h[6];
+ v[ 7] = h[7];
+ v[ 8] = 0x6a09e667f3bcc908L;
+ v[ 9] = 0xbb67ae8584caa73bL;
+ v[10] = 0x3c6ef372fe94f82bL;
+ v[11] = 0xa54ff53a5f1d36f1L;
+ v[12] = t [0] ^ 0x510e527fade682d1L;
+ v[13] = t [1] ^ 0x9b05688c2b3e6c1fL;
+ v[14] = f [0] ^ 0x1f83d9abfb41bd6bL;
+ v[15] = f [1] ^ 0x5be0cd19137e2179L;
+
+ // do the rounds
+ round_0(v, m);
+ round_1(v, m);
+ round_2(v, m);
+ round_3(v, m);
+ round_4(v, m);
+ round_5(v, m);
+ round_6(v, m);
+ round_7(v, m);
+ round_8(v, m);
+ round_9(v, m);
+ round_0(v, m); // round 10 is identical to round 0
+ round_1(v, m); // round 11 is identical to round 1
+
+ // Update state vector h
+ h[0] ^= v[0] ^ v[8];
+ h[1] ^= v[1] ^ v[9];
+ h[2] ^= v[2] ^ v[10];
+ h[3] ^= v[3] ^ v[11];
+ h[4] ^= v[4] ^ v[12];
+ h[5] ^= v[5] ^ v[13];
+ h[6] ^= v[6] ^ v[14];
+ h[7] ^= v[7] ^ v[15];
+
+ /* kaamil */
+ }
+ private void round_0(final long[] v, final long[] m) {
+ v[ 0] = v[ 0] + v[ 4] + m[0];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 0] = v[ 0] + v[ 4] + m [1];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 5] + m[2];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 1] = v[ 1] + v[ 5] + m[3];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 6] + m[4];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 2] = v[ 2] + v[ 6] + m[5];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 7] + m[6];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 3] = v[ 3] + v[ 7] + m[7];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 0] = v[ 0] + v[ 5] + m[8];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 0] = v[ 0] + v[ 5] + m[9];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 6] + m[10];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 1] = v[ 1] + v[ 6] + + m[11];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 7] + m[12];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 2] = v[ 2] + v[ 7] + m[13];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 4] + m[14];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 3] = v[ 3] + v[ 4] + m[15];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+ }
+ private void round_1(final long[] v, final long[] m) {
+ v[ 0] = v[ 0] + v[ 4] + m[14];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 0] = v[ 0] + v[ 4] + m [10];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 5] + m[4];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 1] = v[ 1] + v[ 5] + m[8];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 6] + m[9];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 2] = v[ 2] + v[ 6] + m[15];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 7] + m[13];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 3] = v[ 3] + v[ 7] + m[6];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 0] = v[ 0] + v[ 5] + m[1];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 0] = v[ 0] + v[ 5] + m[12];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 6] + m[0];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 1] = v[ 1] + v[ 6] + + m[2];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 7] + m[11];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 2] = v[ 2] + v[ 7] + m[7];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 4] + m[5];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 3] = v[ 3] + v[ 4] + m[3];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+ }
+ private void round_2(final long[] v, final long[] m) {
+ v[ 0] = v[ 0] + v[ 4] + m[11];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 0] = v[ 0] + v[ 4] + m [8];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 5] + m[12];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 1] = v[ 1] + v[ 5] + m[0];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 6] + m[5];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 2] = v[ 2] + v[ 6] + m[2];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 7] + m[15];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 3] = v[ 3] + v[ 7] + m[13];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 0] = v[ 0] + v[ 5] + m[10];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 0] = v[ 0] + v[ 5] + m[14];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 6] + m[3];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 1] = v[ 1] + v[ 6] + + m[6];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 7] + m[7];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 2] = v[ 2] + v[ 7] + m[1];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 4] + m[9];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 3] = v[ 3] + v[ 4] + m[4];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+ }
+ private void round_3(final long[] v, final long[] m) {
+ v[ 0] = v[ 0] + v[ 4] + m[7];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 0] = v[ 0] + v[ 4] + m [9];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 5] + m[3];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 1] = v[ 1] + v[ 5] + m[1];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 6] + m[13];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 2] = v[ 2] + v[ 6] + m[12];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 7] + m[11];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 3] = v[ 3] + v[ 7] + m[14];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 0] = v[ 0] + v[ 5] + m[2];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 0] = v[ 0] + v[ 5] + m[6];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 6] + m[5];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 1] = v[ 1] + v[ 6] + + m[10];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 7] + m[4];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 2] = v[ 2] + v[ 7] + m[0];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 4] + m[15];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 3] = v[ 3] + v[ 4] + m[8];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+ }
+ private void round_4(final long[] v, final long[] m) {
+ v[ 0] = v[ 0] + v[ 4] + m[9];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 0] = v[ 0] + v[ 4] + m [0];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 5] + m[5];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 1] = v[ 1] + v[ 5] + m[7];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 6] + m[2];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 2] = v[ 2] + v[ 6] + m[4];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 7] + m[10];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 3] = v[ 3] + v[ 7] + m[15];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 0] = v[ 0] + v[ 5] + m[14];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 0] = v[ 0] + v[ 5] + m[1];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 6] + m[11];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 1] = v[ 1] + v[ 6] + + m[12];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 7] + m[6];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 2] = v[ 2] + v[ 7] + m[8];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 4] + m[3];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 3] = v[ 3] + v[ 4] + m[13];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+ }
+ private void round_5(final long[] v, final long[] m) {
+ v[ 0] = v[ 0] + v[ 4] + m[2];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 0] = v[ 0] + v[ 4] + m [12];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 5] + m[6];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 1] = v[ 1] + v[ 5] + m[10];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 6] + m[0];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 2] = v[ 2] + v[ 6] + m[11];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 7] + m[8];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 3] = v[ 3] + v[ 7] + m[3];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 0] = v[ 0] + v[ 5] + m[4];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 0] = v[ 0] + v[ 5] + m[13];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 6] + m[7];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 1] = v[ 1] + v[ 6] + + m[5];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 7] + m[15];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 2] = v[ 2] + v[ 7] + m[14];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 4] + m[1];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 3] = v[ 3] + v[ 4] + m[9];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+ }
+ private void round_6(final long[] v, final long[] m) {
+ v[ 0] = v[ 0] + v[ 4] + m[12];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 0] = v[ 0] + v[ 4] + m [5];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 5] + m[1];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 1] = v[ 1] + v[ 5] + m[15];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 6] + m[14];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 2] = v[ 2] + v[ 6] + m[13];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 7] + m[4];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 3] = v[ 3] + v[ 7] + m[10];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 0] = v[ 0] + v[ 5] + m[0];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 0] = v[ 0] + v[ 5] + m[7];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 6] + m[6];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 1] = v[ 1] + v[ 6] + + m[3];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 7] + m[9];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 2] = v[ 2] + v[ 7] + m[2];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 4] + m[8];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 3] = v[ 3] + v[ 4] + m[11];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+ }
+ private void round_7(final long[] v, final long[] m) {
+ v[ 0] = v[ 0] + v[ 4] + m[13];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 0] = v[ 0] + v[ 4] + m [11];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 5] + m[7];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 1] = v[ 1] + v[ 5] + m[14];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 6] + m[12];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 2] = v[ 2] + v[ 6] + m[1];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 7] + m[3];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 3] = v[ 3] + v[ 7] + m[9];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 0] = v[ 0] + v[ 5] + m[5];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 0] = v[ 0] + v[ 5] + m[0];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 6] + m[15];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 1] = v[ 1] + v[ 6] + + m[4];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 7] + m[8];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 2] = v[ 2] + v[ 7] + m[6];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 4] + m[2];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 3] = v[ 3] + v[ 4] + m[10];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+ }
+ private void round_8(final long[] v, final long[] m) {
+ v[ 0] = v[ 0] + v[ 4] + m[6];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 0] = v[ 0] + v[ 4] + m [15];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 5] + m[14];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 1] = v[ 1] + v[ 5] + m[9];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 6] + m[11];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 2] = v[ 2] + v[ 6] + m[3];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 7] + m[0];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 3] = v[ 3] + v[ 7] + m[8];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 0] = v[ 0] + v[ 5] + m[12];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 0] = v[ 0] + v[ 5] + m[2];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 6] + m[13];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 1] = v[ 1] + v[ 6] + + m[7];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 7] + m[1];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 2] = v[ 2] + v[ 7] + m[4];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 4] + m[10];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 3] = v[ 3] + v[ 4] + m[5];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+ }
+ private void round_9(final long[] v, final long[] m) {
+ v[ 0] = v[ 0] + v[ 4] + m[10];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 0] = v[ 0] + v[ 4] + m [2];
+ v[12] ^= v[ 0];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[ 8] = v[ 8] + v[12];
+ v[ 4] ^= v[ 8];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 5] + m[8];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 1] = v[ 1] + v[ 5] + m[4];
+ v[13] ^= v[ 1];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 9] = v[ 9] + v[13];
+ v[ 5] ^= v[ 9];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 6] + m[7];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 2] = v[ 2] + v[ 6] + m[6];
+ v[14] ^= v[ 2];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[10] = v[10] + v[14];
+ v[ 6] ^= v[10];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 7] + m[1];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 3] = v[ 3] + v[ 7] + m[5];
+ v[15] ^= v[ 3];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[11] = v[11] + v[15];
+ v[ 7] ^= v[11];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 0] = v[ 0] + v[ 5] + m[15];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] << 32 ) | ( v[15] >>> 32 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] >>> 24 ) | ( v[ 5] << 40 );
+ v[ 0] = v[ 0] + v[ 5] + m[11];
+ v[15] ^= v[ 0];
+ v[15] = ( v[15] >>> 16 ) | ( v[15] << 48 );
+ v[10] = v[10] + v[15];
+ v[ 5] ^= v[10];
+ v[ 5] = ( v[ 5] << 1 ) | ( v[ 5] >>> 63 );
+
+ v[ 1] = v[ 1] + v[ 6] + m[9];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] << 32 ) | ( v[12] >>> 32 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] >>> 24 ) | ( v[ 6] << 40 );
+ v[ 1] = v[ 1] + v[ 6] + + m[14];
+ v[12] ^= v[ 1];
+ v[12] = ( v[12] >>> 16 ) | ( v[12] << 48 );
+ v[11] = v[11] + v[12];
+ v[ 6] ^= v[11];
+ v[ 6] = ( v[ 6] << 1 ) | ( v[ 6] >>> 63 );
+
+ v[ 2] = v[ 2] + v[ 7] + m[3];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] << 32 ) | ( v[13] >>> 32 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] >>> 24 ) | ( v[ 7] << 40 );
+ v[ 2] = v[ 2] + v[ 7] + m[12];
+ v[13] ^= v[ 2];
+ v[13] = ( v[13] >>> 16 ) | ( v[13] << 48 );
+ v[ 8] = v[ 8] + v[13];
+ v[ 7] ^= v[ 8];
+ v[ 7] = ( v[ 7] << 1 ) | ( v[ 7] >>> 63 );
+
+ v[ 3] = v[ 3] + v[ 4] + m[13];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] << 32 ) | ( v[14] >>> 32 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] >>> 24 ) | ( v[ 4] << 40 );
+ v[ 3] = v[ 3] + v[ 4] + m[0];
+ v[14] ^= v[ 3];
+ v[14] = ( v[14] >>> 16 ) | ( v[14] << 48 );
+ v[ 9] = v[ 9] + v[14];
+ v[ 4] ^= v[ 9];
+ v[ 4] = ( v[ 4] << 1 ) | ( v[ 4] >>> 63 );
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ /// Compression Kernel //////////////////////////////////////////// FINI
+ ////////////////////////////////////////////////////////////////////////
+
+ /* TEMP - remove at will */
+ public static class Debug {
+ public static void dumpState (Engine e, final String mark) {
+ System.out.format("-- MARK == @ %s @ ===========\n", mark);
+ dumpArray("register t", e.state.t);
+ dumpArray("register h", e.state.h);
+ dumpArray("register f", e.state.f);
+ dumpArray("register offset", new long[]{e.state.buflen});
+ System.out.format("-- END MARK =================\n");
+ }
+ public static void dumpArray (final String label, final long[] b) {
+ System.out.format ( "-- %s -- :\n{\n", label );
+ for( int j = 0; j < b.length ; ++j ) {
+ System.out.format ( " [%2d] : %016X\n", j, b[j]);
+ }
+ System.out.format ( "}\n" );
+ }
+ public static void dumpBuffer (final PrintStream out, final String label, final byte[] b) {
+ dumpBuffer(out, label, b, 0, b.length);
+ }
+ public static void dumpBuffer (final PrintStream out, final byte[] b) {
+ dumpBuffer(out, null, b, 0, b.length);
+ }
+ public static void dumpBuffer (final PrintStream out, final byte[] b, final int offset, final int len) {
+ dumpBuffer(out, null, b, offset, len);
+ }
+ public static void dumpBuffer (final PrintStream out, final String label, final byte[] b, final int offset, final int len) {
+ if(label != null)
+ out.format ( "-- %s -- :\n", label );
+ out.format("{\n ", label);
+ for( int j = 0; j < len ; ++j ) {
+ out.format ("%02X", b[j + offset]);
+ if(j+1 < len) {
+ if ((j+1)%8==0) out.print("\n ");
+ else out.print(' ');
+ }
+ }
+ out.format("\n}\n");
+ }
+ }
+ /* TEMP - remove at will */
+
+ // ---------------------------------------------------------------------
+ // Helper for assert error messages
+ // ---------------------------------------------------------------------
+ public static final class Assert {
+ public final static String exclusiveUpperBound = "'%s' %d is >= %d";
+ public final static String inclusiveUpperBound = "'%s' %d is > %d";
+ public final static String exclusiveLowerBound = "'%s' %d is <= %d";
+ public final static String inclusiveLowerBound = "'%s' %d is < %d";
+ static String assertFail(final String name, final T v, final String err, final T spec) {
+ new Exception().printStackTrace();
+ return String.format(err, name, v, spec);
+ }
+ }
+ // ---------------------------------------------------------------------
+ // Little Endian Codecs (inlined in the compressor)
+ /*
+ * impl note: these are not library funcs and used in hot loops, so no
+ * null or bounds checks are performed. For our purposes, this is OK.
+ */
+ // ---------------------------------------------------------------------
+
+ public static class LittleEndian {
+ private static final byte[] hex_digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ private static final byte[] HEX_digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+ /** @return hex rep of byte (lower case). */
+ static public String toHexStr (final byte[] b) {
+ return toHexStr (b, false); // because String class is slower.
+ }
+ static public String toHexStr (final byte[] b, boolean upperCase) {
+ final int len = b.length;
+ final byte[] digits = new byte[ len * 2 ];
+ final byte[] hex_rep = upperCase ? HEX_digits : hex_digits ;
+ for (int i = 0; i < len; i++) {
+ digits [ i*2 ] = hex_rep [ (byte) (b[i] >> 4 & 0x0F) ];
+ digits [ i*2+1 ] = hex_rep [ (byte) (b[i] & 0x0F) ];
+ }
+ return new String(digits);
+ }
+ public static int readInt (final byte[] b, final int off) {
+ int v0 = ((int)b [ off ] & 0xFF );
+ v0 |= ((int)b [ off + 1 ] & 0xFF ) << 8;
+ v0 |= ((int)b [ off + 2 ] & 0xFF ) << 16;
+ return v0 | ((int)b [ off + 3 ] ) << 24;
+ }
+ /** Little endian - byte[] to long */
+ public static long readLong (final byte[] b, final int off) {
+ long v0 = ((long)b [ off ] & 0xFF );
+ v0 |= ((long)b [ off + 1 ] & 0xFF ) << 8;
+ v0 |= ((long)b [ off + 2 ] & 0xFF ) << 16;
+ v0 |= ((long)b [ off + 3 ] & 0xFF ) << 24;
+ v0 |= ((long)b [ off + 4 ] & 0xFF ) << 32;
+ v0 |= ((long)b [ off + 5 ] & 0xFF ) << 40;
+ v0 |= ((long)b [ off + 6 ] & 0xFF ) << 48;
+ return v0 | ((long)b [ off + 7 ] ) << 56;
+ }
+ /** Little endian - long to byte[] */
+ public static void writeLong (long v, final byte[] b, final int off) {
+ b [ off ] = (byte) v; v >>>= 8;
+ b [ off + 1 ] = (byte) v; v >>>= 8;
+ b [ off + 2 ] = (byte) v; v >>>= 8;
+ b [ off + 3 ] = (byte) v; v >>>= 8;
+ b [ off + 4 ] = (byte) v; v >>>= 8;
+ b [ off + 5 ] = (byte) v; v >>>= 8;
+ b [ off + 6 ] = (byte) v; v >>>= 8;
+ b [ off + 7 ] = (byte) v;
+ }
+ /** Little endian - int to byte[] */
+ public static void writeInt (int v, final byte[] b, final int off) {
+ b [ off ] = (byte) v; v >>>= 8;
+ b [ off + 1 ] = (byte) v; v >>>= 8;
+ b [ off + 2 ] = (byte) v; v >>>= 8;
+ b [ off + 3 ] = (byte) v;
+ }
+ }
+ }
+ // ---------------------------------------------------------------------
+ // digest parameter (block)
+ // ---------------------------------------------------------------------
+ /** Blake2b configuration parameters block per spec */
+ // REVU: need to review a revert back to non-lazy impl TODO: do & bench
+ public static class Param implements AlgorithmParameterSpec {
+ interface Xoff {
+ int digest_length = 0;
+ int key_length = 1;
+ int fanout = 2;
+ int depth = 3;
+ int leaf_length = 4;
+ int node_offset = 8;
+ int node_depth = 16;
+ int inner_length = 17;
+ int reserved = 18;
+ int salt = 32;
+ int personal = 48;
+ }
+ public interface Default {
+ byte digest_length = Spec.max_digest_bytes;
+ byte key_length = 0;
+ byte fanout = 1;
+ byte depth = 1;
+ int leaf_length = 0;
+ long node_offset = 0;
+ byte node_depth = 0;
+ byte inner_length = 0;
+ }
+ /** default bytes of Blake2b parameter block */
+ final static byte[] default_bytes = new byte[ Spec.param_bytes ];
+ /** initialize default_bytes */
+ static {
+ default_bytes [ Xoff.digest_length ] = Default.digest_length;
+ default_bytes [ Xoff.key_length ] = Default.key_length;
+ default_bytes [ Xoff.fanout ] = Default.fanout;
+ default_bytes [ Xoff.depth ] = Default.depth;
+ /* def. leaf_length is 0 fill and already set by new byte[] */
+ /* def. node_offset is 0 fill and already set by new byte[] */
+ default_bytes [ Xoff.node_depth ] = Default.node_depth;
+ default_bytes [ Xoff.inner_length] = Default.inner_length;
+ /* def. salt is 0 fill and already set by new byte[] */
+ /* def. personal is 0 fill and already set by new byte[] */
+ }
+ /** default Blake2b h vector */
+ final static long[] default_h = new long [ Spec.state_space_len ];
+ static {
+ default_h [0] = readLong( default_bytes, 0 );
+ default_h [1] = readLong( default_bytes, 8 );
+ default_h [2] = readLong( default_bytes, 16 );
+ default_h [3] = readLong( default_bytes, 24 );
+ default_h [4] = readLong( default_bytes, 32 );
+ default_h [5] = readLong( default_bytes, 40 );
+ default_h [6] = readLong( default_bytes, 48 );
+ default_h [7] = readLong( default_bytes, 56 );
+
+ default_h [0] ^= Spec.IV [0];
+ default_h [1] ^= Spec.IV [1];
+ default_h [2] ^= Spec.IV [2];
+ default_h [3] ^= Spec.IV [3];
+ default_h [4] ^= Spec.IV [4];
+ default_h [5] ^= Spec.IV [5];
+ default_h [6] ^= Spec.IV [6];
+ default_h [7] ^= Spec.IV [7];
+ }
+
+ /** */
+ private boolean hasKey = false;
+ /** not sure how to make this secure - TODO */
+ private byte[] key_bytes = null;
+ /** */
+ private byte[] bytes = null;
+ /** */
+ private final long[] h = new long [ Spec.state_space_len ];
+
+ /** */
+ public Param() {
+ System.arraycopy( default_h, 0, h, 0, Spec.state_space_len );
+ }
+ /** */
+ public long[] initialized_H () {
+ return h;
+ }
+ /** package only - copy returned - do not use in functional loops */
+ public byte[] getBytes() {
+ lazyInitBytes();
+ byte[] copy = new byte[ bytes.length ];
+ System.arraycopy( bytes, 0, copy, 0, bytes.length );
+ return copy;
+ }
+
+ final byte getByteParam (final int xoffset) {
+ byte[] _bytes = bytes;
+ if(_bytes == null) _bytes = Param.default_bytes;
+ return _bytes[ xoffset];
+ }
+ final int getIntParam (final int xoffset) {
+ byte[] _bytes = bytes;
+ if(_bytes == null) _bytes = Param.default_bytes;
+ return readInt ( _bytes, xoffset);
+ }
+ final long getLongParam (final int xoffset) {
+ byte[] _bytes = bytes;
+ if(_bytes == null) _bytes = Param.default_bytes;
+ return readLong ( _bytes, xoffset);
+ }
+ // TODO same for tree params depth, fanout, inner, node-depth, node-offset
+ public final int getDigestLength() {
+ return (int) getByteParam ( Xoff.digest_length );
+ }
+ public final int getKeyLength() {
+ return (int) getByteParam ( Xoff.key_length );
+ }
+ public final int getFanout() {
+ return (int) getByteParam ( Xoff.fanout );
+ }
+ public final int getDepth() {
+ return (int) getByteParam ( Xoff.depth );
+ }
+ public final int getLeafLength() {
+ return getIntParam ( Xoff.leaf_length );
+ }
+ public final long getNodeOffset() {
+ return getLongParam ( Xoff.node_offset );
+ }
+ public final int getNodeDepth() {
+ return (int) getByteParam ( Xoff.node_depth );
+ }
+ public final int getInnerLength() {
+ return (int) getByteParam ( Xoff.inner_length );
+ }
+
+ public final boolean hasKey() { return this.hasKey; }
+
+ @Override public Param clone() {
+ final Param clone = new Param();
+ System.arraycopy(this.h, 0, clone.h, 0, h.length);
+ clone.lazyInitBytes();
+ System.arraycopy(this.bytes, 0, clone.bytes, 0, this.bytes.length);
+
+ if(this.hasKey){
+ clone.hasKey = this.hasKey;
+ clone.key_bytes = new byte [Spec.max_key_bytes * 2];
+ System.arraycopy(this.key_bytes, 0, clone.key_bytes, 0, this.key_bytes.length);
+ }
+ return clone;
+ }
+ ////////////////////////////////////////////////////////////////////////
+ /// lazy setters - write directly to the bytes image of param block ////
+ ////////////////////////////////////////////////////////////////////////
+ final void lazyInitBytes () {
+ if( bytes == null ) {
+ bytes = new byte [ Spec.param_bytes ];
+ System.arraycopy(Param.default_bytes, 0, bytes, 0, Spec.param_bytes);
+ }
+ }
+ /* 0-7 inclusive */
+ public final Param setDigestLength(int len) {
+ assert len > 0 : assertFail("len", len, exclusiveLowerBound, 0);
+ assert len <= Spec.max_digest_bytes : assertFail("len", len, inclusiveUpperBound, Spec.max_digest_bytes);
+
+ lazyInitBytes();
+ bytes[ Xoff.digest_length ] = (byte) len;
+ h[ 0 ] = readLong( bytes, 0 );
+ h[ 0 ] ^= Spec.IV [ 0 ];
+ return this;
+ }
+ public final Param setKey (final Key key) {
+ assert key != null : "key is null";
+ final byte[] keybytes = key.getEncoded();
+ assert keybytes != null : "key.encoded() is null";
+
+ return this.setKey(keybytes);
+ }
+ public final Param setKey (final byte[] key) {
+ assert key != null : "key is null";
+ assert key.length >= 0 : assertFail("key.length", key.length, inclusiveUpperBound, 0);
+ assert key.length <= Spec.max_key_bytes : assertFail("key.length", key.length, inclusiveUpperBound, Spec.max_key_bytes);
+
+ // zeropad keybytes
+ this.key_bytes = new byte [Spec.max_key_bytes * 2];
+ System.arraycopy ( key, 0, this.key_bytes, 0, key.length );
+ lazyInitBytes();
+ bytes[ Xoff.key_length ] = (byte) key.length; // checked c ref; this is correct
+ h[ 0 ] = readLong( bytes, 0 );
+ h[ 0 ] ^= Spec.IV [ 0 ];
+ this.hasKey = true;
+ return this;
+ }
+ public final Param setFanout(int fanout) {
+ assert fanout > 0 : assertFail("fanout", fanout, exclusiveLowerBound, 0);
+
+ lazyInitBytes();
+ bytes[ Xoff.fanout ] = (byte) fanout;
+ h[ 0 ] = readLong( bytes, 0 );
+ h[ 0 ] ^= Spec.IV [ 0 ];
+ return this;
+ }
+ public final Param setDepth(int depth) {
+ assert depth > 0 : assertFail("depth", depth, exclusiveLowerBound, 0);
+
+ lazyInitBytes();
+ bytes[ Xoff.depth ] = (byte) depth;
+ h[ 0 ] = readLong( bytes, 0 );
+ h[ 0 ] ^= Spec.IV [ 0 ];
+ return this;
+ }
+ public final Param setLeafLength(int leaf_length) {
+ assert leaf_length >= 0 : assertFail("leaf_length", leaf_length, inclusiveLowerBound, 0);
+
+ lazyInitBytes();
+ writeInt (leaf_length, bytes, Xoff.leaf_length);
+ h[ 0 ] = readLong( bytes, 0 );
+ h[ 0 ] ^= Spec.IV [ 0 ];
+ return this;
+ }
+
+ /* 8-15 inclusive */
+ public final Param setNodeOffset(long node_offset) {
+ assert node_offset >= 0 : assertFail("node_offset", node_offset, inclusiveLowerBound, 0);
+
+ lazyInitBytes();
+ writeLong(node_offset, bytes, Xoff.node_offset);
+ h[ 1 ] = readLong( bytes, Xoff.node_offset );
+ h[ 1 ] ^= Spec.IV [ 1 ];
+ return this;
+ }
+
+ /* 16-23 inclusive */
+ public final Param setNodeDepth(int node_depth) {
+ assert node_depth >= 0 : assertFail("node_depth", node_depth, inclusiveLowerBound, 0);
+
+ lazyInitBytes();
+ bytes[ Xoff.node_depth ] = (byte) node_depth;
+ h[ 2 ] = readLong( bytes, Xoff.node_depth );
+ h[ 2 ] ^= Spec.IV [ 2 ];
+ h[ 3 ] = readLong( bytes, Xoff.node_depth + 8);
+ h[ 3 ] ^= Spec.IV [ 3 ];
+ return this;
+ }
+ public final Param setInnerLength(int inner_length) {
+ assert inner_length >= 0 : assertFail("inner_length", inner_length, inclusiveLowerBound, 0);
+
+ lazyInitBytes();
+ bytes[ Xoff.inner_length] = (byte) inner_length;
+ h[ 2 ] = readLong( bytes, Xoff.node_depth );
+ h[ 2 ] ^= Spec.IV [ 2 ];
+ h[ 3 ] = readLong( bytes, Xoff.node_depth + 8);
+ h[ 3 ] ^= Spec.IV [ 3 ];
+ return this;
+ }
+
+ /* 24-31 masked by reserved and remain unchanged */
+
+ /* 32-47 inclusive */
+ public final Param setSalt(final byte[] salt) {
+ assert salt != null : "salt is null";
+ assert salt.length <= Spec.max_salt_bytes : assertFail("salt.length", salt.length, inclusiveUpperBound, Spec.max_salt_bytes);
+
+ lazyInitBytes();
+ Arrays.fill ( bytes, Xoff.salt, Xoff.salt + Spec.max_salt_bytes, (byte)0);
+ System.arraycopy( salt, 0, bytes, Xoff.salt, salt.length );
+ h[ 4 ] = readLong( bytes, Xoff.salt );
+ h[ 4 ] ^= Spec.IV [ 4 ];
+ h[ 5 ] = readLong( bytes, Xoff.salt + 8 );
+ h[ 5 ] ^= Spec.IV [ 5 ];
+ return this;
+ }
+
+ /* 48-63 inclusive */
+ public final Param setPersonal(byte[] personal) {
+ assert personal != null : "personal is null";
+ assert personal.length <= Spec.max_personalization_bytes : assertFail("personal.length", personal.length, inclusiveUpperBound, Spec.max_personalization_bytes);
+
+ lazyInitBytes();
+ Arrays.fill ( bytes, Xoff.personal, Xoff.personal + Spec.max_personalization_bytes, (byte)0);
+ System.arraycopy( personal, 0, bytes, Xoff.personal, personal.length );
+ h[ 6 ] = readLong( bytes, Xoff.personal );
+ h[ 6 ] ^= Spec.IV [ 6 ];
+ h[ 7 ] = readLong( bytes, Xoff.personal + 8 );
+ h[ 7 ] ^= Spec.IV [ 7 ];
+ return this;
+ }
+ ////////////////////////////////////////////////////////////////////////
+ /// lazy setters /////////////////////////////////////////////////// END
+ ////////////////////////////////////////////////////////////////////////
+ }
+}
diff --git a/app/src/main/java/com/tangem/domain/wallet/cardano/CardanoData.java b/app/src/main/java/com/tangem/domain/wallet/cardano/CardanoData.java
new file mode 100644
index 0000000000..afe9f7b101
--- /dev/null
+++ b/app/src/main/java/com/tangem/domain/wallet/cardano/CardanoData.java
@@ -0,0 +1,122 @@
+package com.tangem.domain.wallet.cardano;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.tangem.domain.wallet.CoinData;
+import com.tangem.domain.wallet.CoinEngine;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CardanoData extends CoinData {
+ public CardanoData() {
+ super();
+ }
+
+ private Long balance;
+
+ public String getUnspentInputsDescription() {
+ try {
+// int gatheredUnspents = 0;
+ if( unspentOutputs ==null ) return "";
+// for (int i = 0; i < unspentOutputs.size(); i++) {
+// if (unspentOutputs.get(i).Raw != null && unspentOutputs.get(i).Raw.length() > 1) gatheredUnspents++;
+// }
+ return String.valueOf(unspentOutputs.size()) + " unspents";// (" + String.valueOf(gatheredUnspents) + " received)";
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ return "";
+ }
+ }
+
+ public static class UnspentOutput {
+ public String txID;
+ public Long Amount;
+ public Integer Index;
+
+ public Bundle getAsBundle() {
+ Bundle B = new Bundle();
+ B.putString("txID", txID);
+ B.putLong("Amount", Amount);
+ B.putInt("Index", Index);
+ return B;
+ }
+
+ public void loadFromBundle(Bundle B) {
+ txID = B.getString("txID");
+ Amount = B.getLong("Amount");
+ Index = B.getInt("Index");
+ }
+ }
+
+
+ private List unspentOutputs = null;
+
+ public List getUnspentOutputs() {
+ if (unspentOutputs == null) unspentOutputs = new ArrayList<>();
+ return unspentOutputs;
+ }
+
+ @Override
+ public void loadFromBundle(Bundle B) {
+ super.loadFromBundle(B);
+
+ if (B.containsKey("Balance")) balance = B.getLong("Balance");
+ else balance = null;
+ if (B.containsKey("UnspentTransactions")) {
+ unspentOutputs = new ArrayList<>();
+ Bundle BB = B.getBundle("UnspentTransactions");
+ Integer i = 0;
+ while (BB.containsKey(i.toString())) {
+ UnspentOutput t = new UnspentOutput();
+ t.loadFromBundle(BB.getBundle(i.toString()));
+ unspentOutputs.add(t);
+ i++;
+ }
+ }
+ }
+
+ @Override
+ public void saveToBundle(Bundle B) {
+ super.saveToBundle(B);
+ try {
+ if (unspentOutputs != null) {
+ Bundle BB = new Bundle();
+ for (Integer i = 0; i < unspentOutputs.size(); i++) {
+ BB.putBundle(i.toString(), unspentOutputs.get(i).getAsBundle());
+ }
+ B.putBundle("UnspentTransactions", BB);
+ }
+ if (balance != null) B.putLong("Balance", balance);
+ } catch (Exception e) {
+ Log.e("Can't save to bundle ", e.getMessage());
+ }
+ }
+
+ @Override
+ public void clearInfo() {
+ super.clearInfo();
+ balance = null;
+ unspentOutputs = null;
+ }
+
+ public CoinEngine.InternalAmount getBalanceInInternalUnits() {
+ return new CoinEngine.InternalAmount(BigDecimal.valueOf(balance),"Lovelace");
+ }
+
+ public void setBalance(Long balance) {
+ this.balance = balance;
+ }
+
+ public Long getBalance() {
+ return balance;
+ }
+
+ public boolean hasBalanceInfo() {
+ return balance != null;
+ }
+}
diff --git a/app/src/main/java/com/tangem/domain/wallet/cardano/CardanoEngine.java b/app/src/main/java/com/tangem/domain/wallet/cardano/CardanoEngine.java
new file mode 100644
index 0000000000..8561f4c0ff
--- /dev/null
+++ b/app/src/main/java/com/tangem/domain/wallet/cardano/CardanoEngine.java
@@ -0,0 +1,759 @@
+package com.tangem.domain.wallet.cardano;
+
+import android.net.Uri;
+import android.text.InputFilter;
+import android.util.Base64;
+import android.util.Log;
+
+import com.tangem.data.network.ServerApiAdalite;
+import com.tangem.data.network.model.AdaliteResponse;
+import com.tangem.data.network.model.AdaliteResponseUtxo;
+import com.tangem.data.network.model.UtxoData;
+import com.tangem.domain.wallet.BTCUtils;
+import com.tangem.domain.wallet.BalanceValidator;
+import com.tangem.domain.wallet.Base58;
+import com.tangem.domain.wallet.CoinData;
+import com.tangem.domain.wallet.CoinEngine;
+import com.tangem.domain.wallet.TangemContext;
+import com.tangem.tangemcommon.data.TangemCard;
+import com.tangem.tangemcommon.reader.CardProtocol;
+import com.tangem.tangemcommon.tasks.SignTask;
+import com.tangem.tangemcommon.util.Util;
+import com.tangem.util.DecimalDigitsInputFilter;
+import com.tangem.wallet.R;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.util.DigestFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+import java.util.zip.CRC32;
+
+import co.nstant.in.cbor.CborBuilder;
+import co.nstant.in.cbor.CborDecoder;
+import co.nstant.in.cbor.CborEncoder;
+import co.nstant.in.cbor.CborException;
+import co.nstant.in.cbor.builder.ArrayBuilder;
+import co.nstant.in.cbor.model.Array;
+import co.nstant.in.cbor.model.ByteString;
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.UnsignedInteger;
+
+import static com.tangem.domain.wallet.Base58.decodeBase58;
+import static com.tangem.domain.wallet.Base58.encodeBase58;
+
+public class CardanoEngine extends CoinEngine {
+
+ private static final String TAG = CardanoEngine.class.getSimpleName();
+
+ private static final long protocolMagic = 764824073;
+
+ public CardanoData coinData = null;
+
+ public CardanoEngine(TangemContext context) throws Exception {
+ super(context);
+ if (context.getCoinData() == null) {
+ coinData = new CardanoData();
+ context.setCoinData(coinData);
+ } else if (context.getCoinData() instanceof CardanoData) {
+ coinData = (CardanoData) context.getCoinData();
+ } else {
+ throw new Exception("Invalid type of Blockchain data for CardanoEngine");
+ }
+ }
+
+ public CardanoEngine() {
+ super();
+ }
+
+ private static int getDecimals() {
+ return 6;
+ }
+
+
+ private void checkBlockchainDataExists() throws Exception {
+ if (coinData == null) throw new Exception("No blockchain data");
+ }
+
+ @Override
+ public boolean awaitingConfirmation() {
+ return false;
+ }
+
+ @Override
+ public String getBalanceHTML() {
+ Amount balance = getBalance();
+ if (balance != null) {
+ return balance.toDescriptionString(getDecimals());
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ public String getBalanceCurrency() {
+ return "ADA";
+ }
+
+ @Override
+ public String getOfflineBalanceHTML() {
+ InternalAmount offlineInternalAmount = convertToInternalAmount(ctx.getCard().getOfflineBalance());
+ Amount offlineAmount = convertToAmount(offlineInternalAmount);
+ return offlineAmount.toDescriptionString(getDecimals());
+ }
+
+ @Override
+ public boolean isBalanceNotZero() {
+ if (coinData == null) return false;
+ if (coinData.getBalanceInInternalUnits() == null) return false;
+ return coinData.getBalanceInInternalUnits().notZero();
+ }
+
+ @Override
+ public boolean hasBalanceInfo() {
+ if (coinData == null) return false;
+ return coinData.hasBalanceInfo();
+ }
+
+ @Override
+ public boolean isExtractPossible() {
+ if (!hasBalanceInfo()) {
+ ctx.setMessage(R.string.cannot_obtain_data_from_blockchain);
+ } else if (!isBalanceNotZero()) {
+ ctx.setMessage(R.string.wallet_empty);
+ } else if (awaitingConfirmation()) {
+ ctx.setMessage(R.string.please_wait_while_previous);
+ } else if (coinData.getUnspentOutputs().size() == 0) {
+ ctx.setMessage(R.string.please_wait_for_confirmation);
+ } else {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String getFeeCurrency() {
+ return "ADA";
+ }
+
+ @Override
+ public boolean validateAddress(String address) {
+ if (address == null || address.isEmpty()) {
+ return false;
+ }
+
+ byte[] decAddress = Base58.decodeBase58(address);
+
+ if (decAddress == null || decAddress.length == 0) {
+ return false;
+ }
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(decAddress);
+ try {
+ List addressList = ((Array) new CborDecoder(bais).decode().get(0)).getDataItems();
+ byte[] addressBytes = ((ByteString) addressList.get(0)).getBytes();
+ long addressChecksum = ((UnsignedInteger) addressList.get(1)).getValue().longValue();
+
+ final CRC32 crc32 = new CRC32();
+ crc32.update(addressBytes);
+ long calculatedChecksum = crc32.getValue();
+
+ if (addressChecksum != calculatedChecksum) {
+ return false;
+ }
+
+ } catch (Exception e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean isNeedCheckNode() {
+ return true;
+ }
+
+ @Override
+ public Uri getWalletExplorerUri() {
+ return Uri.parse("https://cardanoexplorer.com/address/" + ctx.getCoinData().getWallet());
+ }
+
+ @Override
+ public Uri getShareWalletUri() {
+ return Uri.parse(ctx.getCoinData().getWallet());
+ }
+
+ @Override
+ public InputFilter[] getAmountInputFilters() {
+ return new InputFilter[]{new DecimalDigitsInputFilter(getDecimals())};
+ }
+
+ @Override
+ public boolean checkNewTransactionAmount(Amount amount) {
+ if (coinData == null) return false;
+ if (amount.compareTo(convertToAmount(coinData.getBalanceInInternalUnits())) > 0) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean checkNewTransactionAmountAndFee(Amount amountValue, Amount feeValue, Boolean isIncludeFee) {
+ InternalAmount fee;
+ InternalAmount amount;
+
+ try {
+ checkBlockchainDataExists();
+ amount = convertToInternalAmount(amountValue);
+ fee = convertToInternalAmount(feeValue);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ if (fee == null || amount == null)
+ return false;
+
+ if (fee.isZero() || amount.isZero())
+ return false;
+
+ if (isIncludeFee && (amount.compareTo(coinData.getBalanceInInternalUnits()) > 0 || amount.compareTo(fee) < 0))
+ return false;
+
+ if (!isIncludeFee && amount.add(fee).compareTo(coinData.getBalanceInInternalUnits()) > 0)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public boolean validateBalance(BalanceValidator balanceValidator) {
+ try {
+ if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) {
+ balanceValidator.setScore(0);
+ balanceValidator.setFirstLine("Unknown balance");
+ balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
+ return false;
+ }
+
+ if (coinData.isBalanceReceived()) {// && coinData.isBalanceEqual()) { TODO:check
+ balanceValidator.setScore(100);
+ balanceValidator.setFirstLine("Verified balance");
+ balanceValidator.setSecondLine("Balance confirmed in blockchain");
+ if (coinData.getBalanceInInternalUnits().isZero()) {
+ balanceValidator.setFirstLine("Empty wallet");
+ balanceValidator.setSecondLine("");
+ }
+ }
+
+ if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
+ balanceValidator.setScore(80);
+ balanceValidator.setFirstLine("Verified offline balance");
+ balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. ");
+ }
+
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @Override
+ public Amount getBalance() {
+ if (!hasBalanceInfo()) return null;
+ return convertToAmount(coinData.getBalanceInInternalUnits());
+ }
+
+ @Override
+ public String evaluateFeeEquivalent(String fee) {
+ if (!coinData.getAmountEquivalentDescriptionAvailable()) return "";
+ try {
+ Amount feeAmount = new Amount(fee, getFeeCurrency());
+ return feeAmount.toEquivalentString(coinData.getRate());
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ @Override
+ public String getBalanceEquivalent() {
+ if (coinData == null || !coinData.getAmountEquivalentDescriptionAvailable()) return "";
+ Amount balance = getBalance();
+ if (balance == null) return "";
+ return balance.toEquivalentString(coinData.getRate());
+ }
+
+ @Override
+ public String calculateAddress(byte[] pkUncompressed) throws CborException, IOException {
+
+ ByteArrayOutputStream exBaos = new ByteArrayOutputStream();
+ exBaos.write(pkUncompressed);
+ exBaos.write(BTCUtils.fromHex("0000000000000000000000000000000000000000000000000000000000000000"));
+ byte[] pkExtended = exBaos.toByteArray();
+
+ exBaos.reset();
+ new CborEncoder(exBaos).encode(new CborBuilder()
+ .addArray()
+ .add(0)
+ .addArray()
+ .add(0)
+ .add(pkExtended)
+ .end()
+ .addMap()
+ .end()
+ .end()
+ .build());
+ byte[] forSha3 = exBaos.toByteArray();
+
+ Digest sha3 = DigestFactory.createSHA3_256();
+ sha3.update(forSha3, 0, forSha3.length);
+ byte[] forBlake = new byte[32];
+ sha3.doFinal(forBlake, 0);
+
+ final Blake2b blake2b = Blake2b.Digest.newInstance(28);
+ byte[] pkHash = blake2b.digest(forBlake);
+
+ //pkHash + attributes
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new CborEncoder(baos).encode(new CborBuilder()
+ .addArray()
+ .add(pkHash)
+ .addMap()//additional attributes
+ .end()
+ .add(0)//address type
+ .end()
+ .build());
+ byte[] addr = baos.toByteArray();
+
+ final CRC32 crc32 = new CRC32();
+ crc32.update(addr);
+ long checksum = crc32.getValue();
+
+ DataItem addrItem = new CborBuilder().add(addr).build().get(0);
+ addrItem.setTag(24);
+
+ //addr + checksum
+ baos.reset();
+ new CborEncoder(baos).encode(new CborBuilder()
+ .addArray()
+ .add(addrItem)
+ .add(checksum)
+ .end()
+ .build());
+
+ byte[] hexAddress = baos.toByteArray();
+ return encodeBase58(hexAddress);
+ }
+
+ @Override
+ public Amount convertToAmount(InternalAmount internalAmount) {
+ BigDecimal d = internalAmount.divide(new BigDecimal("1000000"));
+ return new Amount(d, getBalanceCurrency());
+ }
+
+ @Override
+ public Amount convertToAmount(String strAmount, String currency) {
+ return new Amount(strAmount, currency);
+ }
+
+ @Override
+ public InternalAmount convertToInternalAmount(Amount amount) {
+ BigDecimal d = amount.multiply(new BigDecimal("1000000"));
+ return new InternalAmount(d, "Lovelace");
+ }
+
+ @Override
+ public InternalAmount convertToInternalAmount(byte[] bytes) {
+ if (bytes == null) return null;
+ byte[] reversed = new byte[bytes.length];
+ for (int i = 0; i < bytes.length; i++) reversed[i] = bytes[bytes.length - i - 1];
+ return new InternalAmount(Util.byteArrayToLong(reversed), "Lovelace");
+ }
+
+ @Override
+ public byte[] convertToByteArray(InternalAmount internalAmount) {
+ byte[] bytes = Util.longToByteArray(internalAmount.longValueExact());
+// byte[] reversed = new byte[bytes.length]; TODO: check if needed
+// for (int i = 0; i < bytes.length; i++) reversed[i] = bytes[bytes.length - i - 1];
+// return reversed;
+ return bytes;
+ }
+
+ @Override
+ public CoinData createCoinData() {
+ return new CardanoData();
+ }
+
+ @Override
+ public String getUnspentInputsDescription() {
+ return coinData.getUnspentInputsDescription();
+ }
+
+ @Override
+ public SignTask.TransactionToSign constructTransaction(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception {
+ checkBlockchainDataExists();
+
+ String myAddress = ctx.getCoinData().getWallet();
+ byte[] pbKey = ctx.getCard().getWalletPublicKey();
+ List utxoList = coinData.getUnspentOutputs();
+ long fullAmount = coinData.getBalance();
+
+// long fullAmount = 0; TODO: check
+// for (int i = 0; i < unspentOutputs.size(); ++i) {
+// fullAmount += unspentOutputs.get(i).value;
+// }
+
+ long fees = convertToInternalAmount(feeValue).longValueExact();
+ long amount = convertToInternalAmount(amountValue).longValueExact();
+ long change = fullAmount - amount;
+ if (IncFee) {
+ amount = amount - fees;
+ } else {
+ change = change - fees;
+ }
+
+ final long amountFinal = amount;
+ final long changeFinal = change;
+
+ if (amount + fees > fullAmount) {
+ throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount));
+ }
+
+ CborBuilder cborBuilder = new CborBuilder();
+ ArrayBuilder txArray = cborBuilder.addArray();
+ ArrayBuilder> inputsArray = txArray.startArray();
+ ArrayBuilder> outputsArray = txArray.startArray();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ //Inputs
+ for (CardanoData.UnspentOutput utxo : utxoList) {
+ baos.reset();
+
+ //txID + inputPos
+ new CborEncoder(baos).encode(new CborBuilder()
+ .addArray()
+ .add(Util.fromHexString(utxo.txID))
+ .add(utxo.Index)
+ .end()
+ .build());
+ byte[] input = baos.toByteArray();
+
+ DataItem inputItem = new CborBuilder().add(input).build().get(0);
+ inputItem.setTag(24);
+
+ //input type + input
+ inputsArray
+ .addArray()
+ .add(0)
+ .add(inputItem)
+ .end();
+ }
+
+ //1st output
+ DataItem targetAddressItem = new CborDecoder(new ByteArrayInputStream(decodeBase58(targetAddress))).decode().get(0);
+ outputsArray
+ .addArray()
+ .add(targetAddressItem)
+ .add(amountFinal)
+ .end();
+ //2nd output (optional)
+ if (changeFinal > 0) {
+ DataItem myAddressItem = new CborDecoder(new ByteArrayInputStream(decodeBase58(myAddress))).decode().get(0);
+ outputsArray
+ .addArray()
+ .add(myAddressItem)
+ .add(changeFinal)
+ .end();
+ }
+
+ inputsArray.end();
+ outputsArray.end();
+
+ txArray.addMap().end();
+
+ baos.reset();
+ new CborEncoder(baos).encode(cborBuilder.build());
+ byte[] txBody = baos.toByteArray();
+
+ final Blake2b blake2b = Blake2b.Digest.newInstance(32);//28?
+ byte[] txHash = blake2b.digest(txBody);
+
+ baos.reset();
+ new CborEncoder(baos).encode(new CborBuilder().add(protocolMagic).build());
+ byte[] magic = baos.toByteArray();
+
+ //dataToSign prefix
+ baos.reset();
+ baos.write(new byte[]{(byte) 0x01});
+ baos.write(magic);
+ baos.write(new byte[]{(byte) 0x58, (byte) 0x20});
+
+ baos.write(txHash);
+ byte[] dataToSign = baos.toByteArray();
+
+
+ return new SignTask.TransactionToSign() {
+
+ @Override
+ public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) {
+ return signingMethod == TangemCard.SigningMethod.Sign_Hash || signingMethod == TangemCard.SigningMethod.Sign_Raw;
+ }
+
+ @Override
+ public byte[][] getHashesToSign() throws Exception {
+ byte[][] dataForSign = new byte[1][];
+ dataForSign[0] = dataToSign;
+ return dataForSign;
+ }
+
+ @Override
+ public byte[] getRawDataToSign() throws Exception {
+ throw new Exception("Signing Raw Data is not supported for Cardano");
+ }
+
+ @Override
+ public String getHashAlgToSign() {
+ return "sha-256x2";
+ }
+
+ @Override
+ public byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception {
+ throw new Exception("Issuer validation not supported!");
+ }
+
+ @Override
+ public byte[] onSignCompleted(byte[] signFromCard) throws Exception {
+ ByteArrayOutputStream exBaos = new ByteArrayOutputStream();
+ exBaos.write(ctx.getCard().getWalletPublicKey());
+ exBaos.write(BTCUtils.fromHex("0000000000000000000000000000000000000000000000000000000000000000"));
+ byte[] pkExtended = exBaos.toByteArray();
+
+ //pubkey + signature
+ baos.reset();
+ new CborEncoder(baos).encode(new CborBuilder()
+ .addArray()
+ .add(pkExtended)
+ .add(signFromCard)
+ .end()
+ .build());
+ byte[] witnessBody = baos.toByteArray();
+ DataItem witnessBodyItem = new CborBuilder().add(witnessBody).build().get(0);
+ witnessBodyItem.setTag(24);
+
+ //array of witnesses
+ CborBuilder witnessBuilder = new CborBuilder();
+ ArrayBuilder witnessArrayBuilder = witnessBuilder.addArray();
+
+ //witness type + witness body
+ for (CardanoData.UnspentOutput utxo : coinData.getUnspentOutputs()) {
+ witnessArrayBuilder
+ .addArray()
+ .add(0)
+ .add(witnessBodyItem)
+ .end();
+ }
+
+ baos.reset();
+ new CborEncoder(baos).encode(witnessBuilder.build());
+ byte[] witness = baos.toByteArray();
+
+ baos.reset();
+ baos.write(new byte[]{(byte) 0x82});
+ baos.write(txBody);
+ baos.write(witness);
+// new CborEncoder(baos).encode(new CborBuilder()
+// .addArray()
+// .add(txBody)
+// .add(witness)
+// .end()
+// .build());
+ byte[] txForSend = baos.toByteArray();
+ notifyOnNeedSendTransaction(txForSend);
+ //String hex = BTCUtils.toHex(txForSend);
+ return txForSend;
+ }
+ };
+ }
+
+ private int calculateEstimatedTransactionSize(String targetAddress, Amount amount) {
+ Amount feeDummy = new Amount(new BigDecimal(0.2).setScale(getDecimals(), RoundingMode.DOWN), getFeeCurrency());
+ try {
+ OnNeedSendTransaction onNeedSendTransactionBackup = onNeedSendTransaction;
+ onNeedSendTransaction =(tx)->{}; // empty function to bypass exception
+
+ SignTask.TransactionToSign ttsDummy = constructTransaction(amount, feeDummy, true, targetAddress);
+ byte[] txForSendDummy = ttsDummy.onSignCompleted(new byte[64]);
+
+ onNeedSendTransaction = onNeedSendTransactionBackup;
+ Log.e(TAG, "txForSendDummy.length=" + String.valueOf(txForSendDummy.length));
+ return txForSendDummy.length;
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, "Can't calculate transaction size -> use default!");
+ return 1000;
+ }
+ }
+
+ @Override
+ public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) {
+ final ServerApiAdalite serverApiAdalite = new ServerApiAdalite();
+
+ ServerApiAdalite.ResponseListener adaliteListener = new ServerApiAdalite.ResponseListener() {
+ @Override
+ public void onSuccess(String method, AdaliteResponse adaliteResponse) {
+ Log.i(TAG, "onSuccess: " + method);
+ try {
+ String walletAddress = adaliteResponse.getRight().getCaAddress();
+ if (!walletAddress.equals(coinData.getWallet())) {
+ // todo - check
+ throw new Exception("Invalid wallet address in answer!");
+ }
+ coinData.setBalanceReceived(true);
+ coinData.setBalance(adaliteResponse.getRight().getCaBalance().getGetCoin());
+ coinData.setValidationNodeDescription(ServerApiAdalite.lastNode);
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, "FAIL ADALITE_ADDRESS Exception");
+ }
+
+ if (serverApiAdalite.isRequestsSequenceCompleted()) {
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());
+ } else {
+ blockchainRequestsCallbacks.onProgress();
+ }
+ }
+
+
+ @Override
+ public void onSuccess(String method, AdaliteResponseUtxo adaliteResponseUtxo) {
+ Log.i(TAG, "onSuccess: " + method);
+ try {
+ coinData.getUnspentOutputs().clear();
+ for (UtxoData utxo : adaliteResponseUtxo.getRight()) {
+ CardanoData.UnspentOutput unspentOutput = new CardanoData.UnspentOutput();
+ unspentOutput.txID = utxo.getCuId();
+ unspentOutput.Amount = utxo.getCuCoins().getGetCoin();
+ unspentOutput.Index = utxo.getCuOutIndex();
+ coinData.getUnspentOutputs().add(unspentOutput);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ if (serverApiAdalite.isRequestsSequenceCompleted()) {
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());
+ } else {
+ blockchainRequestsCallbacks.onProgress();
+ Log.e(TAG, "FAIL INSIGHT_UNSPENT_OUTPUTS Exception");
+ }
+ }
+
+ @Override
+ public void onSuccess(String method, List listResponse) {
+ Log.e(TAG, "Wrong response type for requestBalanceAndUnspentTransactions");
+ ctx.setError("Wrong response type for requestBalanceAndUnspentTransactions");
+ }
+
+ @Override
+ public void onFail(String method, String message) {
+ Log.i(TAG, "onFail: " + method + " " + message);
+ ctx.setError(message);
+ if (serverApiAdalite.isRequestsSequenceCompleted()) {
+ blockchainRequestsCallbacks.onComplete(false);
+ } else {
+ blockchainRequestsCallbacks.onProgress();
+ }
+ }
+ };
+
+ serverApiAdalite.setResponseListener(adaliteListener);
+
+ serverApiAdalite.requestData(ServerApiAdalite.ADALITE_ADDRESS, ctx.getCoinData().getWallet(), "");
+ serverApiAdalite.requestData(ServerApiAdalite.ADALITE_UNSPENT_OUTPUTS, ctx.getCoinData().getWallet(), "");
+ }
+
+ @Override
+ public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) {
+ final double A = 0.155381;
+ final double B = 0.000043946;
+ int calcSize = calculateEstimatedTransactionSize(targetAddress, amount);
+
+ double fee = A + B * calcSize;
+ Amount feeAmount = new Amount(new BigDecimal(fee).setScale(getDecimals(), RoundingMode.UP), getFeeCurrency());
+
+ coinData.minFee = feeAmount;
+ coinData.normalFee = feeAmount;
+ coinData.maxFee = feeAmount;
+
+ blockchainRequestsCallbacks.onComplete(true);
+ }
+
+ @Override
+ public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) {
+ final ServerApiAdalite serverApiAdalite = new ServerApiAdalite();
+ final String txStr = Base64.encodeToString(txForSend, Base64.NO_WRAP);
+ // BTCUtils.toHex(txForSend);
+
+ final ServerApiAdalite.ResponseListener responseListener = new ServerApiAdalite.ResponseListener() {
+ @Override
+ public void onSuccess(String method, AdaliteResponse adaliteResponse) {
+ Log.e(TAG, "Wrong response type for requestSendTransaction");
+ ctx.setError("Wrong response type for requestSendTransaction");
+ }
+
+ @Override
+ public void onSuccess(String method, AdaliteResponseUtxo adaliteResponseUtxo) {
+ Log.e(TAG, "Wrong response type for requestSendTransaction");
+ ctx.setError("Wrong response type for requestSendTransaction");
+ }
+
+ @Override
+ public void onSuccess(String method, List listResponse) {
+ if (method.equals(ServerApiAdalite.ADALITE_SEND)) {
+ String resultString = listResponse.toString();
+ try {
+ if (resultString.isEmpty()) {
+ ctx.setError("No response from node");
+ blockchainRequestsCallbacks.onComplete(false);
+ } else { // TODO: Make check for a valid send response
+ ctx.setError(null);
+ blockchainRequestsCallbacks.onComplete(true);
+ }
+ } catch (Exception e) {
+ if (e.getMessage() != null) {
+ ctx.setError(e.getMessage());
+ blockchainRequestsCallbacks.onComplete(false);
+ } else {
+ ctx.setError(e.getClass().getName());
+ blockchainRequestsCallbacks.onComplete(false);
+ Log.e(TAG, resultString);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFail(String method, String message) {
+ if (!serverApiAdalite.isRequestsSequenceCompleted()) {
+ ctx.setError(message);
+ blockchainRequestsCallbacks.onComplete(false);
+ }
+ }
+ };
+ serverApiAdalite.setResponseListener(responseListener);
+
+ serverApiAdalite.requestData(ServerApiAdalite.ADALITE_SEND, "", txStr);
+ }
+
+ @Override
+ public boolean allowSelectFeeLevel() {
+ return false;
+ }
+}
diff --git a/app/src/main/java/com/tangem/domain/wallet/cardano/CardanoUtils.java b/app/src/main/java/com/tangem/domain/wallet/cardano/CardanoUtils.java
new file mode 100644
index 0000000000..a32e926e0b
--- /dev/null
+++ b/app/src/main/java/com/tangem/domain/wallet/cardano/CardanoUtils.java
@@ -0,0 +1,4 @@
+package com.tangem.domain.wallet.cardano;
+
+public final class CardanoUtils {
+}
diff --git a/app/src/main/java/com/tangem/domain/wallet/ducatus/DucatusEngine.java b/app/src/main/java/com/tangem/domain/wallet/ducatus/DucatusEngine.java
index 192e105830..438643f7db 100644
--- a/app/src/main/java/com/tangem/domain/wallet/ducatus/DucatusEngine.java
+++ b/app/src/main/java/com/tangem/domain/wallet/ducatus/DucatusEngine.java
@@ -253,7 +253,7 @@ public class DucatusEngine extends BtcEngine {
return false;
}
- if (coinData.isBalanceReceived() && coinData.isBalanceEqual()) {
+ if (coinData.isBalanceReceived()) {// && coinData.isBalanceEqual()) { TODO:check
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
diff --git a/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java b/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java
index 0f0086f8d1..7ddebba10a 100644
--- a/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java
+++ b/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java
@@ -321,8 +321,8 @@ public class EthEngine extends CoinEngine {
if (!coinData.getUnconfirmedTXCount().equals(coinData.getConfirmedTXCount())) {
balanceValidator.setScore(0);
- balanceValidator.setFirstLine("Unguaranteed balance");
- balanceValidator.setSecondLine("Transaction is in progress. Wait for confirmation in blockchain.");
+ balanceValidator.setFirstLine("Transaction in progress");
+ balanceValidator.setSecondLine("Wait for confirmation in blockchain");
return false;
}
diff --git a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java
index 96d178234f..ee10a59073 100644
--- a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java
+++ b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java
@@ -394,8 +394,8 @@ public class TokenEngine extends CoinEngine {
if (!coinData.getUnconfirmedTXCount().equals(coinData.getConfirmedTXCount())) {
balanceValidator.setScore(0);
- balanceValidator.setFirstLine("Unguaranteed balance");
- balanceValidator.setSecondLine("Transaction is in progress. Wait for confirmation in blockchain.");
+ balanceValidator.setFirstLine("Transaction in progress");
+ balanceValidator.setSecondLine("Wait for confirmation in blockchain");
return false;
}
diff --git a/app/src/main/java/com/tangem/presentation/activity/ConfirmTransactionActivity.kt b/app/src/main/java/com/tangem/presentation/activity/ConfirmTransactionActivity.kt
index 92815f8889..adede77949 100644
--- a/app/src/main/java/com/tangem/presentation/activity/ConfirmTransactionActivity.kt
+++ b/app/src/main/java/com/tangem/presentation/activity/ConfirmTransactionActivity.kt
@@ -87,9 +87,8 @@ class ConfirmTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallbac
btnSend.visibility = View.INVISIBLE
- val allowFeeLevelSelection = engine.allowSelectFeeLevel()
- for (lol in rgFee.touchables) {
- lol.isEnabled = allowFeeLevelSelection
+ if (!engine.allowSelectFeeLevel()) {
+ rgFee.visibility = View.INVISIBLE
}
// set listeners
diff --git a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt
index 35a0d6eed3..9a25817c4d 100644
--- a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt
+++ b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt
@@ -729,6 +729,7 @@ class LoadedWallet : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback
Blockchain.Litecoin -> "litecoin"
Blockchain.Rootstock -> "bitcoin"
Blockchain.RootstockToken -> "bitcoin"
+ Blockchain.Cardano -> "cardano"
else -> {
throw Exception("Can''t get rate for blockchain " + ctx.blockchainName)
}
diff --git a/app/src/main/res/layout/activity_confirm_transaction.xml b/app/src/main/res/layout/activity_confirm_transaction.xml
index acb261225c..decf945d60 100644
--- a/app/src/main/res/layout/activity_confirm_transaction.xml
+++ b/app/src/main/res/layout/activity_confirm_transaction.xml
@@ -263,7 +263,7 @@
+ app:layout_constraintWidth_min="130dp" />
R.drawable.card_ru030
card.batch == "0021" -> R.drawable.card_ru031
card.batch == "0022" -> R.drawable.card_ru032
+ card.batch == "FF32" -> R.drawable.card_ff32
else -> null
}
diff --git a/tangemserver-android/src/main/res/drawable/card_ff32.png b/tangemserver-android/src/main/res/drawable/card_ff32.png
new file mode 100644
index 0000000000..3e0cd61995
Binary files /dev/null and b/tangemserver-android/src/main/res/drawable/card_ff32.png differ