diff --git a/blockchain/build.gradle b/blockchain/build.gradle index 09ba1f5663..d8e1ebe426 100644 --- a/blockchain/build.gradle +++ b/blockchain/build.gradle @@ -60,6 +60,8 @@ dependencies { implementation "com.github.walleth.kethereum:crypto_api:$kethereum_version" implementation "com.github.walleth.kethereum:model:$kethereum_version" + implementation 'co.nstant.in:cbor:0.8' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2' testImplementation "com.google.truth:truth:1.0" androidTestImplementation 'androidx.test.ext:junit:1.1.1' diff --git a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/network/BitcoinNetworkManager.kt b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/network/BitcoinNetworkManager.kt index 56e9a70ad0..fbea3c1359 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/network/BitcoinNetworkManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/network/BitcoinNetworkManager.kt @@ -90,7 +90,8 @@ class BitcoinNetworkManager(private val isTestNet: Boolean) : BitcoinProvider { data class BitcoinAddressResponse( val balance: Long, val hasUnconfirmed: Boolean, - val unspentTransactions: List) + val unspentTransactions: List +) data class BitcoinFee( val minimalPerKb: BigDecimal, diff --git a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/network/BlockchainInfoProvider.kt b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/network/BlockchainInfoProvider.kt index 18902089d1..a8d1c0f51c 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/network/BlockchainInfoProvider.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/network/BlockchainInfoProvider.kt @@ -23,7 +23,7 @@ class BlockchainInfoProvider( val addressData = addressDeferred.await() val unspents = unspentsDeferred.await() - val unconfinedTransactions = addressData.transactions?.find { it.blockHeight == 0L } != null + val unconfirmedTransactions = addressData.transactions?.find { it.blockHeight == 0L } != null val bitcoinUnspents = unspents.unspentOutputs.map { UnspentTransaction( @@ -36,7 +36,7 @@ class BlockchainInfoProvider( Result.Success( BitcoinAddressResponse( addressData.finalBalance - ?: 0L, unconfinedTransactions, bitcoinUnspents)) + ?: 0L, unconfirmedTransactions, bitcoinUnspents)) } } catch (exception: Exception) { Result.Failure(exception) diff --git a/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoAddress.kt b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoAddress.kt new file mode 100644 index 0000000000..f8b6667362 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoAddress.kt @@ -0,0 +1,106 @@ +package com.tangem.blockchain.cardano + +import co.nstant.`in`.cbor.CborBuilder +import co.nstant.`in`.cbor.CborDecoder +import co.nstant.`in`.cbor.CborEncoder +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 com.tangem.blockchain.cardano.crypto.Blake2b +import com.tangem.blockchain.common.extensions.decodeBase58 +import com.tangem.blockchain.common.extensions.encodeBase58 +import org.spongycastle.crypto.util.DigestFactory +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.util.zip.CRC32 + +class CardanoAddressFactory { + companion object { + fun makeAddress(cardPublicKey: ByteArray, testNet: Boolean = false): String { + val extendedPublicKey = extendPublicKey(cardPublicKey) + + val pubKeyWithAttributesBaos = ByteArrayOutputStream() + CborEncoder(pubKeyWithAttributesBaos).encode(CborBuilder() + .addArray() + .add(0) + .addArray() + .add(0) + .add(extendedPublicKey) + .end() + .addMap() + .end() + .end() + .build()) + val pubKeyWithAttributes = pubKeyWithAttributesBaos.toByteArray() + + val sha3Digest = DigestFactory.createSHA3_256() + sha3Digest.update(pubKeyWithAttributes, 0, pubKeyWithAttributes.size) + val sha3Hash = ByteArray(32) + sha3Digest.doFinal(sha3Hash, 0) + + val blake2b = Blake2b.Digest.newInstance(28) + val blakeHash = blake2b.digest(sha3Hash) + + val hashWithAttributesBaos = ByteArrayOutputStream() + CborEncoder(hashWithAttributesBaos).encode(CborBuilder() + .addArray() + .add(blakeHash) + .addMap() //additional attributes + .end() + .add(0) //address type + .end() + .build()) + val hashWithAttributes = hashWithAttributesBaos.toByteArray() + + val crc32 = CRC32() + crc32.update(hashWithAttributes) + val checksum = crc32.value + + val addressItem = CborBuilder().add(hashWithAttributes).build().get(0) + addressItem.setTag(24) + + //addr + checksum + val addressBaos = ByteArrayOutputStream() + CborEncoder(addressBaos).encode(CborBuilder() + .addArray() + .add(addressItem) + .add(checksum) + .end() + .build()) + + val hexAddress = addressBaos.toByteArray() + return hexAddress.encodeBase58() + } + + fun extendPublicKey(publicKey: ByteArray): ByteArray { + val zeroBytes = ByteArray(32) + zeroBytes.fill(0) + return publicKey + zeroBytes + } + } +} + +class CardanoAddressValidator { + companion object { + fun validate(address: String): Boolean { + val decoded = address.decodeBase58() ?: return false + + return try { + val bais = ByteArrayInputStream(decoded) + val addressList = + (CborDecoder(bais).decode()[0] as Array).dataItems + val addressItemBytes = (addressList[0] as ByteString).bytes + val checksum = (addressList[1] as UnsignedInteger).value.toLong() + + val crc32 = CRC32() + crc32.update(addressItemBytes) + val calculatedChecksum = crc32.value + + checksum == calculatedChecksum + } catch (e: Exception) { + false + } + } + } +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoTransactionBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoTransactionBuilder.kt new file mode 100644 index 0000000000..cbf54fc93d --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoTransactionBuilder.kt @@ -0,0 +1,155 @@ +package com.tangem.blockchain.cardano + +import co.nstant.`in`.cbor.CborBuilder +import co.nstant.`in`.cbor.CborDecoder +import co.nstant.`in`.cbor.CborEncoder +import co.nstant.`in`.cbor.builder.ArrayBuilder +import com.tangem.blockchain.cardano.crypto.Blake2b +import com.tangem.blockchain.common.TransactionData +import com.tangem.blockchain.common.extensions.decodeBase58 +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream + +class CardanoTransactionBuilder() { + var unspentOutputs: List = listOf() + var transactionBody: ByteArray = ByteArray(0) + + fun buildToSign(transactionData: TransactionData): ByteArray { + val transactionBuilder = CborBuilder() + + val transactionArray = transactionBuilder.addArray() + transactionArray.addInputArray() + transactionArray.addOutputArray(transactionData) + transactionArray.addMap().end() + + val transactionBaos = ByteArrayOutputStream() + CborEncoder(transactionBaos).encode(transactionBuilder.build()) + transactionBody = transactionBaos.toByteArray() + + val blake2b = Blake2b.Digest.newInstance(32) + val transactionBodyHash = blake2b.digest(transactionBody) + + val magicBaos = ByteArrayOutputStream() + CborEncoder(magicBaos).encode(CborBuilder().add(PROTOCOL_MAGIC).build()) + val magic = magicBaos.toByteArray() + + //dataToSign prefix + val prefixedHashBaos = ByteArrayOutputStream() + prefixedHashBaos.write(byteArrayOf(0x01.toByte())) + prefixedHashBaos.write(magic) + prefixedHashBaos.write(byteArrayOf(0x58.toByte(), 0x20.toByte())) + prefixedHashBaos.write(transactionBodyHash) + return prefixedHashBaos.toByteArray() + } + + fun buildToSend(signature: ByteArray, publicKey: ByteArray): ByteArray { + val extendedPublicKey = CardanoAddressFactory.extendPublicKey(publicKey) + + //pubkey + signature + val witnessBodyBaos = ByteArrayOutputStream() + CborEncoder(witnessBodyBaos).encode(CborBuilder() + .addArray() + .add(extendedPublicKey) + .add(signature) + .end() + .build()) + val witnessBody = witnessBodyBaos.toByteArray() + val witnessBodyItem = CborBuilder().add(witnessBody).build()[0] + witnessBodyItem.setTag(24) + + val witnessBuilder = CborBuilder() + val witnessArrayBuilder = witnessBuilder.addArray() + + //witness type + witness body + for (utxo in unspentOutputs) { + witnessArrayBuilder + .addArray() + .add(0) + .add(witnessBodyItem) + .end() + } + + val witnessBaos = ByteArrayOutputStream() + CborEncoder(witnessBaos).encode(witnessBuilder.build()) + val witness = witnessBaos.toByteArray() + + val transactionBaos = ByteArrayOutputStream() + transactionBaos.write(byteArrayOf(0x82.toByte())) + transactionBaos.write(transactionBody) + transactionBaos.write(witness) + return transactionBaos.toByteArray() + } + + private fun ArrayBuilder.addInputArray() { + val inputArray = this.startArray() + + for (utxo in unspentOutputs) { + val inputBaos = ByteArrayOutputStream() + CborEncoder(inputBaos).encode(CborBuilder() + .addArray() + .add(utxo.hash) + .add(utxo.outputIndex) + .end() + .build()) + val input = inputBaos.toByteArray() + + val inputItem = CborBuilder().add(input).build().get(0) + inputItem.setTag(24) + //input type + input + inputArray + .addArray() + .add(0) + .add(inputItem) + .end() + } + inputArray.end() + } + + private fun ArrayBuilder.addOutputArray(transactionData: TransactionData) { + val amount = transactionData.amount.value!! + .movePointRight(transactionData.amount.decimals.toInt()).toLong() + val fee = transactionData.fee!!.value!! + .movePointRight(transactionData.fee.decimals.toInt()).toLong() + val change = calculateChange(amount, fee) + + val outputArray = this.startArray() + + //1st output + val targetAddressItem = + CborDecoder(ByteArrayInputStream(transactionData.destinationAddress.decodeBase58())) + .decode()[0] + outputArray + .addArray() + .add(targetAddressItem) + .add(amount) + .end() + + //2nd output (optional) + if (change > 0) { + val myAddressItem = + CborDecoder(ByteArrayInputStream(transactionData.sourceAddress.decodeBase58())) + .decode()[0] + outputArray + .addArray() + .add(myAddressItem) + .add(change) + .end() + } + outputArray.end() + } + + private fun calculateChange(amount: Long, fee: Long): Long { + val fullAmount = unspentOutputs.map { it.amount }.sum() + return fullAmount - (amount + fee) + } + + companion object { + private const val PROTOCOL_MAGIC: Long = 764824073 + } +} + +class UnspentOutput( + val amount: Long, + val outputIndex: Long, + val hash: ByteArray +) \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoWalletManager.kt new file mode 100644 index 0000000000..421eb5d996 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoWalletManager.kt @@ -0,0 +1,85 @@ +package com.tangem.blockchain.cardano + +import android.util.Base64 +import android.util.Log +import com.tangem.blockchain.cardano.network.CardanoAddressResponse +import com.tangem.blockchain.cardano.network.CardanoNetworkManager +import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.extensions.Result +import com.tangem.blockchain.common.extensions.SimpleResult +import com.tangem.blockchain.common.extensions.encodeBase64NoWrap +import com.tangem.blockchain.wallets.CurrencyWallet +import com.tangem.tasks.TaskEvent +import java.math.BigDecimal + +class CardanoWalletManager( + private val cardId: String, + private val walletPublicKey: ByteArray, + walletConfig: WalletConfig +) : WalletManager, + TransactionEstimator, + TransactionSender, + FeeProvider { + + override val blockchain = Blockchain.Cardano + private val address = blockchain.makeAddress(walletPublicKey) + private val currencyWallet = CurrencyWallet(walletConfig, address) + override var wallet: Wallet = currencyWallet + private val transactionBuilder = CardanoTransactionBuilder() + private val networkManager = CardanoNetworkManager() + + override suspend fun update() { + val response = networkManager.getInfo(address) + when (response) { + is Result.Success -> updateWallet(response.data) + is Result.Failure -> updateError(response.error) + } + } + + private fun updateWallet(response: CardanoAddressResponse) { + Log.d(this::class.java.simpleName, "Balance is ${response.balance.toString()}") + currencyWallet.balances[AmountType.Coin]?.value = + response.balance.toBigDecimal().movePointLeft(blockchain.decimals.toInt()) + transactionBuilder.unspentOutputs = response.unspentOutputs + } + + private fun updateError(error: Throwable?) { + Log.e(this::class.java.simpleName, error?.message ?: "") + } + + override suspend fun getEstimateSize(transactionData: TransactionData): Int { + val dummyFeeValue = BigDecimal.valueOf(0.1) + + val dummyFee = transactionData.amount.copy(value = dummyFeeValue) + val dummyAmount = + transactionData.amount.copy(value = transactionData.amount.value!! - dummyFeeValue) + + val dummyTransactionData = transactionData.copy( + amount = dummyAmount, + fee = dummyFee + ) + transactionBuilder.buildToSign(dummyTransactionData) + return transactionBuilder.buildToSend(ByteArray(64), walletPublicKey).size + } + + override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult { + val transactionHash = transactionBuilder.buildToSign(transactionData) + + when (val signerResponse = signer.sign(arrayOf(transactionHash), cardId)) { + is TaskEvent.Event -> { + val transactionToSend = transactionBuilder.buildToSend(signerResponse.data.signature, walletPublicKey) + return networkManager.sendTransaction(transactionToSend.encodeBase64NoWrap()) + } + is TaskEvent.Completion -> return SimpleResult.Failure(signerResponse.error) + } + } + + override suspend fun getFee(amount: Amount, source: String, destination: String): Result> { + val a = 0.155381 + val b = 0.000043946 + val size = getEstimateSize(TransactionData(amount, null, source, destination)) + + val fee = (a + b * size).toBigDecimal() + return Result.Success(listOf(Amount(blockchain.currency, fee, source, blockchain.decimals))) + } +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/cardano/crypto/Blake2b.java b/blockchain/src/main/java/com/tangem/blockchain/cardano/crypto/Blake2b.java new file mode 100644 index 0000000000..01358597fd --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/crypto/Blake2b.java @@ -0,0 +1,2097 @@ +/* !!! 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.blockchain.cardano.crypto; + +import java.io.PrintStream; +import java.io.Serializable; +import java.security.Key; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Arrays; + +import static com.tangem.blockchain.cardano.crypto.Blake2b.Engine.Assert.assertFail; +import static com.tangem.blockchain.cardano.crypto.Blake2b.Engine.Assert.exclusiveLowerBound; +import static com.tangem.blockchain.cardano.crypto.Blake2b.Engine.Assert.inclusiveLowerBound; +import static com.tangem.blockchain.cardano.crypto.Blake2b.Engine.Assert.inclusiveUpperBound; +import static com.tangem.blockchain.cardano.crypto.Blake2b.Engine.LittleEndian.readInt; +import static com.tangem.blockchain.cardano.crypto.Blake2b.Engine.LittleEndian.readLong; +import static com.tangem.blockchain.cardano.crypto.Blake2b.Engine.LittleEndian.writeInt; +import static com.tangem.blockchain.cardano.crypto.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/blockchain/src/main/java/com/tangem/blockchain/cardano/network/CardanoNetworkManager.kt b/blockchain/src/main/java/com/tangem/blockchain/cardano/network/CardanoNetworkManager.kt new file mode 100644 index 0000000000..1852c6b994 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/network/CardanoNetworkManager.kt @@ -0,0 +1,67 @@ +package com.tangem.blockchain.cardano.network + +import com.tangem.blockchain.cardano.UnspentOutput +import com.tangem.blockchain.cardano.network.adalite.AdaliteProvider +import com.tangem.blockchain.cardano.network.api.AdaliteApi +import com.tangem.blockchain.common.extensions.Result +import com.tangem.blockchain.common.extensions.SimpleResult +import com.tangem.blockchain.common.network.API_ADALITE +import com.tangem.blockchain.common.network.API_ADALITE_RESERVE +import com.tangem.blockchain.common.network.createRetrofitInstance +import retrofit2.HttpException +import java.io.IOException + +class CardanoNetworkManager { + private val adaliteProvider by lazy { + val api = createRetrofitInstance(API_ADALITE) + .create(AdaliteApi::class.java) + AdaliteProvider(api) + } + + private val adaliteReserveProvider by lazy { + val api = createRetrofitInstance(API_ADALITE_RESERVE) + .create(AdaliteApi::class.java) + AdaliteProvider(api) + } + + private var provider = adaliteProvider + + private fun changeProvider() { + provider = if (provider == adaliteProvider) adaliteReserveProvider else adaliteProvider + } + + suspend fun getInfo(address: String): Result { + val result = provider.getInfo(address) + when (result) { + is Result.Success -> return result + is Result.Failure -> { + if (result.error is IOException || result.error is HttpException) { + changeProvider() + return provider.getInfo(address) + } else { + return result + } + } + } + } + + suspend fun sendTransaction(transaction: String): SimpleResult { + val result = provider.sendTransaction(transaction) + when (result) { + is SimpleResult.Success -> return result + is SimpleResult.Failure -> { + if (result.error is IOException || result.error is HttpException) { + changeProvider() + return provider.sendTransaction(transaction) + } else { + return result + } + } + } + } +} + +data class CardanoAddressResponse( + val balance: Long, + val unspentOutputs: List +) \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/cardano/network/adalite/AdaliteApi.kt b/blockchain/src/main/java/com/tangem/blockchain/cardano/network/adalite/AdaliteApi.kt new file mode 100644 index 0000000000..e394d424f6 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/network/adalite/AdaliteApi.kt @@ -0,0 +1,20 @@ +package com.tangem.blockchain.cardano.network.api + +import com.tangem.blockchain.cardano.network.adalite.AdaliteAddress +import com.tangem.blockchain.cardano.network.adalite.AdaliteSendBody +import com.tangem.blockchain.cardano.network.adalite.AdaliteUnspents +import retrofit2.http.* +import shadow.okhttp3.ResponseBody + +interface AdaliteApi { + @GET("/api/addresses/summary/{address}") + suspend fun getAddress(@Path("address") address: String): AdaliteAddress + + @Headers("Content-Type: application/json") + @POST("/api/bulk/addresses/utxo") + suspend fun getUnspents(@Body address: String): AdaliteUnspents + + @Headers("Content-Type: application/json") + @POST("/api/v2/txs/signed") + suspend fun sendTransaction(@Body adaliteBody: AdaliteSendBody): ResponseBody // List? +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/cardano/network/adalite/AdaliteProvider.kt b/blockchain/src/main/java/com/tangem/blockchain/cardano/network/adalite/AdaliteProvider.kt new file mode 100644 index 0000000000..ec05798ced --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/network/adalite/AdaliteProvider.kt @@ -0,0 +1,53 @@ +package com.tangem.blockchain.cardano.network.adalite + +import com.tangem.blockchain.cardano.network.CardanoAddressResponse +import com.tangem.blockchain.cardano.UnspentOutput +import com.tangem.blockchain.cardano.network.api.AdaliteApi +import com.tangem.blockchain.common.extensions.Result +import com.tangem.blockchain.common.extensions.SimpleResult +import com.tangem.blockchain.common.extensions.retryIO +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +class AdaliteProvider(private val api: AdaliteApi) { + + suspend fun getInfo(address: String): Result { + return try { + coroutineScope { + val addressDeferred = retryIO { async { api.getAddress(address) } } + val unspentsDeferred = retryIO { async { api.getUnspents(address) } } + + val addressData = addressDeferred.await() + val unspents = unspentsDeferred.await() + + val cardanoUnspents = unspents.data.map { + UnspentOutput( + it.amountData!!.amount!!, + it.outputIndex!!.toLong(), + it.hash!!.toByteArray() + ) + } + + Result.Success( + CardanoAddressResponse( + addressData.data!!.balanceData!!.amount!!, + cardanoUnspents + ) + ) + } + } catch (exception: Exception) { + Result.Failure(exception) + } + } + + suspend fun sendTransaction(transaction: String): SimpleResult { + return try { + retryIO { api.sendTransaction(AdaliteSendBody(transaction)) } + SimpleResult.Success + } catch (exception: Exception) { + SimpleResult.Failure(exception) + } + } +} + +data class AdaliteSendBody(val signedTransaction: String) \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/cardano/network/adalite/AdaliteResponse.kt b/blockchain/src/main/java/com/tangem/blockchain/cardano/network/adalite/AdaliteResponse.kt new file mode 100644 index 0000000000..4bc1f86894 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/network/adalite/AdaliteResponse.kt @@ -0,0 +1,49 @@ +package com.tangem.blockchain.cardano.network.adalite + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class AdaliteAddress( + @Json(name = "final_balance") + var data: AdaliteAddressData? = null +) + +@JsonClass(generateAdapter = true) +data class AdaliteAddressData( + @Json(name = "caBalance") + var balanceData: BalanceData? = null + +// @Json(name = "caTxList") +// var transactions: List +) + +@JsonClass(generateAdapter = true) +data class BalanceData( + @Json(name = "getCoin") + var amount: Long? = null +) + +@JsonClass(generateAdapter = true) +data class AdaliteUnspents( + @Json(name = "Right") + var data: List +) + +@JsonClass(generateAdapter = true) +data class AdaliteUtxo( + @Json(name = "cuId") + var hash: String? = null, + + @Json(name = "cuOutIndex") + var outputIndex: Int? = null, + + @Json(name = "cuCoins") + var amountData: BalanceData? = null +) + +//@JsonClass(generateAdapter = true) +//data class AdaliteTransaction( +// @Json(name = "ctbId") +// var hash: String? = null +//) \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt b/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt index c048f2faaf..72edea8f71 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/Blockchain.kt @@ -2,6 +2,8 @@ package com.tangem.blockchain.common import com.tangem.blockchain.bitcoin.BitcoinAddressFactory import com.tangem.blockchain.bitcoin.BitcoinAddressValidator +import com.tangem.blockchain.cardano.CardanoAddressFactory +import com.tangem.blockchain.cardano.CardanoAddressValidator import com.tangem.blockchain.eth.EthereumAddressFactory import com.tangem.blockchain.eth.EthereumAddressValidator import com.tangem.blockchain.stellar.StellarAddressFactory @@ -37,7 +39,7 @@ enum class Blockchain( BitcoinTestnet -> BitcoinAddressFactory.makeAddress(cardPublicKey, testNet = true) Ethereum -> EthereumAddressFactory.makeAddress(cardPublicKey) // Rootstock -> RootstockAddressFactory.makeAddress(cardPublicKey) -// Cardano -> CardanoAddressFactory.makeAddress(cardPublicKey) + Cardano -> CardanoAddressFactory.makeAddress(cardPublicKey) // Ripple -> RippleAddressFactory.makeAddress(cardPublicKey) // Binance -> BinanceAddressFactory.makeAddress(cardPublicKey) Stellar -> StellarAddressFactory.makeAddress(cardPublicKey) @@ -52,7 +54,7 @@ enum class Blockchain( BitcoinTestnet -> BitcoinAddressValidator.validate(address, testNet = true) Ethereum -> EthereumAddressValidator.validate(address) // Rootstock -> RootstockAddressValidator.validate(address) -// Cardano -> CardanoAddressValidator.validate(address) + Cardano -> CardanoAddressValidator.validate(address) // Ripple -> RippleAddressValidator.validate(address) // Binance -> BinanceAddressValidator.validate(address) // Stellar -> StellarAddressValidator.validate(address) diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt index 090c15eb1a..ebb54d6efe 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManagerFactory.kt @@ -1,6 +1,7 @@ package com.tangem.blockchain.common import com.tangem.blockchain.bitcoin.BitcoinWalletManager +import com.tangem.blockchain.cardano.CardanoWalletManager import com.tangem.blockchain.eth.Chain import com.tangem.blockchain.eth.EthereumWalletManager import com.tangem.blockchain.stellar.StellarWalletManager @@ -41,6 +42,13 @@ object WalletManagerFactory { token = token, isTestNet = isTestNet(blockchainName)) } + blockchainName.contains("cardano") -> { + return CardanoWalletManager( + cardId = card.cardId, + walletPublicKey = walletPublicKey, + walletConfig = WalletConfig(false, true) + ) + } else -> return null } } diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/extensions/ByteArray.kt b/blockchain/src/main/java/com/tangem/blockchain/common/extensions/ByteArray.kt new file mode 100644 index 0000000000..29ce488afc --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/common/extensions/ByteArray.kt @@ -0,0 +1,12 @@ +package com.tangem.blockchain.common.extensions + +import android.util.Base64 +import org.bitcoinj.core.Base58 + +fun ByteArray.encodeBase58(): String { + return Base58.encode(this) +} + +fun ByteArray.encodeBase64NoWrap(): String { + return Base64.encodeToString(this, Base64.NO_WRAP) +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/extensions/String.kt b/blockchain/src/main/java/com/tangem/blockchain/common/extensions/String.kt new file mode 100644 index 0000000000..8f14abf5f8 --- /dev/null +++ b/blockchain/src/main/java/com/tangem/blockchain/common/extensions/String.kt @@ -0,0 +1,12 @@ +package com.tangem.blockchain.common.extensions + +import org.bitcoinj.core.AddressFormatException +import org.bitcoinj.core.Base58 + +fun String.decodeBase58(): ByteArray? { + return try { + Base58.decode(this) + } catch (exception: AddressFormatException) { + null + } +} \ No newline at end of file diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/network/RetrofitBuilder.kt b/blockchain/src/main/java/com/tangem/blockchain/common/network/RetrofitBuilder.kt index 90e05fa592..362e57726e 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/network/RetrofitBuilder.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/network/RetrofitBuilder.kt @@ -41,4 +41,6 @@ const val API_MATIC_TESTNET = "https://testnet2.matic.network/" const val API_STELLAR = "https://horizon.stellar.org/" const val API_STELLAR_RESERVE = "https://horizon.sui.li/" const val API_STELLAR_TESTNET = "https://horizon-testnet.stellar.org/" -const val API_BLOCKCHAIN_INFO = "https://blockchain.info/" \ No newline at end of file +const val API_BLOCKCHAIN_INFO = "https://blockchain.info/" +const val API_ADALITE = "https://explorer2.adalite.io" +const val API_ADALITE_RESERVE = "https://nodes.southeastasia.cloudapp.azure.com" \ No newline at end of file