├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── org │ └── p2p │ └── solanaj │ ├── core │ ├── Account.java │ ├── AccountKeysList.java │ ├── AccountMeta.java │ ├── Lockup.java │ ├── Message.java │ ├── NonceAccount.java │ ├── PublicKey.java │ ├── Sysvar.java │ ├── Transaction.java │ └── TransactionInstruction.java │ ├── programs │ ├── ComputeBudgetProgram.java │ ├── StakeProgram.java │ ├── SystemProgram.java │ └── TokenProgram.java │ ├── rpc │ ├── Cluster.java │ ├── RpcApi.java │ ├── RpcClient.java │ ├── RpcException.java │ └── types │ │ ├── AccountInfo.java │ │ ├── ConfigObjects.java │ │ ├── ConfirmedTransaction.java │ │ ├── ProgramAccount.java │ │ ├── RecentBlockhash.java │ │ ├── RpcNotificationResult.java │ │ ├── RpcRequest.java │ │ ├── RpcResponse.java │ │ ├── RpcResultObject.java │ │ ├── RpcResultTypes.java │ │ ├── RpcSendTransactionConfig.java │ │ └── SignatureInformation.java │ ├── utils │ ├── ByteUtils.java │ ├── SerializeUtils.java │ ├── ShortvecEncoding.java │ └── TweetNaclFast.java │ └── ws │ ├── SignatureNotification.java │ ├── SubscriptionWebSocketClient.java │ └── listeners │ └── NotificationEventListener.java └── test └── java └── org └── p2p └── solanaj ├── core ├── AccountKeysListTest.java ├── AccountTest.java ├── MainnetTest.java ├── MessageTest.java ├── PublicKeyTest.java └── TransactionTest.java ├── programs └── SystemProgramTest.java └── utils ├── ByteUtilsTest.java └── ShortvecEncodingTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | target 3 | build 4 | .project 5 | .classpath 6 | .settings 7 | .idea 8 | *.iml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 p2p.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # solanaj 2 | 3 | Based on [p2p.org/solanaj](https://github.com/p2p-org/solanaj) 4 | 5 | Solana blockchain client, written in pure Java. 6 | Solanaj is an API for integrating with Solana blockchain using the [Solana RPC API](https://docs.solana.com/apps/jsonrpc-api) 7 | 8 | ## Requirements 9 | - Java 8+ 10 | 11 | ## Dependencies 12 | - bitcoinj 13 | - OkHttp 14 | - Moshi 15 | 16 | ### Example 17 | 18 | ##### Generate Keypair 19 | 20 | ```java 21 | Account account = new Account(); 22 | byte[] privateKey = account.getSecretKey(); 23 | PublicKey publicKey = account.getPublicKey(); 24 | String address = publicKey.toBase58(); 25 | ``` 26 | ##### Transfer lamports 27 | ```java 28 | RpcClient client = new RpcClient(Cluster.TESTNET); 29 | 30 | PublicKey fromPublicKey = new PublicKey("QqCCvshxtqMAL2CVALqiJB7uEeE5mjSPsseQdDzsRUo"); 31 | PublicKey toPublickKey = new PublicKey("GrDMoeqMLFjeXQ24H56S1RLgT4R76jsuWCd6SvXyGPQ5"); 32 | int lamports = 3000; 33 | 34 | Account signer = new Account(secret_key); 35 | 36 | Transaction transaction = new Transaction(); 37 | transaction.addInstruction(SystemProgram.transfer(fromPublicKey, toPublickKey, lamports)); 38 | 39 | String signature = client.getApi().sendTransaction(transaction, signer); 40 | ``` 41 | 42 | ##### Get balance 43 | 44 | ```java 45 | RpcClient client = new RpcClient(Cluster.TESTNET); 46 | 47 | long balance = client.getApi().getBalance(new PublicKey("QqCCvshxtqMAL2CVALqiJB7uEeE5mjSPsseQdDzsRUo")); 48 | ``` 49 | ##### Create nonce account 50 | ```java 51 | Account walletAccount = new Account(); 52 | String walletAddress = walletAccount.getPublicKey().toBase58(); 53 | 54 | Account nonceAccount = new Account(); 55 | String nonceAddress = nonceAccount.getPublicKey().toBase58(); 56 | Transaction transaction = new Transaction(); 57 | 58 | RpcClient rpcClient = new RpcClient(Cluster.DEVNET); 59 | long rent = rpcClient.getApi().getMinimumBalanceForRentExemption(NonceAccount.NONCE_ACCOUNT_LENGTH); 60 | transaction.addInstruction(SystemProgram.createAccount(walletAddress, nonceAddress, rent, NonceAccount.NONCE_ACCOUNT_LENGTH, SystemProgram.PROGRAM_ID)); 61 | transaction.addInstruction(SystemProgram.nonceInitialize(nonceAddress, walletAddress)); 62 | 63 | String recentBlockhash = rpcClient.getApi().getRecentBlockhash(); 64 | transaction.setRecentBlockHash(recentBlockhash); 65 | transaction.setFeePayer(walletAddress); 66 | 67 | transaction.sign(Arrays.asList(walletAccount, nonceAccount)); 68 | String txHash = rpcClient.getApi().sendTransaction(transaction); 69 | ``` 70 | ##### Use nonceAccount transfer 71 | ```java 72 | Transaction transaction = new Transaction(); 73 | transaction.addInstruction(SystemProgram.nonceAdvance(nonceAddress,walletAddress)); 74 | transaction.addInstruction(SystemProgram.transfer(walletAddress,"receiveAddress",1000000000L)); 75 | 76 | AccountInfo nonceAccountInfo = rpcClient.getApi().getAccountInfo(nonceAccount.getPublicKey()); 77 | List data = nonceAccountInfo.getValue().getData(); 78 | NonceAccount nonce = NonceAccount.fromAccountData(data); 79 | 80 | transaction.setRecentBlockHash(nonce.getBlockHash()); 81 | transaction.setFeePayer(walletAddress); 82 | //sender and nonceAccount`s owner signature 83 | transaction.sign(walletAccount); 84 | String txHash = rpcClient.getApi().sendTransaction(transaction); 85 | ``` 86 | 87 | ##### Calculate Associated token Address 88 | ```java 89 | String associatedTokenAddress = TokenProgram.getAssociatedTokenAddress(TokenProgram.TOKEN_PROGRAM_ID, mintAddress, walletAddress); 90 | ``` 91 | ##### Create Associated token Address 92 | ```java 93 | String toAssociatedTokenAddress = TokenProgram.getAssociatedTokenAddress(TokenProgram.TOKEN_PROGRAM_ID, mint, to); 94 | transaction.addInstruction(TokenProgram.createAssociatedTokenAccount(TokenProgram.TOKEN_PROGRAM_ID,mint,toAssociatedTokenAddress,to,from)); 95 | ``` 96 | 97 | ##### Transfer SPL token 98 | ```java 99 | String fromAssociatedTokenAddress = TokenProgram.getAssociatedTokenAddress(TokenProgram.TOKEN_PROGRAM_ID, mint, from); 100 | String toAssociatedTokenAddress = TokenProgram.getAssociatedTokenAddress(TokenProgram.TOKEN_PROGRAM_ID, mint, to); 101 | transaction.addInstruction(TokenProgram.createTransferChecked(TokenProgram.TOKEN_PROGRAM_ID,fromAssociatedTokenAddress,mint,toAssociatedTokenAddress,from,new ArrayList<>(),value,decimals)); 102 | ``` 103 | 104 | ##### Serializable transaction 105 | ```java 106 | Message message = new Message(); 107 | 108 | //add a few Instruction 109 | message.addInstruction(); 110 | message.addInstruction(); 111 | message.addInstruction(); 112 | 113 | message.setRecentBlockHash(blockHash); 114 | message.setFeePayer(feePayer); 115 | 116 | byte[] serializedMessage = message.serialize(); 117 | 118 | //sign 119 | List signers = new ArrayList<>(); 120 | List signatures = new ArrayList<>(); 121 | signers.add(new byte[]{});//signer A 122 | signers.add(new byte[]{});//signer B 123 | for (byte[] privateKey : signers) { 124 | TweetNaclFast.Signature signatureProvider = new TweetNaclFast.Signature(new byte[0],privateKey); 125 | byte[] signature = signatureProvider.detached(serializedMessage); 126 | signatures.add(signature); 127 | } 128 | 129 | byte[] signedTx = SerializeUtils.serialize(serializedMessage, signatures); 130 | 131 | String base64SignedTx = Base64.getEncoder().encodeToString(signedTx); 132 | String txHash = Base58.encode(signatures.get(0)); 133 | ``` 134 | 135 | ## Contribution 136 | 137 | Welcome to contribute, feel free to change and open a PR. 138 | 139 | 140 | ## License 141 | 142 | Solanaj is available under the MIT license. See the LICENSE file for more info. 143 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | org.p2p 5 | solanaj 6 | jar 7 | 1.0-SNAPSHOT 8 | solanaj 9 | http://maven.apache.org 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 8 17 | 8 18 | 19 | 20 | 21 | 22 | 8 23 | 8 24 | 25 | 26 | 27 | junit 28 | junit 29 | 4.13.1 30 | test 31 | 32 | 33 | org.bitcoinj 34 | bitcoinj-core 35 | 0.15.8 36 | 37 | 38 | com.squareup.okhttp3 39 | okhttp 40 | 4.9.0 41 | 42 | 43 | com.squareup.moshi 44 | moshi 45 | 1.9.2 46 | 47 | 48 | com.squareup.moshi 49 | moshi-adapters 50 | 1.9.2 51 | 52 | 53 | org.java-websocket 54 | Java-WebSocket 55 | 1.5.1 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/core/Account.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import java.util.List; 4 | 5 | import org.bitcoinj.crypto.DeterministicHierarchy; 6 | import org.bitcoinj.crypto.DeterministicKey; 7 | import org.bitcoinj.crypto.HDKeyDerivation; 8 | import org.bitcoinj.crypto.HDUtils; 9 | import org.bitcoinj.crypto.MnemonicCode; 10 | import org.p2p.solanaj.utils.TweetNaclFast; 11 | 12 | public class Account { 13 | private TweetNaclFast.Signature.KeyPair keyPair; 14 | 15 | public Account() { 16 | this.keyPair = TweetNaclFast.Signature.keyPair(); 17 | } 18 | 19 | public Account(byte[] secretKey) { 20 | this.keyPair = TweetNaclFast.Signature.keyPair_fromSecretKey(secretKey); 21 | } 22 | 23 | private Account(TweetNaclFast.Signature.KeyPair keyPair) { 24 | this.keyPair = keyPair; 25 | } 26 | 27 | public static Account fromMnemonic(List words, String passphrase) { 28 | byte[] seed = MnemonicCode.toSeed(words, passphrase); 29 | DeterministicKey masterPrivateKey = HDKeyDerivation.createMasterPrivateKey(seed); 30 | DeterministicHierarchy deterministicHierarchy = new DeterministicHierarchy(masterPrivateKey); 31 | DeterministicKey child = deterministicHierarchy.get(HDUtils.parsePath("M/501H/0H/0/0"), true, true); 32 | 33 | TweetNaclFast.Signature.KeyPair keyPair = TweetNaclFast.Signature.keyPair_fromSeed(child.getPrivKeyBytes()); 34 | 35 | return new Account(keyPair); 36 | } 37 | 38 | public PublicKey getPublicKey() { 39 | return new PublicKey(keyPair.getPublicKey()); 40 | } 41 | 42 | public byte[] getSecretKey() { 43 | return keyPair.getSecretKey(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/core/AccountKeysList.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Comparator; 6 | import java.util.List; 7 | 8 | /** 9 | * @author dongyuhao 10 | */ 11 | public class AccountKeysList { 12 | private List accountsList; 13 | 14 | public AccountKeysList() { 15 | accountsList = new ArrayList<>(); 16 | } 17 | 18 | public void add(AccountMeta accountMeta) { 19 | accountsList.add(accountMeta); 20 | } 21 | 22 | public void addAll(Collection metas) { 23 | accountsList.addAll(metas); 24 | } 25 | 26 | public List getList() { 27 | ArrayList uniqueMetas = new ArrayList<>(); 28 | 29 | for (AccountMeta accountMeta : accountsList) { 30 | PublicKey pubKey = accountMeta.getPublicKey(); 31 | 32 | int index = AccountMeta.findAccountIndex(uniqueMetas, pubKey); 33 | if (index > -1) { 34 | uniqueMetas.set(index, 35 | new AccountMeta(pubKey, uniqueMetas.get(index).isSigner() || accountMeta.isSigner(), 36 | uniqueMetas.get(index).isWritable() || accountMeta.isWritable())); 37 | } else { 38 | uniqueMetas.add(accountMeta); 39 | } 40 | } 41 | 42 | uniqueMetas.sort(metaComparator); 43 | 44 | return uniqueMetas; 45 | } 46 | 47 | private static final Comparator metaComparator = (am1, am2) -> { 48 | 49 | int cmpSigner = am1.isSigner() == am2.isSigner() ? 0 : am1.isSigner() ? -1 : 1; 50 | if (cmpSigner != 0) { 51 | return cmpSigner; 52 | } 53 | 54 | int cmpkWritable = am1.isWritable() == am2.isWritable() ? 0 : am1.isWritable() ? -1 : 1; 55 | if (cmpkWritable != 0) { 56 | return cmpkWritable; 57 | } 58 | 59 | return 0; 60 | }; 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/core/AccountMeta.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import java.util.List; 4 | 5 | public class AccountMeta { 6 | private PublicKey publicKey; 7 | private boolean isSigner; 8 | private boolean isWritable; 9 | 10 | public AccountMeta(PublicKey publicKey, boolean isSigner, boolean isWritable) { 11 | this.publicKey = publicKey; 12 | this.isSigner = isSigner; 13 | this.isWritable = isWritable; 14 | } 15 | 16 | public PublicKey getPublicKey() { 17 | return publicKey; 18 | } 19 | 20 | public boolean isSigner() { 21 | return isSigner; 22 | } 23 | 24 | public boolean isWritable() { 25 | return isWritable; 26 | } 27 | 28 | public static int findAccountIndex(List accountMetaList, PublicKey key) { 29 | for (int i = 0; i < accountMetaList.size(); i++) { 30 | if (accountMetaList.get(i).getPublicKey().equals(key)) { 31 | return i; 32 | } 33 | } 34 | 35 | return -1; 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/core/Lockup.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | 4 | /** 5 | * @author dongyuhao 6 | */ 7 | 8 | public class Lockup { 9 | private Long unixTimestamp; 10 | 11 | private Long epoch; 12 | 13 | private PublicKey custodian; 14 | 15 | 16 | public Lockup(Long unixTimestamp, Long epoch, String custodian) { 17 | this.unixTimestamp = unixTimestamp; 18 | this.epoch = epoch; 19 | this.custodian = new PublicKey(custodian); 20 | } 21 | 22 | public Long getUnixTimestamp() { 23 | return unixTimestamp; 24 | } 25 | 26 | public void setUnixTimestamp(Long unixTimestamp) { 27 | this.unixTimestamp = unixTimestamp; 28 | } 29 | 30 | public Long getEpoch() { 31 | return epoch; 32 | } 33 | 34 | public void setEpoch(Long epoch) { 35 | this.epoch = epoch; 36 | } 37 | 38 | public PublicKey getCustodian() { 39 | return custodian; 40 | } 41 | 42 | public void setCustodian(PublicKey custodian) { 43 | this.custodian = custodian; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/core/Message.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | 4 | import org.bitcoinj.core.Base58; 5 | import org.p2p.solanaj.core.AccountKeysList; 6 | import org.p2p.solanaj.core.AccountMeta; 7 | import org.p2p.solanaj.core.PublicKey; 8 | import org.p2p.solanaj.core.TransactionInstruction; 9 | import org.p2p.solanaj.utils.ShortvecEncoding; 10 | 11 | import java.nio.ByteBuffer; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class Message { 16 | private class MessageHeader { 17 | static final int HEADER_LENGTH = 3; 18 | 19 | byte numRequiredSignatures = 0; 20 | byte numReadonlySignedAccounts = 0; 21 | byte numReadonlyUnsignedAccounts = 0; 22 | 23 | byte[] toByteArray() { 24 | return new byte[] { numRequiredSignatures, numReadonlySignedAccounts, numReadonlyUnsignedAccounts }; 25 | } 26 | } 27 | 28 | private class CompiledInstruction { 29 | byte programIdIndex; 30 | byte[] keyIndicesCount; 31 | byte[] keyIndices; 32 | byte[] dataLength; 33 | byte[] data; 34 | 35 | int getLength() { 36 | // 1 = programIdIndex length 37 | return 1 + keyIndicesCount.length + keyIndices.length + dataLength.length + data.length; 38 | } 39 | } 40 | 41 | private static final int RECENT_BLOCK_HASH_LENGTH = 32; 42 | 43 | private MessageHeader messageHeader; 44 | private String recentBlockhash; 45 | private AccountKeysList accountKeys; 46 | private List instructions; 47 | private PublicKey feePayer; 48 | private List programIds; 49 | 50 | public Message() { 51 | this.programIds = new ArrayList<>(); 52 | this.accountKeys = new AccountKeysList(); 53 | this.instructions = new ArrayList<>(); 54 | } 55 | 56 | public Message addInstruction(TransactionInstruction instruction) { 57 | accountKeys.addAll(instruction.getKeys()); 58 | instructions.add(instruction); 59 | 60 | if (!programIds.contains(instruction.getProgramId().toBase58())) { 61 | programIds.add(instruction.getProgramId().toBase58()); 62 | } 63 | 64 | return this; 65 | } 66 | 67 | public void setRecentBlockHash(String recentBlockhash) { 68 | this.recentBlockhash = recentBlockhash; 69 | } 70 | 71 | public byte[] serialize() { 72 | 73 | if (recentBlockhash == null) { 74 | throw new IllegalArgumentException("recentBlockhash required"); 75 | } 76 | 77 | if (instructions.size() == 0) { 78 | throw new IllegalArgumentException("No instructions provided"); 79 | } 80 | 81 | messageHeader = new MessageHeader(); 82 | 83 | for (String programId : programIds) { 84 | accountKeys.add(new AccountMeta(new PublicKey(programId), false, false)); 85 | } 86 | List keysList = getAccountKeys(); 87 | int accountKeysSize = keysList.size(); 88 | 89 | byte[] accountAddressesLength = ShortvecEncoding.encodeLength(accountKeysSize); 90 | 91 | int compiledInstructionsLength = 0; 92 | List compiledInstructions = new ArrayList<>(); 93 | 94 | for (TransactionInstruction instruction : instructions) { 95 | int keysSize = instruction.getKeys().size(); 96 | 97 | byte[] keyIndices = new byte[keysSize]; 98 | for (int i = 0; i < keysSize; i++) { 99 | keyIndices[i] = (byte) AccountMeta.findAccountIndex(keysList, 100 | instruction.getKeys().get(i).getPublicKey()); 101 | } 102 | 103 | CompiledInstruction compiledInstruction = new CompiledInstruction(); 104 | compiledInstruction.programIdIndex = (byte) AccountMeta.findAccountIndex(keysList, 105 | instruction.getProgramId()); 106 | compiledInstruction.keyIndicesCount = ShortvecEncoding.encodeLength(keysSize); 107 | compiledInstruction.keyIndices = keyIndices; 108 | compiledInstruction.dataLength = ShortvecEncoding.encodeLength(instruction.getData().length); 109 | compiledInstruction.data = instruction.getData(); 110 | 111 | compiledInstructions.add(compiledInstruction); 112 | 113 | compiledInstructionsLength += compiledInstruction.getLength(); 114 | } 115 | 116 | byte[] instructionsLength = ShortvecEncoding.encodeLength(compiledInstructions.size()); 117 | 118 | int bufferSize = MessageHeader.HEADER_LENGTH + RECENT_BLOCK_HASH_LENGTH + accountAddressesLength.length 119 | + (accountKeysSize * PublicKey.PUBLIC_KEY_LENGTH) + instructionsLength.length 120 | + compiledInstructionsLength; 121 | 122 | ByteBuffer out = ByteBuffer.allocate(bufferSize); 123 | 124 | ByteBuffer accountKeysBuff = ByteBuffer.allocate(accountKeysSize * PublicKey.PUBLIC_KEY_LENGTH); 125 | for (AccountMeta accountMeta : keysList) { 126 | accountKeysBuff.put(accountMeta.getPublicKey().toByteArray()); 127 | 128 | if (accountMeta.isSigner()) { 129 | messageHeader.numRequiredSignatures += 1; 130 | if (!accountMeta.isWritable()) { 131 | messageHeader.numReadonlySignedAccounts += 1; 132 | } 133 | } else { 134 | if (!accountMeta.isWritable()) { 135 | messageHeader.numReadonlyUnsignedAccounts += 1; 136 | } 137 | } 138 | } 139 | 140 | out.put(messageHeader.toByteArray()); 141 | 142 | out.put(accountAddressesLength); 143 | out.put(accountKeysBuff.array()); 144 | 145 | out.put(Base58.decode(recentBlockhash)); 146 | 147 | out.put(instructionsLength); 148 | for (CompiledInstruction compiledInstruction : compiledInstructions) { 149 | out.put(compiledInstruction.programIdIndex); 150 | out.put(compiledInstruction.keyIndicesCount); 151 | out.put(compiledInstruction.keyIndices); 152 | out.put(compiledInstruction.dataLength); 153 | out.put(compiledInstruction.data); 154 | } 155 | 156 | return out.array(); 157 | } 158 | 159 | public void setFeePayer(PublicKey feePayer) { 160 | this.feePayer = feePayer; 161 | } 162 | 163 | public void setFeePayer(String feePayer){ 164 | setFeePayer(new PublicKey(feePayer)); 165 | } 166 | 167 | private List getAccountKeys() { 168 | List keysList = accountKeys.getList(); 169 | int feePayerIndex = AccountMeta.findAccountIndex(keysList, feePayer); 170 | 171 | List newList = new ArrayList<>(); 172 | 173 | if (feePayerIndex != -1) { 174 | AccountMeta feePayerMeta = keysList.get(feePayerIndex); 175 | newList.add(new AccountMeta(feePayerMeta.getPublicKey(), true, true)); 176 | keysList.remove(feePayerIndex); 177 | } else { 178 | newList.add(new AccountMeta(feePayer, true, true)); 179 | } 180 | newList.addAll(keysList); 181 | 182 | return newList; 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/core/NonceAccount.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import org.bitcoinj.core.Base58; 4 | import org.bitcoinj.core.Utils; 5 | 6 | import java.math.BigDecimal; 7 | import java.util.Base64; 8 | import java.util.List; 9 | 10 | /** 11 | * @author melody 12 | * @version NonceAccount.java, v 0.1 2022年07月20日 13 | */ 14 | public class NonceAccount extends Account { 15 | public static Long NONCE_ACCOUNT_LENGTH = 80L; 16 | private PublicKey authorizedPubkey; 17 | private String blockHash; 18 | private BigDecimal feeCalculator; 19 | 20 | public static NonceAccount fromAccountData(List data) { 21 | if (data == null || data.isEmpty()) { 22 | return null; 23 | } 24 | if (data.size() != 2) { 25 | return null; 26 | } 27 | if (!data.get(1).equals("base64")) { 28 | return null; 29 | } 30 | NonceAccount nonceAccount = new NonceAccount(); 31 | byte[] decode = Base64.getDecoder().decode(data.get(0)); 32 | byte[] authorityBytes = new byte[32]; 33 | System.arraycopy(decode, 8, authorityBytes, 0, 32); 34 | nonceAccount.setAuthorizedPubkey(new PublicKey(Base58.encode(authorityBytes))); 35 | 36 | byte[] blockHashBytes = new byte[32]; 37 | System.arraycopy(decode, 40, blockHashBytes, 0, 32); 38 | nonceAccount.setBlockHash(Base58.encode(blockHashBytes)); 39 | 40 | byte[] lamportBytes = new byte[8]; 41 | System.arraycopy(decode, 72, lamportBytes, 0, 8); 42 | nonceAccount.setFeeCalculator(new BigDecimal(Utils.readInt64(lamportBytes, 0))); 43 | return nonceAccount; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "NonceAccount{" + 49 | "authorizedPubkey='" + authorizedPubkey + '\'' + 50 | ", nonce='" + blockHash + '\'' + 51 | ", feeCalculator=" + feeCalculator + 52 | '}'; 53 | } 54 | 55 | public PublicKey getAuthorizedPubkey() { 56 | return authorizedPubkey; 57 | } 58 | 59 | public void setAuthorizedPubkey(PublicKey authorizedPubkey) { 60 | this.authorizedPubkey = authorizedPubkey; 61 | } 62 | 63 | public String getBlockHash() { 64 | return blockHash; 65 | } 66 | 67 | public void setBlockHash(String blockHash) { 68 | this.blockHash = blockHash; 69 | } 70 | 71 | public BigDecimal getFeeCalculator() { 72 | return feeCalculator; 73 | } 74 | 75 | public void setFeeCalculator(BigDecimal feeCalculator) { 76 | this.feeCalculator = feeCalculator; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/core/PublicKey.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import org.bitcoinj.core.Base58; 9 | import org.bitcoinj.core.Sha256Hash; 10 | import org.p2p.solanaj.utils.ByteUtils; 11 | import org.p2p.solanaj.utils.TweetNaclFast; 12 | 13 | public class PublicKey { 14 | 15 | public static final int PUBLIC_KEY_LENGTH = 32; 16 | 17 | private byte[] pubkey; 18 | 19 | public PublicKey(String pubkey) { 20 | if (pubkey.length() < PUBLIC_KEY_LENGTH) { 21 | throw new IllegalArgumentException("Invalid public key input"); 22 | } 23 | this.pubkey = Base58.decode(pubkey); 24 | } 25 | 26 | public PublicKey(byte[] pubkey) { 27 | 28 | if (pubkey.length > PUBLIC_KEY_LENGTH) { 29 | throw new IllegalArgumentException("Invalid public key input"); 30 | } 31 | 32 | this.pubkey = pubkey; 33 | } 34 | 35 | public static PublicKey readPubkey(byte[] bytes, int offset) { 36 | byte[] buf = ByteUtils.readBytes(bytes, offset, PUBLIC_KEY_LENGTH); 37 | return new PublicKey(buf); 38 | } 39 | 40 | public byte[] toByteArray() { 41 | return pubkey; 42 | } 43 | 44 | public String toBase58() { 45 | return Base58.encode(pubkey); 46 | } 47 | 48 | public boolean equals(PublicKey pubkey) { 49 | return Arrays.equals(this.pubkey, pubkey.toByteArray()); 50 | } 51 | 52 | public String toString() { 53 | return toBase58(); 54 | } 55 | 56 | public static PublicKey createProgramAddress(List seeds, PublicKey programId) throws Exception { 57 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 58 | 59 | for (byte[] seed : seeds) { 60 | if (seed.length > 32) { 61 | throw new IllegalArgumentException("Max seed length exceeded"); 62 | } 63 | buffer.write(seed); 64 | } 65 | 66 | buffer.write(programId.toByteArray()); 67 | buffer.write(programId.toByteArray()); 68 | buffer.write("ProgramDerivedAddress".getBytes()); 69 | 70 | byte[] hash = Sha256Hash.hash(buffer.toByteArray()); 71 | 72 | if (TweetNaclFast.is_on_curve(hash) != 0) { 73 | throw new Exception("Invalid seeds, address must fall off the curve"); 74 | } 75 | 76 | return new PublicKey(hash); 77 | } 78 | 79 | public static class ProgramDerivedAddress { 80 | private PublicKey address; 81 | private int nonce; 82 | 83 | public ProgramDerivedAddress(PublicKey address, int nonce) { 84 | this.address = address; 85 | this.nonce = nonce; 86 | } 87 | 88 | public PublicKey getAddress() { 89 | return address; 90 | } 91 | 92 | public int getNonce() { 93 | return nonce; 94 | } 95 | 96 | } 97 | 98 | public static ProgramDerivedAddress findProgramAddress(List seeds, PublicKey programId) throws Exception { 99 | int nonce = 255; 100 | PublicKey address; 101 | 102 | List seedsWithNonce = new ArrayList(); 103 | seedsWithNonce.addAll(seeds); 104 | 105 | while (nonce != 0) { 106 | try { 107 | seedsWithNonce.add(new byte[] { (byte) nonce }); 108 | address = createProgramAddress(seedsWithNonce, programId); 109 | } catch (Exception e) { 110 | seedsWithNonce.remove(seedsWithNonce.size() - 1); 111 | nonce--; 112 | continue; 113 | } 114 | 115 | return new ProgramDerivedAddress(address, nonce); 116 | } 117 | 118 | throw new Exception("Unable to find a viable program address nonce"); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/core/Sysvar.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | public class Sysvar { 4 | public static final PublicKey SYSVAR_CLOCK_ADDRESS = new PublicKey("SysvarC1ock11111111111111111111111111111111"); 5 | public static final PublicKey SYSVAR_EPOCH_SCHEDULE_ADDRESS = new PublicKey("SysvarEpochSchedu1e111111111111111111111111"); 6 | public static final PublicKey SYSVAR_INSTRUCTIONS_ADDRESS = new PublicKey("Sysvar1nstructions1111111111111111111111111"); 7 | public static final PublicKey SYSVAR_RECENT_BLOCKHASHES_ADDRESS = new PublicKey("SysvarRecentB1ockHashes11111111111111111111"); 8 | public static final PublicKey SYSVAR_REWARDS_ADDRESS = new PublicKey("SysvarRewards111111111111111111111111111111"); 9 | public static final PublicKey SYSVAR_SLOT_HASHES_ADDRESS = new PublicKey("SysvarS1otHashes111111111111111111111111111"); 10 | public static final PublicKey SYSVAR_SLOT_HISTORY_ADDRESS = new PublicKey("SysvarS1otHistory11111111111111111111111111"); 11 | public static final PublicKey SYSVAR_STAKE_HISTORY_ADDRESS = new PublicKey("SysvarStakeHistory1111111111111111111111111"); 12 | public static final PublicKey SYSVAR_RENT_ADDRESS = new PublicKey("SysvarRent111111111111111111111111111111111"); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/core/Transaction.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import org.bitcoinj.core.Base58; 9 | import org.p2p.solanaj.utils.ShortvecEncoding; 10 | import org.p2p.solanaj.utils.TweetNaclFast; 11 | 12 | public class Transaction { 13 | 14 | public static final int SIGNATURE_LENGTH = 64; 15 | 16 | private Message messgae; 17 | private List signatures; 18 | private byte[] serializedMessage; 19 | private PublicKey feePayer; 20 | 21 | public Transaction() { 22 | this.messgae = new Message(); 23 | this.signatures = new ArrayList(); 24 | } 25 | 26 | public Transaction addInstruction(TransactionInstruction instruction) { 27 | messgae.addInstruction(instruction); 28 | 29 | return this; 30 | } 31 | 32 | public void setRecentBlockHash(String recentBlockhash) { 33 | messgae.setRecentBlockHash(recentBlockhash); 34 | } 35 | 36 | public void setFeePayer(PublicKey feePayer) { 37 | this.feePayer = feePayer; 38 | } 39 | 40 | public void setFeePayer(String feePayer) { 41 | this.feePayer = new PublicKey(feePayer); 42 | } 43 | 44 | public void sign(Account signer) { 45 | sign(Arrays.asList(signer)); 46 | } 47 | 48 | public void sign(List signers) { 49 | 50 | if (signers.size() == 0) { 51 | throw new IllegalArgumentException("No signers"); 52 | } 53 | 54 | if (feePayer == null) { 55 | feePayer = signers.get(0).getPublicKey(); 56 | } 57 | messgae.setFeePayer(feePayer); 58 | 59 | serializedMessage = messgae.serialize(); 60 | 61 | for (Account signer : signers) { 62 | TweetNaclFast.Signature signatureProvider = new TweetNaclFast.Signature(new byte[0], signer.getSecretKey()); 63 | byte[] signature = signatureProvider.detached(serializedMessage); 64 | 65 | signatures.add(Base58.encode(signature)); 66 | } 67 | } 68 | 69 | public byte[] serialize() { 70 | int signaturesSize = signatures.size(); 71 | byte[] signaturesLength = ShortvecEncoding.encodeLength(signaturesSize); 72 | 73 | ByteBuffer out = ByteBuffer 74 | .allocate(signaturesLength.length + signaturesSize * SIGNATURE_LENGTH + serializedMessage.length); 75 | 76 | out.put(signaturesLength); 77 | 78 | for (String signature : signatures) { 79 | byte[] rawSignature = Base58.decode(signature); 80 | out.put(rawSignature); 81 | } 82 | 83 | out.put(serializedMessage); 84 | 85 | return out.array(); 86 | } 87 | 88 | public String getSignature() { 89 | if (signatures.size() > 0) { 90 | return signatures.get(0); 91 | } 92 | 93 | return null; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/core/TransactionInstruction.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import java.util.List; 4 | 5 | public class TransactionInstruction { 6 | 7 | private List keys; 8 | private PublicKey programId; 9 | private byte[] data; 10 | 11 | public TransactionInstruction(PublicKey programId, List keys, byte[] data) { 12 | this.programId = programId; 13 | this.keys = keys; 14 | this.data = data; 15 | } 16 | 17 | public List getKeys() { 18 | return keys; 19 | } 20 | 21 | public PublicKey getProgramId() { 22 | return programId; 23 | } 24 | 25 | public byte[] getData() { 26 | return data; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/programs/ComputeBudgetProgram.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.programs; 2 | 3 | import org.p2p.solanaj.core.PublicKey; 4 | import org.p2p.solanaj.core.TransactionInstruction; 5 | 6 | import java.util.Collections; 7 | 8 | import static org.p2p.solanaj.utils.ByteUtils.*; 9 | 10 | 11 | /** 12 | * @Author melody 13 | * @Date 2024/3/19 13:41 14 | * @Version 1.0 15 | */ 16 | public class ComputeBudgetProgram { 17 | private static final PublicKey PROGRAM_ID = new PublicKey("ComputeBudget111111111111111111111111111111"); 18 | private static final Integer SET_COMPUTE_UNIT_Limit_INDEX = 2; 19 | private static final Integer SET_COMPUTE_UNIT_PRICE_INDEX = 3; 20 | 21 | public static TransactionInstruction setComputeUnitPrice(long microLamports){ 22 | byte[] data = new byte[1 + 8]; 23 | uint8ToByteArrayLE(SET_COMPUTE_UNIT_PRICE_INDEX, data, 0); 24 | uint64ToByteArrayLE(microLamports, data, 1); 25 | return new TransactionInstruction(PROGRAM_ID, Collections.emptyList(), data); 26 | } 27 | 28 | public static TransactionInstruction setComputeUnitLimit(long limit){ 29 | byte[] data = new byte[1 + 4]; 30 | uint8ToByteArrayLE(SET_COMPUTE_UNIT_Limit_INDEX, data, 0); 31 | uint32ToByteArrayLE(limit, data, 1); 32 | return new TransactionInstruction(PROGRAM_ID, Collections.emptyList(), data); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/programs/StakeProgram.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.programs; 2 | 3 | import org.p2p.solanaj.core.AccountMeta; 4 | import org.p2p.solanaj.core.Lockup; 5 | import org.p2p.solanaj.core.PublicKey; 6 | import org.p2p.solanaj.core.TransactionInstruction; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | import static org.p2p.solanaj.utils.ByteUtils.uint32ToByteArrayLE; 12 | import static org.p2p.solanaj.utils.ByteUtils.uint64ToByteArrayLE; 13 | 14 | 15 | /** 16 | * @author dongyuhao 17 | */ 18 | public class StakeProgram { 19 | public static final Long SPACE = 200L; 20 | private static final Integer INITIALIZE = 0; 21 | private static final Integer DELEGATE = 2; 22 | private static final Integer SPLIT = 3; 23 | private static final Integer WITHDRAW = 4; 24 | private static final Integer DEACTIVATE = 5; 25 | private static final Integer MERGE = 7; 26 | 27 | public static final PublicKey PROGRAM_ID = new PublicKey("Stake11111111111111111111111111111111111111"); 28 | public static final PublicKey SYSVAR_CLOCK_PUBKEY = new PublicKey("SysvarC1ock11111111111111111111111111111111"); 29 | public static final PublicKey SYSVAR_STAKE_HISTORY_PUBKEY = new PublicKey("SysvarStakeHistory1111111111111111111111111"); 30 | public static final PublicKey STAKE_CONFIG_ID = new PublicKey("StakeConfig11111111111111111111111111111111"); 31 | 32 | 33 | private static TransactionInstruction initialize(PublicKey stakePubkey, PublicKey staker, PublicKey withdrawer, Lockup lockup) { 34 | byte[] data = new byte[116]; 35 | uint32ToByteArrayLE(INITIALIZE, data, 0); 36 | System.arraycopy(staker.toByteArray(), 0, data, 4, 32); 37 | System.arraycopy(withdrawer.toByteArray(), 0, data, 36, 32); 38 | uint64ToByteArrayLE(lockup.getUnixTimestamp(), data, 68); 39 | uint64ToByteArrayLE(lockup.getEpoch(), data, 76); 40 | System.arraycopy(lockup.getCustodian().toByteArray(), 0, data, 84, 32); 41 | 42 | List keys = new ArrayList<>(); 43 | keys.add(new AccountMeta(stakePubkey, false, true)); 44 | keys.add(new AccountMeta(SystemProgram.SYSVAR_RENT_PUBKEY, false, false)); 45 | return new TransactionInstruction(PROGRAM_ID, keys, data); 46 | } 47 | 48 | public static TransactionInstruction initialize(String stakePubkey, String staker, String withdrawer, Lockup lockup) { 49 | return initialize(new PublicKey(stakePubkey), new PublicKey(staker), new PublicKey(withdrawer), lockup); 50 | } 51 | 52 | private static TransactionInstruction delegate(PublicKey stakePubkey, PublicKey authorizedPubkey, PublicKey votePubkey) { 53 | byte[] data = new byte[4]; 54 | uint32ToByteArrayLE(DELEGATE, data, 0); 55 | List keys = new ArrayList<>(); 56 | keys.add(new AccountMeta(stakePubkey, false, true)); 57 | keys.add(new AccountMeta(votePubkey, false, false)); 58 | keys.add(new AccountMeta(SYSVAR_CLOCK_PUBKEY, false, false)); 59 | keys.add(new AccountMeta(SYSVAR_STAKE_HISTORY_PUBKEY, false, false)); 60 | keys.add(new AccountMeta(STAKE_CONFIG_ID, false, false)); 61 | keys.add(new AccountMeta(authorizedPubkey, true, false)); 62 | return new TransactionInstruction(PROGRAM_ID, keys, data); 63 | } 64 | 65 | public static TransactionInstruction delegate(String stakePubkey, String authorizedPubkey, String votePubkey) { 66 | return delegate(new PublicKey(stakePubkey), new PublicKey(authorizedPubkey), new PublicKey(votePubkey)); 67 | } 68 | 69 | private static TransactionInstruction deactivate(PublicKey stakePubkey, PublicKey authorizedPubkey) { 70 | byte[] data = new byte[4]; 71 | uint32ToByteArrayLE(DEACTIVATE, data, 0); 72 | List keys = new ArrayList<>(); 73 | keys.add(new AccountMeta(stakePubkey, false, true)); 74 | keys.add(new AccountMeta(SYSVAR_CLOCK_PUBKEY, false, false)); 75 | keys.add(new AccountMeta(authorizedPubkey, true, false)); 76 | return new TransactionInstruction(PROGRAM_ID, keys, data); 77 | } 78 | 79 | public static TransactionInstruction deactivate(String stakePubkey, String authorizedPubkey) { 80 | return deactivate(new PublicKey(stakePubkey), new PublicKey(authorizedPubkey)); 81 | } 82 | 83 | private static TransactionInstruction withdraw(PublicKey stakePubkey, PublicKey authorizedPubkey, PublicKey to, Long lamports) { 84 | byte[] data = new byte[4 + 8]; 85 | uint32ToByteArrayLE(WITHDRAW, data, 0); 86 | uint64ToByteArrayLE(lamports, data, 4); 87 | List keys = new ArrayList<>(); 88 | keys.add(new AccountMeta(stakePubkey, false, true)); 89 | keys.add(new AccountMeta(to, false, true)); 90 | keys.add(new AccountMeta(SYSVAR_CLOCK_PUBKEY, false, false)); 91 | keys.add(new AccountMeta(SYSVAR_STAKE_HISTORY_PUBKEY, false, false)); 92 | keys.add(new AccountMeta(authorizedPubkey, true, false)); 93 | return new TransactionInstruction(PROGRAM_ID, keys, data); 94 | } 95 | 96 | public static TransactionInstruction withdraw(String stakePubkey, String authorizedPubkey, String to, Long lamports) { 97 | return withdraw(new PublicKey(stakePubkey), new PublicKey(authorizedPubkey), new PublicKey(to), lamports); 98 | } 99 | 100 | private static TransactionInstruction split(PublicKey stakePubkey, PublicKey authorizedPubkey, PublicKey splitStakePubkey, Long lamports) { 101 | byte[] data = new byte[4 + 8]; 102 | uint32ToByteArrayLE(SPLIT, data, 0); 103 | uint64ToByteArrayLE(lamports, data, 4); 104 | List keys = new ArrayList<>(); 105 | keys.add(new AccountMeta(stakePubkey, false, true)); 106 | keys.add(new AccountMeta(splitStakePubkey, false, true)); 107 | keys.add(new AccountMeta(authorizedPubkey, true, false)); 108 | return new TransactionInstruction(PROGRAM_ID, keys, data); 109 | } 110 | 111 | public static TransactionInstruction split(String stakePubkey, String authorizedPubkey, String splitStakePubkey, Long lamports) { 112 | return split(new PublicKey(stakePubkey), new PublicKey(authorizedPubkey), new PublicKey(splitStakePubkey), lamports); 113 | } 114 | 115 | private static TransactionInstruction merge(PublicKey stakePubkey, PublicKey sourceStakePubKey, PublicKey authorizedPubkey){ 116 | byte[] data = new byte[4]; 117 | uint32ToByteArrayLE(MERGE, data, 0); 118 | List keys = new ArrayList<>(); 119 | keys.add(new AccountMeta(stakePubkey, false, true)); 120 | keys.add(new AccountMeta(sourceStakePubKey, false, true)); 121 | keys.add(new AccountMeta(SYSVAR_CLOCK_PUBKEY, false, false)); 122 | keys.add(new AccountMeta(SYSVAR_STAKE_HISTORY_PUBKEY, false, false)); 123 | keys.add(new AccountMeta(authorizedPubkey, true, false)); 124 | return new TransactionInstruction(PROGRAM_ID, keys, data); 125 | } 126 | 127 | public static TransactionInstruction merge(String stakePubkey, String sourceStakePubKey, String authorizedPubkey){ 128 | return merge(new PublicKey(stakePubkey),new PublicKey(sourceStakePubKey),new PublicKey(authorizedPubkey)); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/programs/SystemProgram.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.programs; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.p2p.solanaj.core.PublicKey; 7 | import org.p2p.solanaj.core.TransactionInstruction; 8 | import org.p2p.solanaj.core.AccountMeta; 9 | 10 | import static org.bitcoinj.core.Utils.*; 11 | import static org.p2p.solanaj.utils.ByteUtils.uint64ToByteArrayLE; 12 | 13 | public class SystemProgram { 14 | public static final PublicKey PROGRAM_ID = new PublicKey("11111111111111111111111111111111"); 15 | 16 | public static final int PROGRAM_INDEX_CREATE_ACCOUNT = 0; 17 | public static final int ASSIGN = 1; 18 | public static final int PROGRAM_INDEX_TRANSFER = 2; 19 | public static final int ADVANCE_NONCE_ACCOUNT = 4; 20 | public static final int INITIALIZE_NONCE_ACCOUNT = 6; 21 | public static final PublicKey SYSVAR_RECENT_BLOCKHASHES_PUBKEY = new PublicKey("SysvarRecentB1ockHashes11111111111111111111"); 22 | public static final PublicKey SYSVAR_RENT_PUBKEY = new PublicKey("SysvarRent111111111111111111111111111111111"); 23 | 24 | public static final Long SPACE = 50L; 25 | 26 | public static TransactionInstruction transfer(PublicKey fromPublicKey, PublicKey toPublickKey, long lamports) { 27 | List keys = new ArrayList<>(); 28 | keys.add(new AccountMeta(fromPublicKey, true, true)); 29 | keys.add(new AccountMeta(toPublickKey, false, true)); 30 | 31 | // 4 byte instruction index + 8 bytes lamports 32 | byte[] data = new byte[4 + 8]; 33 | uint32ToByteArrayLE(PROGRAM_INDEX_TRANSFER, data, 0); 34 | uint64ToByteArrayLE(lamports, data, 4); 35 | 36 | return new TransactionInstruction(PROGRAM_ID, keys, data); 37 | } 38 | 39 | public static TransactionInstruction transfer(String fromPublicKey, String toPublickKey, long lamports) { 40 | return transfer(new PublicKey(fromPublicKey), new PublicKey(toPublickKey), lamports); 41 | } 42 | 43 | public static TransactionInstruction createAccount(PublicKey fromPublicKey, PublicKey newAccountPublikkey, 44 | long lamports, long space, PublicKey programId) { 45 | List keys = new ArrayList<>(); 46 | keys.add(new AccountMeta(fromPublicKey, true, true)); 47 | keys.add(new AccountMeta(newAccountPublikkey, true, true)); 48 | 49 | byte[] data = new byte[4 + 8 + 8 + 32]; 50 | uint32ToByteArrayLE(PROGRAM_INDEX_CREATE_ACCOUNT, data, 0); 51 | uint64ToByteArrayLE(lamports, data, 4); 52 | uint64ToByteArrayLE(space, data, 12); 53 | System.arraycopy(programId.toByteArray(), 0, data, 20, 32); 54 | 55 | return new TransactionInstruction(PROGRAM_ID, keys, data); 56 | } 57 | 58 | public static TransactionInstruction createAccount(String fromPublicKey, String newAccountPublikkey, 59 | long lamports, long space, PublicKey programId) { 60 | return createAccount(new PublicKey(fromPublicKey), new PublicKey(newAccountPublikkey), lamports, space, programId); 61 | } 62 | 63 | public static TransactionInstruction nonceInitialize(PublicKey noncePubkey, PublicKey authorizedPubkey) { 64 | List keys = new ArrayList<>(); 65 | keys.add(new AccountMeta(noncePubkey, false, true)); 66 | keys.add(new AccountMeta(SYSVAR_RECENT_BLOCKHASHES_PUBKEY, false, false)); 67 | keys.add(new AccountMeta(SYSVAR_RENT_PUBKEY, false, false)); 68 | 69 | byte[] data = new byte[4 + 32]; 70 | uint32ToByteArrayLE(INITIALIZE_NONCE_ACCOUNT, data, 0); 71 | System.arraycopy(authorizedPubkey.toByteArray(), 0, data, 4, 32); 72 | return new TransactionInstruction(PROGRAM_ID, keys, data); 73 | } 74 | 75 | public static TransactionInstruction nonceInitialize(String noncePubkey, String authorizedPubkey) { 76 | return nonceInitialize(new PublicKey(noncePubkey), new PublicKey(authorizedPubkey)); 77 | } 78 | 79 | 80 | private static TransactionInstruction nonceAdvance(PublicKey nonceAccountPublicKey, PublicKey authorizedPubkey) { 81 | List keys = new ArrayList<>(); 82 | byte[] data = new byte[4]; 83 | uint32ToByteArrayLE(ADVANCE_NONCE_ACCOUNT, data, 0); 84 | keys.add(new AccountMeta(nonceAccountPublicKey, false, true)); 85 | keys.add(new AccountMeta(SYSVAR_RECENT_BLOCKHASHES_PUBKEY, false, false)); 86 | keys.add(new AccountMeta(authorizedPubkey, true, false)); 87 | return new TransactionInstruction(PROGRAM_ID, keys, data); 88 | } 89 | 90 | public static TransactionInstruction nonceAdvance(String nonceAccountPublicKey, String authorizedPubkey) { 91 | return nonceAdvance(new PublicKey(nonceAccountPublicKey), new PublicKey(authorizedPubkey)); 92 | } 93 | 94 | public static TransactionInstruction assign(PublicKey accountPubkey, PublicKey programId) { 95 | List keys = new ArrayList<>(); 96 | byte[] data = new byte[36]; 97 | uint32ToByteArrayLE(ASSIGN, data, 0); 98 | System.arraycopy(programId.toByteArray(), 0, data, 4, 32); 99 | keys.add(new AccountMeta(accountPubkey, true, true)); 100 | return new TransactionInstruction(PROGRAM_ID, keys, data); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/programs/TokenProgram.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.programs; 2 | 3 | import org.p2p.solanaj.core.AccountMeta; 4 | import org.p2p.solanaj.core.PublicKey; 5 | import org.p2p.solanaj.core.TransactionInstruction; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | import static org.p2p.solanaj.utils.ByteUtils.uint64ToByteArrayLE; 12 | import static org.p2p.solanaj.utils.ByteUtils.uint8ToByteArrayLE; 13 | 14 | 15 | public class TokenProgram { 16 | public static PublicKey ASSOCIATED_TOKEN_PROGRAM_ID = new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); 17 | public static PublicKey TOKEN_PROGRAM_ID = new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); 18 | public static PublicKey TOKEN_2022_PROGRAM_ID = new PublicKey("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"); 19 | 20 | public static String getAssociatedTokenAddress(PublicKey programId, String mint, String owner) throws Exception { 21 | return getAssociatedTokenAddress(programId,new PublicKey(mint), new PublicKey(owner)); 22 | } 23 | 24 | private static String getAssociatedTokenAddress(PublicKey programId,PublicKey mint, PublicKey owner) throws Exception { 25 | return PublicKey.findProgramAddress(Arrays.asList(owner.toByteArray(), programId.toByteArray(), mint.toByteArray()), ASSOCIATED_TOKEN_PROGRAM_ID).getAddress().toBase58(); 26 | } 27 | 28 | private static TransactionInstruction createAssociatedTokenAccount(PublicKey prigramId, PublicKey mint, PublicKey associatedAccount, PublicKey owner, PublicKey payer) { 29 | byte[] data = new byte[0]; 30 | List keys = new ArrayList<>(); 31 | keys.add(new AccountMeta(payer, true, true)); 32 | keys.add(new AccountMeta(associatedAccount, false, true)); 33 | keys.add(new AccountMeta(owner, false, false)); 34 | keys.add(new AccountMeta(mint, false, false)); 35 | keys.add(new AccountMeta(SystemProgram.PROGRAM_ID, false, false)); 36 | keys.add(new AccountMeta(prigramId, false, false)); 37 | keys.add(new AccountMeta(SystemProgram.SYSVAR_RENT_PUBKEY, false, false)); 38 | return new TransactionInstruction(ASSOCIATED_TOKEN_PROGRAM_ID, keys, data); 39 | } 40 | 41 | public static TransactionInstruction createAssociatedTokenAccount(PublicKey programId, String mint, String associatedAccount, String owner, String payer) { 42 | return createAssociatedTokenAccount( 43 | programId, 44 | new PublicKey(mint), 45 | new PublicKey(associatedAccount), 46 | new PublicKey(owner), 47 | new PublicKey(payer) 48 | ); 49 | } 50 | 51 | 52 | private static TransactionInstruction createTransferChecked(PublicKey programId, PublicKey source, PublicKey mint, PublicKey destination, PublicKey owner, List multiSigners, Long amount, Integer decimals) { 53 | // 1 byte instruction index + 8 bytes amount + 1 bytes decimals 54 | byte[] data = new byte[1 + 8 + 1]; 55 | uint8ToByteArrayLE(12, data, 0); 56 | uint64ToByteArrayLE(amount, data, 1); 57 | uint8ToByteArrayLE(decimals, data, 9); 58 | List keys = new ArrayList<>(); 59 | keys.add(new AccountMeta(source, false, true)); 60 | keys.add(new AccountMeta(mint, false, false)); 61 | keys.add(new AccountMeta(destination, false, true)); 62 | if (multiSigners == null || multiSigners.size() == 0) { 63 | keys.add(new AccountMeta(owner, true, false)); 64 | } else { 65 | keys.add(new AccountMeta(owner, false, false)); 66 | for (String signer : multiSigners) { 67 | keys.add(new AccountMeta(new PublicKey(signer), true, false)); 68 | } 69 | } 70 | return new TransactionInstruction(programId, keys, data); 71 | } 72 | 73 | public static TransactionInstruction createTransferChecked(PublicKey programId, String source, String mint, String destination, String owner, List multiSigners, Long amount, Integer decimals) { 74 | return createTransferChecked(programId,new PublicKey(source), new PublicKey(mint), new PublicKey(destination), new PublicKey(owner), multiSigners, amount, decimals); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/Cluster.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc; 2 | 3 | public enum Cluster { 4 | DEVNET("https://devnet.solana.com"), 5 | TESTNET("https://testnet.solana.com"), 6 | MAINNET("https://api.mainnet-beta.solana.com"); 7 | 8 | private String endpoint; 9 | 10 | Cluster(String endpoint) { 11 | this.endpoint = endpoint; 12 | } 13 | 14 | public String getEndpoint() { 15 | return endpoint; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/RpcApi.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc; 2 | 3 | import java.util.*; 4 | 5 | import org.p2p.solanaj.core.Account; 6 | import org.p2p.solanaj.core.PublicKey; 7 | import org.p2p.solanaj.core.Transaction; 8 | import org.p2p.solanaj.rpc.types.ConfigObjects.*; 9 | import org.p2p.solanaj.rpc.types.AccountInfo; 10 | import org.p2p.solanaj.rpc.types.ConfirmedTransaction; 11 | import org.p2p.solanaj.rpc.types.ProgramAccount; 12 | import org.p2p.solanaj.rpc.types.RecentBlockhash; 13 | import org.p2p.solanaj.rpc.types.RpcSendTransactionConfig; 14 | import org.p2p.solanaj.rpc.types.SignatureInformation; 15 | import org.p2p.solanaj.rpc.types.RpcResultTypes.ValueLong; 16 | import org.p2p.solanaj.rpc.types.RpcSendTransactionConfig.Encoding; 17 | import org.p2p.solanaj.ws.SubscriptionWebSocketClient; 18 | import org.p2p.solanaj.ws.listeners.NotificationEventListener; 19 | 20 | public class RpcApi { 21 | private RpcClient client; 22 | 23 | public RpcApi(RpcClient client) { 24 | this.client = client; 25 | } 26 | 27 | public String getRecentBlockhash() throws RpcException { 28 | return client.call("getRecentBlockhash", null, RecentBlockhash.class).getRecentBlockhash(); 29 | } 30 | 31 | public String sendTransaction(Transaction transaction, Account signer) throws RpcException { 32 | return sendTransaction(transaction, Arrays.asList(signer)); 33 | } 34 | 35 | public String sendTransaction(Transaction transaction, List signers) throws RpcException { 36 | String recentBlockhash = getRecentBlockhash(); 37 | transaction.setRecentBlockHash(recentBlockhash); 38 | transaction.sign(signers); 39 | byte[] serializedTransaction = transaction.serialize(); 40 | 41 | String base64Trx = Base64.getEncoder().encodeToString(serializedTransaction); 42 | 43 | List params = new ArrayList(); 44 | 45 | params.add(base64Trx); 46 | params.add(new RpcSendTransactionConfig()); 47 | 48 | return client.call("sendTransaction", params, String.class); 49 | } 50 | 51 | public String sendTransaction(Transaction transaction) throws RpcException { 52 | byte[] serializedTransaction = transaction.serialize(); 53 | 54 | String base64Trx = Base64.getEncoder().encodeToString(serializedTransaction); 55 | 56 | List params = new ArrayList(); 57 | 58 | params.add(base64Trx); 59 | params.add(new RpcSendTransactionConfig()); 60 | 61 | return client.call("sendTransaction", params, String.class); 62 | } 63 | 64 | public void sendAndConfirmTransaction(Transaction transaction, List signers, 65 | NotificationEventListener listener) throws RpcException { 66 | String signature = sendTransaction(transaction, signers); 67 | 68 | SubscriptionWebSocketClient subClient = SubscriptionWebSocketClient.getInstance(client.getEndpoint()); 69 | subClient.signatureSubscribe(signature, listener); 70 | } 71 | 72 | public long getBalance(PublicKey account) throws RpcException { 73 | List params = new ArrayList(); 74 | 75 | params.add(account.toString()); 76 | 77 | return client.call("getBalance", params, ValueLong.class).getValue(); 78 | } 79 | 80 | public ConfirmedTransaction getConfirmedTransaction(String signature) throws RpcException { 81 | List params = new ArrayList(); 82 | 83 | params.add(signature); 84 | // TODO jsonParsed, base58, base64 85 | // the default encoding is JSON 86 | // params.add("json"); 87 | 88 | return client.call("getConfirmedTransaction", params, ConfirmedTransaction.class); 89 | } 90 | 91 | @SuppressWarnings({ "unchecked", "rawtypes" }) 92 | public List getConfirmedSignaturesForAddress2(PublicKey account, int limit) 93 | throws RpcException { 94 | List params = new ArrayList(); 95 | 96 | params.add(account.toString()); 97 | params.add(new ConfirmedSignFAddr2(limit)); 98 | 99 | List rawResult = client.call("getConfirmedSignaturesForAddress2", params, List.class); 100 | 101 | List result = new ArrayList(); 102 | for (AbstractMap item : rawResult) { 103 | result.add(new SignatureInformation(item)); 104 | } 105 | 106 | return result; 107 | } 108 | 109 | public List getProgramAccounts(PublicKey account, long offset, String bytes) throws RpcException { 110 | List filters = new ArrayList(); 111 | filters.add(new Filter(new Memcmp(offset, bytes))); 112 | 113 | ProgramAccountConfig programAccountConfig = new ProgramAccountConfig(filters); 114 | return getProgramAccounts(account, programAccountConfig); 115 | } 116 | 117 | public List getProgramAccounts(PublicKey account) throws RpcException { 118 | return getProgramAccounts(account, new ProgramAccountConfig(Encoding.base64)); 119 | } 120 | 121 | @SuppressWarnings({ "unchecked", "rawtypes" }) 122 | public List getProgramAccounts(PublicKey account, ProgramAccountConfig programAccountConfig) 123 | throws RpcException { 124 | List params = new ArrayList(); 125 | 126 | params.add(account.toString()); 127 | 128 | if (programAccountConfig != null) { 129 | params.add(programAccountConfig); 130 | } 131 | 132 | List rawResult = client.call("getProgramAccounts", params, List.class); 133 | 134 | List result = new ArrayList(); 135 | for (AbstractMap item : rawResult) { 136 | result.add(new ProgramAccount(item)); 137 | } 138 | 139 | return result; 140 | } 141 | 142 | public AccountInfo getAccountInfo(PublicKey account) throws RpcException { 143 | List params = new ArrayList(); 144 | 145 | params.add(account.toString()); 146 | params.add(new RpcSendTransactionConfig()); 147 | 148 | return client.call("getAccountInfo", params, AccountInfo.class); 149 | } 150 | 151 | public long getMinimumBalanceForRentExemption(long dataLength) throws RpcException { 152 | List params = new ArrayList(); 153 | 154 | params.add(dataLength); 155 | 156 | return client.call("getMinimumBalanceForRentExemption", params, Long.class); 157 | } 158 | 159 | public long getBlockTime(long block) throws RpcException { 160 | List params = new ArrayList(); 161 | 162 | params.add(block); 163 | 164 | return client.call("getBlockTime", params, Long.class); 165 | } 166 | 167 | public String requestAirdrop(PublicKey address, long lamports) throws RpcException { 168 | List params = new ArrayList(); 169 | 170 | params.add(address.toString()); 171 | params.add(lamports); 172 | 173 | return client.call("requestAirdrop", params, String.class); 174 | } 175 | 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/RpcClient.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc; 2 | 3 | import okhttp3.MediaType; 4 | import okhttp3.OkHttpClient; 5 | import okhttp3.Request; 6 | import okhttp3.RequestBody; 7 | import okhttp3.Response; 8 | 9 | import java.io.IOException; 10 | import java.lang.reflect.Type; 11 | import java.util.List; 12 | 13 | import com.squareup.moshi.JsonAdapter; 14 | import com.squareup.moshi.Moshi; 15 | import com.squareup.moshi.Types; 16 | 17 | import org.p2p.solanaj.rpc.types.RpcRequest; 18 | import org.p2p.solanaj.rpc.types.RpcResponse; 19 | 20 | public class RpcClient { 21 | private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); 22 | 23 | private String endpoint; 24 | private OkHttpClient httpClient = new OkHttpClient(); 25 | private RpcApi rpcApi; 26 | 27 | public RpcClient(Cluster endpoint) { 28 | this(endpoint.getEndpoint()); 29 | } 30 | 31 | public RpcClient(String endpoint) { 32 | this.endpoint = endpoint; 33 | rpcApi = new RpcApi(this); 34 | } 35 | 36 | public T call(String method, List params, Class clazz) throws RpcException { 37 | RpcRequest rpcRequest = new RpcRequest(method, params); 38 | 39 | JsonAdapter rpcRequestJsonAdapter = new Moshi.Builder().build().adapter(RpcRequest.class); 40 | JsonAdapter> resultAdapter = new Moshi.Builder().build() 41 | .adapter(Types.newParameterizedType(RpcResponse.class, Type.class.cast(clazz))); 42 | 43 | Request request = new Request.Builder().url(endpoint) 44 | .post(RequestBody.create(rpcRequestJsonAdapter.toJson(rpcRequest), JSON)).build(); 45 | 46 | try { 47 | Response response = httpClient.newCall(request).execute(); 48 | RpcResponse rpcResult = resultAdapter.fromJson(response.body().string()); 49 | 50 | if (rpcResult.getError() != null) { 51 | throw new RpcException(rpcResult.getError().getMessage()); 52 | } 53 | 54 | return (T) rpcResult.getResult(); 55 | } catch (IOException e) { 56 | throw new RpcException(e.getMessage()); 57 | } 58 | } 59 | 60 | public RpcApi getApi() { 61 | return rpcApi; 62 | } 63 | 64 | public String getEndpoint() { 65 | return endpoint; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/RpcException.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc; 2 | 3 | public class RpcException extends Exception { 4 | private final static long serialVersionUID = 8315999767009642193L; 5 | 6 | public RpcException(String message) { 7 | super(message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/AccountInfo.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import java.util.List; 4 | 5 | import com.squareup.moshi.Json; 6 | 7 | public class AccountInfo extends RpcResultObject { 8 | 9 | public static class Value { 10 | @Json(name = "data") 11 | private List data = null; 12 | @Json(name = "executable") 13 | private boolean executable; 14 | @Json(name = "lamports") 15 | private long lamports; 16 | @Json(name = "owner") 17 | private String owner; 18 | @Json(name = "rentEpoch") 19 | private long rentEpoch; 20 | 21 | public List getData() { 22 | return data; 23 | } 24 | 25 | public boolean isExecutable() { 26 | return executable; 27 | } 28 | 29 | public long getLamports() { 30 | return lamports; 31 | } 32 | 33 | public String getOwner() { 34 | return owner; 35 | } 36 | 37 | public long getRentEpoch() { 38 | return rentEpoch; 39 | } 40 | 41 | } 42 | 43 | @Json(name = "value") 44 | private Value value; 45 | 46 | public Value getValue() { 47 | return value; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/ConfigObjects.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import java.util.List; 4 | 5 | import com.squareup.moshi.Json; 6 | 7 | import org.p2p.solanaj.rpc.types.RpcSendTransactionConfig.Encoding; 8 | 9 | public class ConfigObjects { 10 | 11 | public static class ConfirmedSignFAddr2 { 12 | @Json(name = "limit") 13 | private long limit; 14 | @Json(name = "before") 15 | private String before; 16 | @Json(name = "until") 17 | private String until; 18 | 19 | public ConfirmedSignFAddr2(int limit) { 20 | this.limit = limit; 21 | } 22 | } 23 | 24 | public static class Memcmp { 25 | @Json(name = "offset") 26 | private long offset; 27 | @Json(name = "bytes") 28 | private String bytes; 29 | 30 | public Memcmp() { 31 | } 32 | 33 | public Memcmp(long offset, String bytes) { 34 | this.offset = offset; 35 | this.bytes = bytes; 36 | } 37 | 38 | } 39 | 40 | public static class Filter { 41 | @Json(name = "memcmp") 42 | private Memcmp memcmp; 43 | 44 | public Filter() { 45 | } 46 | 47 | public Filter(Memcmp memcmp) { 48 | this.memcmp = memcmp; 49 | } 50 | 51 | } 52 | 53 | public static class ProgramAccountConfig { 54 | @Json(name = "encoding") 55 | private Encoding encoding = null; 56 | @Json(name = "filters") 57 | private List filters = null; 58 | 59 | public ProgramAccountConfig() { 60 | } 61 | 62 | public ProgramAccountConfig(List filters) { 63 | this.filters = filters; 64 | } 65 | 66 | public ProgramAccountConfig(Encoding encoding) { 67 | this.encoding = encoding; 68 | } 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/ConfirmedTransaction.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import java.util.List; 4 | 5 | import com.squareup.moshi.Json; 6 | 7 | public class ConfirmedTransaction { 8 | 9 | public static class Header { 10 | 11 | @Json(name = "numReadonlySignedAccounts") 12 | private long numReadonlySignedAccounts; 13 | @Json(name = "numReadonlyUnsignedAccounts") 14 | private long numReadonlyUnsignedAccounts; 15 | @Json(name = "numRequiredSignatures") 16 | private long numRequiredSignatures; 17 | 18 | public long getNumReadonlySignedAccounts() { 19 | return numReadonlySignedAccounts; 20 | } 21 | 22 | public long getNumReadonlyUnsignedAccounts() { 23 | return numReadonlyUnsignedAccounts; 24 | } 25 | 26 | public long getNumRequiredSignatures() { 27 | return numRequiredSignatures; 28 | } 29 | 30 | } 31 | 32 | public static class Instruction { 33 | 34 | @Json(name = "accounts") 35 | private List accounts = null; 36 | @Json(name = "data") 37 | private String data; 38 | @Json(name = "programIdIndex") 39 | private long programIdIndex; 40 | 41 | public List getAccounts() { 42 | return accounts; 43 | } 44 | 45 | public String getData() { 46 | return data; 47 | } 48 | 49 | public long getProgramIdIndex() { 50 | return programIdIndex; 51 | } 52 | 53 | } 54 | 55 | public static class Message { 56 | 57 | @Json(name = "accountKeys") 58 | private List accountKeys = null; 59 | @Json(name = "header") 60 | private Header header; 61 | @Json(name = "instructions") 62 | private List instructions = null; 63 | @Json(name = "recentBlockhash") 64 | private String recentBlockhash; 65 | 66 | public List getAccountKeys() { 67 | return accountKeys; 68 | } 69 | 70 | public Header getHeader() { 71 | return header; 72 | } 73 | 74 | public List getInstructions() { 75 | return instructions; 76 | } 77 | 78 | public String getRecentBlockhash() { 79 | return recentBlockhash; 80 | } 81 | 82 | } 83 | 84 | public static class Status { 85 | 86 | @Json(name = "Ok") 87 | private Object ok; 88 | 89 | public Object getOk() { 90 | return ok; 91 | } 92 | 93 | } 94 | 95 | public static class Meta { 96 | 97 | @Json(name = "err") 98 | private Object err; 99 | @Json(name = "fee") 100 | private long fee; 101 | @Json(name = "innerInstructions") 102 | private List innerInstructions = null; 103 | @Json(name = "postBalances") 104 | private List postBalances = null; 105 | @Json(name = "preBalances") 106 | private List preBalances = null; 107 | @Json(name = "status") 108 | private Status status; 109 | 110 | public Object getErr() { 111 | return err; 112 | } 113 | 114 | public long getFee() { 115 | return fee; 116 | } 117 | 118 | public List getInnerInstructions() { 119 | return innerInstructions; 120 | } 121 | 122 | public List getPostBalances() { 123 | return postBalances; 124 | } 125 | 126 | public List getPreBalances() { 127 | return preBalances; 128 | } 129 | 130 | public Status getStatus() { 131 | return status; 132 | } 133 | 134 | } 135 | 136 | public static class Transaction { 137 | 138 | @Json(name = "message") 139 | private Message message; 140 | @Json(name = "signatures") 141 | private List signatures = null; 142 | 143 | public Message getMessage() { 144 | return message; 145 | } 146 | 147 | public List getSignatures() { 148 | return signatures; 149 | } 150 | 151 | } 152 | 153 | @Json(name = "meta") 154 | private Meta meta; 155 | @Json(name = "slot") 156 | private long slot; 157 | @Json(name = "transaction") 158 | private Transaction transaction; 159 | 160 | public Meta getMeta() { 161 | return meta; 162 | } 163 | 164 | public long getSlot() { 165 | return slot; 166 | } 167 | 168 | public Transaction getTransaction() { 169 | return transaction; 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/ProgramAccount.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import java.util.AbstractMap; 4 | import java.util.List; 5 | import java.util.Base64; 6 | 7 | import com.squareup.moshi.Json; 8 | 9 | import org.p2p.solanaj.rpc.types.RpcSendTransactionConfig.Encoding; 10 | 11 | import org.bitcoinj.core.Base58; 12 | 13 | public class ProgramAccount { 14 | 15 | public final class Account { 16 | @Json(name = "data") 17 | private String data; 18 | @Json(name = "executable") 19 | private boolean executable; 20 | @Json(name = "lamports") 21 | private double lamports; 22 | @Json(name = "owner") 23 | private String owner; 24 | @Json(name = "rentEpoch") 25 | private double rentEpoch; 26 | 27 | private String encoding; 28 | 29 | @SuppressWarnings({ "rawtypes", "unchecked" }) 30 | public Account(Object acc) { 31 | AbstractMap account = (AbstractMap) acc; 32 | 33 | Object rawData = account.get("data"); 34 | if (rawData instanceof List) { 35 | List dataList = ((List) rawData); 36 | 37 | this.data = dataList.get(0); 38 | this.encoding = (String) dataList.get(1); 39 | } else if (rawData instanceof String) { 40 | this.data = (String) rawData; 41 | } 42 | 43 | this.executable = (boolean) account.get("executable"); 44 | this.lamports = (double) account.get("lamports"); 45 | this.owner = (String) account.get("owner"); 46 | this.rentEpoch = (double) account.get("rentEpoch"); 47 | } 48 | 49 | public String getData() { 50 | return data; 51 | } 52 | 53 | public byte[] getDecodedData() { 54 | if (encoding.equals(Encoding.base64.toString())) { 55 | return Base64.getDecoder().decode(data); 56 | } 57 | 58 | return Base58.decode(data); 59 | } 60 | 61 | public boolean isExecutable() { 62 | return executable; 63 | } 64 | 65 | public double getLamports() { 66 | return lamports; 67 | } 68 | 69 | public String getOwner() { 70 | return owner; 71 | } 72 | 73 | public double getRentEpoch() { 74 | return rentEpoch; 75 | } 76 | 77 | } 78 | 79 | @Json(name = "account") 80 | private Account account; 81 | @Json(name = "pubkey") 82 | private String pubkey; 83 | 84 | public Account getAccount() { 85 | return account; 86 | } 87 | 88 | public String getPubkey() { 89 | return pubkey; 90 | } 91 | 92 | public ProgramAccount() { 93 | } 94 | 95 | @SuppressWarnings({ "rawtypes" }) 96 | public ProgramAccount(AbstractMap pa) { 97 | this.account = (Account) new Account(pa.get("account")); 98 | this.pubkey = (String) pa.get("pubkey"); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/RecentBlockhash.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import com.squareup.moshi.Json; 4 | 5 | public class RecentBlockhash extends RpcResultObject { 6 | public static class FeeCalculator { 7 | 8 | @Json(name = "lamportsPerSignature") 9 | private long lamportsPerSignature; 10 | 11 | public long getLamportsPerSignature() { 12 | return lamportsPerSignature; 13 | } 14 | 15 | } 16 | 17 | public static class Value { 18 | @Json(name = "blockhash") 19 | private String blockhash; 20 | @Json(name = "feeCalculator") 21 | private FeeCalculator feeCalculator; 22 | 23 | public String getBlockhash() { 24 | return blockhash; 25 | } 26 | 27 | public FeeCalculator getFeeCalculator() { 28 | return feeCalculator; 29 | } 30 | 31 | } 32 | 33 | @Json(name = "value") 34 | private Value value; 35 | 36 | public Value getValue() { 37 | return value; 38 | } 39 | 40 | public String getRecentBlockhash() { 41 | return getValue().getBlockhash(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/RpcNotificationResult.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import com.squareup.moshi.Json; 4 | 5 | public class RpcNotificationResult { 6 | 7 | public static class Result extends RpcResultObject { 8 | @Json(name = "value") 9 | private Object value; 10 | 11 | public Object getValue() { 12 | return value; 13 | } 14 | } 15 | 16 | public static class Params { 17 | 18 | @Json(name = "result") 19 | private Result result; 20 | @Json(name = "subscription") 21 | private long subscription; 22 | 23 | public Result getResult() { 24 | return result; 25 | } 26 | 27 | public long getSubscription() { 28 | return subscription; 29 | } 30 | 31 | } 32 | 33 | @Json(name = "jsonrpc") 34 | private String jsonrpc; 35 | @Json(name = "method") 36 | private String method; 37 | @Json(name = "params") 38 | private Params params; 39 | 40 | public String getJsonrpc() { 41 | return jsonrpc; 42 | } 43 | 44 | public String getMethod() { 45 | return method; 46 | } 47 | 48 | public Params getParams() { 49 | return params; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/RpcRequest.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import java.util.List; 4 | import java.util.UUID; 5 | 6 | import com.squareup.moshi.Json; 7 | 8 | public class RpcRequest { 9 | @Json(name = "jsonrpc") 10 | private String jsonrpc = "2.0"; 11 | @Json(name = "method") 12 | private String method; 13 | @Json(name = "params") 14 | private List params = null; 15 | @Json(name = "id") 16 | private String id = UUID.randomUUID().toString(); 17 | 18 | public RpcRequest(String method) { 19 | this(method, null); 20 | } 21 | 22 | public RpcRequest(String method, List params) { 23 | this.method = method; 24 | this.params = params; 25 | } 26 | 27 | public String getMethod() { 28 | return method; 29 | } 30 | 31 | public List getParams() { 32 | return params; 33 | } 34 | 35 | public String getId() { 36 | return id; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/RpcResponse.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import com.squareup.moshi.Json; 4 | 5 | public class RpcResponse { 6 | 7 | public static class Error { 8 | @Json(name = "code") 9 | private long code; 10 | @Json(name = "message") 11 | private String message; 12 | 13 | public long getCode() { 14 | return code; 15 | } 16 | 17 | public String getMessage() { 18 | return message; 19 | } 20 | 21 | } 22 | 23 | @Json(name = "jsonrpc") 24 | private String jsonrpc; 25 | @Json(name = "result") 26 | private T result; 27 | @Json(name = "error") 28 | private Error error; 29 | @Json(name = "id") 30 | private String id; 31 | 32 | public Error getError() { 33 | return error; 34 | } 35 | 36 | public T getResult() { 37 | return result; 38 | } 39 | 40 | public void setResult(T result) { 41 | this.result = result; 42 | } 43 | 44 | public String getId() { 45 | return id; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/RpcResultObject.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import com.squareup.moshi.Json; 4 | 5 | public class RpcResultObject { 6 | public static class Context { 7 | @Json(name = "slot") 8 | private long slot; 9 | 10 | public long getSlot() { 11 | return slot; 12 | } 13 | 14 | } 15 | 16 | @Json(name = "context") 17 | protected Context context; 18 | 19 | public Context gContext() { 20 | return context; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/RpcResultTypes.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import com.squareup.moshi.Json; 4 | 5 | public class RpcResultTypes { 6 | 7 | public static class ValueLong extends RpcResultObject { 8 | @Json(name = "value") 9 | private long value; 10 | 11 | public long getValue() { 12 | return value; 13 | } 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/RpcSendTransactionConfig.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import com.squareup.moshi.Json; 4 | 5 | public class RpcSendTransactionConfig { 6 | 7 | public static enum Encoding { 8 | base64("base64"), 9 | jsonParsed("jsonParsed"); 10 | private String enc; 11 | 12 | Encoding(String enc) { 13 | this.enc = enc; 14 | } 15 | 16 | public String getEncoding() { 17 | return enc; 18 | } 19 | 20 | } 21 | 22 | @Json(name = "encoding") 23 | private Encoding encoding = Encoding.base64; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/rpc/types/SignatureInformation.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.rpc.types; 2 | 3 | import java.util.AbstractMap; 4 | 5 | import com.squareup.moshi.Json; 6 | 7 | public class SignatureInformation { 8 | @Json(name = "err") 9 | private Object err; 10 | @Json(name = "memo") 11 | private Object memo; 12 | @Json(name = "signature") 13 | private String signature; 14 | @Json(name = "slot") 15 | private long slot; 16 | 17 | public SignatureInformation() { 18 | } 19 | 20 | @SuppressWarnings({ "rawtypes" }) 21 | public SignatureInformation(AbstractMap info) { 22 | this.err = info.get("err"); 23 | this.memo = info.get("memo"); 24 | this.signature = (String) info.get("signature"); 25 | this.err = info.get("slot"); 26 | } 27 | 28 | public Object getErr() { 29 | return err; 30 | } 31 | 32 | public Object getMemo() { 33 | return memo; 34 | } 35 | 36 | public String getSignature() { 37 | return signature; 38 | } 39 | 40 | public long getSlot() { 41 | return slot; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/utils/ByteUtils.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.utils; 2 | 3 | import static org.bitcoinj.core.Utils.*; 4 | 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | import java.math.BigInteger; 8 | 9 | public class ByteUtils { 10 | public static final int UINT_32_LENGTH = 4; 11 | public static final int UINT_64_LENGTH = 8; 12 | 13 | public static byte[] readBytes(byte[] buf, int offset, int length) { 14 | byte[] b = new byte[length]; 15 | System.arraycopy(buf, offset, b, 0, length); 16 | return b; 17 | } 18 | 19 | public static BigInteger readUint64(byte[] buf, int offset) { 20 | return new BigInteger(reverseBytes(readBytes(buf, offset, UINT_64_LENGTH))); 21 | } 22 | 23 | public static void uint64ToByteStreamLE(BigInteger val, OutputStream stream) throws IOException { 24 | byte[] bytes = val.toByteArray(); 25 | if (bytes.length > 8) { 26 | if (bytes[0] == 0) { 27 | bytes = readBytes(bytes, 1, bytes.length - 1); 28 | } else { 29 | throw new RuntimeException("Input too large to encode into a uint64"); 30 | } 31 | } 32 | bytes = reverseBytes(bytes); 33 | stream.write(bytes); 34 | if (bytes.length < 8) { 35 | for (int i = 0; i < 8 - bytes.length; i++) 36 | stream.write(0); 37 | } 38 | } 39 | 40 | 41 | public static void uint64ToByteArrayLE(long val, byte[] out, int offset) { 42 | out[offset] = (byte) (0xFF & val); 43 | out[offset + 1] = (byte) (0xFF & (val >> 8)); 44 | out[offset + 2] = (byte) (0xFF & (val >> 16)); 45 | out[offset + 3] = (byte) (0xFF & (val >> 24)); 46 | out[offset + 4] = (byte) (0xFF & (val >> 32)); 47 | out[offset + 5] = (byte) (0xFF & (val >> 40)); 48 | out[offset + 6] = (byte) (0xFF & (val >> 48)); 49 | out[offset + 7] = (byte) (0xFF & (val >> 56)); 50 | } 51 | 52 | public static void uint32ToByteArrayLE(long val, byte[] out, int offset) { 53 | out[offset] = (byte) (0xFF & val); 54 | out[offset + 1] = (byte) (0xFF & (val >> 8)); 55 | out[offset + 2] = (byte) (0xFF & (val >> 16)); 56 | out[offset + 3] = (byte) (0xFF & (val >> 24)); 57 | } 58 | 59 | public static void uint8ToByteArrayLE(long val, byte[] out, int offset) { 60 | out[offset] = (byte) (0xFF & val); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/utils/SerializeUtils.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.utils; 2 | 3 | 4 | import org.bitcoinj.core.Base58; 5 | 6 | import java.nio.ByteBuffer; 7 | import java.util.List; 8 | 9 | import static org.p2p.solanaj.core.Transaction.SIGNATURE_LENGTH; 10 | 11 | /** 12 | * @author dongyuhao 13 | */ 14 | public class SerializeUtils { 15 | public static byte[] serialize(byte[] serializedMessage,List signatures) { 16 | int signaturesSize = signatures.size(); 17 | byte[] signaturesLength = ShortvecEncoding.encodeLength(signaturesSize); 18 | 19 | ByteBuffer out = ByteBuffer 20 | .allocate(signaturesLength.length + signaturesSize * SIGNATURE_LENGTH + serializedMessage.length); 21 | 22 | out.put(signaturesLength); 23 | 24 | for (byte[] signature : signatures) { 25 | out.put(signature); 26 | } 27 | 28 | out.put(serializedMessage); 29 | 30 | return out.array(); 31 | } 32 | 33 | 34 | public static byte[] serialize(byte[] serializedMessage, byte[]... signatures) { 35 | int signaturesSize = signatures.length; 36 | byte[] signaturesLength = ShortvecEncoding.encodeLength(signaturesSize); 37 | 38 | ByteBuffer out = ByteBuffer 39 | .allocate(signaturesLength.length + signaturesSize * SIGNATURE_LENGTH + serializedMessage.length); 40 | 41 | out.put(signaturesLength); 42 | 43 | for (byte[] signature : signatures) { 44 | out.put(signature); 45 | } 46 | 47 | out.put(serializedMessage); 48 | 49 | return out.array(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/utils/ShortvecEncoding.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.utils; 2 | 3 | import static org.bitcoinj.core.Utils.*; 4 | 5 | public class ShortvecEncoding { 6 | 7 | public static byte[] encodeLength(int len) { 8 | byte[] out = new byte[10]; 9 | int remLen = len; 10 | int cursor = 0; 11 | 12 | for (;;) { 13 | int elem = remLen & 0x7f; 14 | remLen >>= 7; 15 | if (remLen == 0) { 16 | uint16ToByteArrayLE(elem, out, cursor); 17 | break; 18 | } else { 19 | elem |= 0x80; 20 | uint16ToByteArrayLE(elem, out, cursor); 21 | cursor += 1; 22 | } 23 | } 24 | 25 | byte[] bytes = new byte[cursor + 1]; 26 | System.arraycopy(out, 0, bytes, 0, cursor + 1); 27 | 28 | return bytes; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/utils/TweetNaclFast.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.utils; 2 | 3 | // https://github.com/InstantWebP2P/tweetnacl-java/blob/master/src/main/java/com/iwebpp/crypto/TweetNaclFast.java 4 | 5 | // Copyright (c) 2014 Tom Zhou 6 | 7 | import java.io.UnsupportedEncodingException; 8 | import java.security.SecureRandom; 9 | import java.lang.System; 10 | import java.util.concurrent.atomic.AtomicLong; 11 | 12 | /* 13 | * @description 14 | * TweetNacl.c Java porting 15 | * */ 16 | public final class TweetNaclFast { 17 | 18 | private final static String TAG = "TweetNaclFast"; 19 | 20 | /* 21 | * @description 22 | * Box algorithm, Public-key authenticated encryption 23 | * */ 24 | public static final class Box { 25 | 26 | private final static String TAG = "Box"; 27 | 28 | private AtomicLong nonce; 29 | 30 | private byte [] theirPublicKey; 31 | private byte [] mySecretKey; 32 | private byte [] sharedKey; 33 | 34 | public Box(byte [] theirPublicKey, byte [] mySecretKey) { 35 | this(theirPublicKey, mySecretKey, 68); 36 | } 37 | 38 | public Box(byte [] theirPublicKey, byte [] mySecretKey, long nonce) { 39 | this.theirPublicKey = theirPublicKey; 40 | this.mySecretKey = mySecretKey; 41 | 42 | this.nonce = new AtomicLong(nonce); 43 | 44 | // generate pre-computed shared key 45 | before(); 46 | } 47 | 48 | public void setNonce(long nonce) { 49 | this.nonce.set(nonce); 50 | } 51 | public long getNonce() { 52 | return this.nonce.get(); 53 | } 54 | public long incrNonce() { 55 | return this.nonce.incrementAndGet(); 56 | } 57 | private byte[] generateNonce() { 58 | // generate nonce 59 | long nonce = this.nonce.get(); 60 | 61 | byte [] n = new byte[nonceLength]; 62 | for (int i = 0; i < nonceLength; i += 8) { 63 | n[i+0] = (byte) (nonce>>> 0); 64 | n[i+1] = (byte) (nonce>>> 8); 65 | n[i+2] = (byte) (nonce>>>16); 66 | n[i+3] = (byte) (nonce>>>24); 67 | n[i+4] = (byte) (nonce>>>32); 68 | n[i+5] = (byte) (nonce>>>40); 69 | n[i+6] = (byte) (nonce>>>48); 70 | n[i+7] = (byte) (nonce>>>56); 71 | } 72 | 73 | return n; 74 | } 75 | 76 | /* 77 | * @description 78 | * Encrypt and authenticates message using peer's public key, 79 | * our secret key, and the given nonce, which must be unique 80 | * for each distinct message for a key pair. 81 | * 82 | * Returns an encrypted and authenticated message, 83 | * which is nacl.box.overheadLength longer than the original message. 84 | * */ 85 | public byte [] box(byte [] message) { 86 | if (message==null) return null; 87 | return box(message, 0, message.length); 88 | } 89 | 90 | public byte [] box(byte [] message, final int moff) { 91 | if (!(message!=null && message.length>moff)) return null; 92 | return box(message, moff, message.length-moff); 93 | } 94 | 95 | public byte [] box(byte [] message, final int moff, final int mlen) { 96 | if (!(message!=null && message.length>=(moff+mlen))) return null; 97 | 98 | // prepare shared key 99 | if (this.sharedKey == null) before(); 100 | 101 | return after(message, moff, mlen); 102 | } 103 | 104 | /* 105 | * @description 106 | * Encrypt and authenticates message using peer's public key, 107 | * our secret key, and the given nonce, which must be unique 108 | * for each distinct message for a key pair. 109 | * 110 | * Explicitly pass the nonce 111 | * 112 | * Returns an encrypted and authenticated message, 113 | * which is nacl.box.overheadLength longer than the original message. 114 | * */ 115 | public byte [] box(byte [] message, byte [] theNonce) { 116 | if (message==null) return null; 117 | return box(message, 0, message.length, theNonce); 118 | } 119 | 120 | public byte [] box(byte [] message, final int moff, byte [] theNonce) { 121 | if (!(message!=null && message.length>moff)) return null; 122 | return box(message, moff, message.length-moff, theNonce); 123 | } 124 | 125 | public byte [] box(byte [] message, final int moff, final int mlen, byte [] theNonce) { 126 | if (!(message!=null && message.length>=(moff+mlen) && 127 | theNonce!=null && theNonce.length==nonceLength)) 128 | return null; 129 | 130 | // prepare shared key 131 | if (this.sharedKey == null) before(); 132 | 133 | return after(message, moff, mlen, theNonce); 134 | } 135 | 136 | /* 137 | * @description 138 | * Authenticates and decrypts the given box with peer's public key, 139 | * our secret key, and the given nonce. 140 | * 141 | * Returns the original message, or null if authentication fails. 142 | * */ 143 | public byte [] open(byte [] box) { 144 | if (box==null) return null; 145 | 146 | // prepare shared key 147 | if (this.sharedKey == null) before(); 148 | 149 | return open_after(box, 0, box.length); 150 | } 151 | public byte [] open(byte [] box, final int boxoff) { 152 | if (!(box!=null && box.length>boxoff)) return null; 153 | 154 | // prepare shared key 155 | if (this.sharedKey == null) before(); 156 | 157 | return open_after(box, boxoff, box.length-boxoff); 158 | } 159 | public byte [] open(byte [] box, final int boxoff, final int boxlen) { 160 | if (!(box!=null && box.length>=(boxoff+boxlen))) return null; 161 | 162 | // prepare shared key 163 | if (this.sharedKey == null) before(); 164 | 165 | return open_after(box, boxoff, boxlen); 166 | } 167 | 168 | 169 | /* 170 | * @description 171 | * Authenticates and decrypts the given box with peer's public key, 172 | * our secret key, and the given nonce. 173 | * Explicit passing of nonce 174 | * Returns the original message, or null if authentication fails. 175 | * */ 176 | public byte [] open(byte [] box, byte [] theNonce) { 177 | if (!(box!=null && 178 | theNonce!=null && theNonce.length==nonceLength)) 179 | return null; 180 | 181 | // prepare shared key 182 | if (this.sharedKey == null) before(); 183 | 184 | return open_after(box, 0, box.length, theNonce); 185 | } 186 | public byte [] open(byte [] box, final int boxoff, byte [] theNonce) { 187 | if (!(box!=null && box.length>boxoff && 188 | theNonce!=null && theNonce.length==nonceLength)) 189 | return null; 190 | 191 | // prepare shared key 192 | if (this.sharedKey == null) before(); 193 | 194 | return open_after(box, boxoff, box.length-boxoff, theNonce); 195 | } 196 | public byte [] open(byte [] box, final int boxoff, final int boxlen, byte [] theNonce) { 197 | if (!(box!=null && box.length>=(boxoff+boxlen) && 198 | theNonce!=null && theNonce.length==nonceLength)) 199 | return null; 200 | 201 | // prepare shared key 202 | if (this.sharedKey == null) before(); 203 | 204 | return open_after(box, boxoff, boxlen, theNonce); 205 | } 206 | 207 | 208 | /* 209 | * @description 210 | * Returns a precomputed shared key 211 | * which can be used in nacl.box.after and nacl.box.open.after. 212 | * */ 213 | public byte [] before() { 214 | if (this.sharedKey == null) { 215 | this.sharedKey = new byte[sharedKeyLength]; 216 | crypto_box_beforenm(this.sharedKey, this.theirPublicKey, this.mySecretKey); 217 | } 218 | 219 | return this.sharedKey; 220 | } 221 | 222 | /* 223 | * @description 224 | * Same as nacl.box, but uses a shared key precomputed with nacl.box.before. 225 | * */ 226 | public byte [] after(byte [] message, final int moff, final int mlen) { 227 | return after(message, moff, mlen, generateNonce()); 228 | } 229 | 230 | /* 231 | * @description 232 | * Same as nacl.box, but uses a shared key precomputed with nacl.box.before, 233 | * and passes a nonce explicitly. 234 | * */ 235 | public byte [] after(byte [] message, final int moff, final int mlen, byte [] theNonce) { 236 | // check message 237 | if (!(message!=null && message.length>=(moff+mlen) && 238 | theNonce!=null && theNonce.length==nonceLength)) 239 | return null; 240 | 241 | // message buffer 242 | byte [] m = new byte[mlen + zerobytesLength]; 243 | 244 | // cipher buffer 245 | byte [] c = new byte[m.length]; 246 | 247 | for (int i = 0; i < mlen; i ++) 248 | m[i+zerobytesLength] = message[i+moff]; 249 | 250 | if (0 != crypto_box_afternm(c, m, m.length, theNonce, sharedKey)) 251 | return null; 252 | 253 | // wrap byte_buf_t on c offset@boxzerobytesLength 254 | ///return new byte_buf_t(c, boxzerobytesLength, c.length-boxzerobytesLength); 255 | byte [] ret = new byte[c.length-boxzerobytesLength]; 256 | 257 | for (int i = 0; i < ret.length; i ++) 258 | ret[i] = c[i+boxzerobytesLength]; 259 | 260 | return ret; 261 | } 262 | 263 | /* 264 | * @description 265 | * Same as nacl.box.open, 266 | * but uses a shared key pre-computed with nacl.box.before. 267 | * */ 268 | public byte [] open_after(byte [] box, final int boxoff, final int boxlen) { 269 | return open_after(box, boxoff, boxlen, generateNonce()); 270 | } 271 | 272 | public byte [] open_after(byte [] box, final int boxoff, final int boxlen, byte [] theNonce) { 273 | // check message 274 | if (!(box!=null && box.length>=(boxoff+boxlen) && boxlen>=boxzerobytesLength)) 275 | return null; 276 | 277 | // cipher buffer 278 | byte [] c = new byte[boxlen + boxzerobytesLength]; 279 | 280 | // message buffer 281 | byte [] m = new byte[c.length]; 282 | 283 | for (int i = 0; i < boxlen; i++) 284 | c[i+boxzerobytesLength] = box[i+boxoff]; 285 | 286 | if (crypto_box_open_afternm(m, c, c.length, theNonce, sharedKey) != 0) 287 | return null; 288 | 289 | // wrap byte_buf_t on m offset@zerobytesLength 290 | ///return new byte_buf_t(m, zerobytesLength, m.length-zerobytesLength); 291 | byte [] ret = new byte[m.length-zerobytesLength]; 292 | 293 | for (int i = 0; i < ret.length; i ++) 294 | ret[i] = m[i+zerobytesLength]; 295 | 296 | return ret; 297 | } 298 | 299 | /* 300 | * @description 301 | * Length of public key in bytes. 302 | * */ 303 | public static final int publicKeyLength = 32; 304 | 305 | /* 306 | * @description 307 | * Length of secret key in bytes. 308 | * */ 309 | public static final int secretKeyLength = 32; 310 | 311 | /* 312 | * @description 313 | * Length of precomputed shared key in bytes. 314 | * */ 315 | public static final int sharedKeyLength = 32; 316 | 317 | /* 318 | * @description 319 | * Length of nonce in bytes. 320 | * */ 321 | public static final int nonceLength = 24; 322 | 323 | /* 324 | * @description 325 | * zero bytes in case box 326 | * */ 327 | public static final int zerobytesLength = 32; 328 | /* 329 | * @description 330 | * zero bytes in case open box 331 | * */ 332 | public static final int boxzerobytesLength = 16; 333 | 334 | /* 335 | * @description 336 | * Length of overhead added to box compared to original message. 337 | * */ 338 | public static final int overheadLength = 16; 339 | 340 | public static class KeyPair { 341 | private byte [] publicKey; 342 | private byte [] secretKey; 343 | 344 | public KeyPair() { 345 | publicKey = new byte[publicKeyLength]; 346 | secretKey = new byte[secretKeyLength]; 347 | } 348 | 349 | public byte [] getPublicKey() { 350 | return publicKey; 351 | } 352 | 353 | public byte [] getSecretKey() { 354 | return secretKey; 355 | } 356 | } 357 | 358 | /* 359 | * @description 360 | * Generates a new random key pair for box and 361 | * returns it as an object with publicKey and secretKey members: 362 | * */ 363 | public static KeyPair keyPair() { 364 | KeyPair kp = new KeyPair(); 365 | 366 | crypto_box_keypair(kp.getPublicKey(), kp.getSecretKey()); 367 | return kp; 368 | } 369 | 370 | public static KeyPair keyPair_fromSecretKey(byte [] secretKey) { 371 | KeyPair kp = new KeyPair(); 372 | byte [] sk = kp.getSecretKey(); 373 | byte [] pk = kp.getPublicKey(); 374 | 375 | // copy sk 376 | for (int i = 0; i < sk.length; i ++) 377 | sk[i] = secretKey[i]; 378 | 379 | crypto_scalarmult_base(pk, sk); 380 | return kp; 381 | } 382 | 383 | } 384 | 385 | /* 386 | * @description 387 | * Secret Box algorithm, secret key 388 | * */ 389 | public static final class SecretBox { 390 | 391 | private final static String TAG = "SecretBox"; 392 | 393 | private AtomicLong nonce; 394 | 395 | private byte [] key; 396 | 397 | public SecretBox(byte [] key) { 398 | this(key, 68); 399 | } 400 | 401 | public SecretBox(byte [] key, long nonce) { 402 | this.key = key; 403 | 404 | this.nonce = new AtomicLong(nonce); 405 | } 406 | 407 | public void setNonce(long nonce) { 408 | this.nonce.set(nonce); 409 | } 410 | public long getNonce() { 411 | return this.nonce.get(); 412 | } 413 | public long incrNonce() { 414 | return this.nonce.incrementAndGet(); 415 | } 416 | private byte[] generateNonce() { 417 | // generate nonce 418 | long nonce = this.nonce.get(); 419 | 420 | byte [] n = new byte[nonceLength]; 421 | for (int i = 0; i < nonceLength; i += 8) { 422 | n[i+0] = (byte) (nonce>>> 0); 423 | n[i+1] = (byte) (nonce>>> 8); 424 | n[i+2] = (byte) (nonce>>>16); 425 | n[i+3] = (byte) (nonce>>>24); 426 | n[i+4] = (byte) (nonce>>>32); 427 | n[i+5] = (byte) (nonce>>>40); 428 | n[i+6] = (byte) (nonce>>>48); 429 | n[i+7] = (byte) (nonce>>>56); 430 | } 431 | 432 | return n; 433 | } 434 | 435 | /* 436 | * @description 437 | * Encrypt and authenticates message using the key and the nonce. 438 | * The nonce must be unique for each distinct message for this key. 439 | * 440 | * Returns an encrypted and authenticated message, 441 | * which is nacl.secretbox.overheadLength longer than the original message. 442 | * */ 443 | public byte [] box(byte [] message) { 444 | if (message==null) return null; 445 | return box(message, 0, message.length); 446 | } 447 | 448 | public byte [] box(byte [] message, final int moff) { 449 | if (!(message!=null && message.length>moff)) return null; 450 | return box(message, moff, message.length-moff); 451 | } 452 | 453 | public byte [] box(byte [] message, final int moff, final int mlen) { 454 | // check message 455 | if (!(message!=null && message.length>=(moff+mlen))) 456 | return null; 457 | return box(message, moff, message.length-moff, generateNonce()); 458 | } 459 | 460 | public byte [] box(byte [] message, byte [] theNonce) { 461 | if (message==null) return null; 462 | return box(message, 0, message.length, theNonce); 463 | } 464 | 465 | public byte [] box(byte [] message, final int moff, byte [] theNonce) { 466 | if (!(message!=null && message.length>moff)) return null; 467 | return box(message, moff, message.length-moff, theNonce); 468 | } 469 | 470 | public byte [] box(byte [] message, final int moff, final int mlen, byte [] theNonce) { 471 | // check message 472 | if (!(message!=null && message.length>=(moff+mlen) && 473 | theNonce!=null && theNonce.length==nonceLength)) 474 | return null; 475 | 476 | // message buffer 477 | byte [] m = new byte[mlen + zerobytesLength]; 478 | 479 | // cipher buffer 480 | byte [] c = new byte[m.length]; 481 | 482 | for (int i = 0; i < mlen; i ++) 483 | m[i+zerobytesLength] = message[i+moff]; 484 | 485 | if (0 != crypto_secretbox(c, m, m.length, theNonce, key)) 486 | return null; 487 | 488 | // TBD optimizing ... 489 | // wrap byte_buf_t on c offset@boxzerobytesLength 490 | ///return new byte_buf_t(c, boxzerobytesLength, c.length-boxzerobytesLength); 491 | byte [] ret = new byte[c.length-boxzerobytesLength]; 492 | 493 | for (int i = 0; i < ret.length; i ++) 494 | ret[i] = c[i+boxzerobytesLength]; 495 | 496 | return ret; 497 | } 498 | 499 | /* 500 | * @description 501 | * Authenticates and decrypts the given secret box 502 | * using the key and the nonce. 503 | * 504 | * Returns the original message, or null if authentication fails. 505 | * */ 506 | public byte [] open(byte [] box) { 507 | if (box==null) return null; 508 | return open(box, 0, box.length); 509 | } 510 | 511 | public byte [] open(byte [] box, final int boxoff) { 512 | if (!(box!=null && box.length>boxoff)) return null; 513 | return open(box, boxoff, box.length-boxoff); 514 | } 515 | 516 | public byte [] open(byte [] box, final int boxoff, final int boxlen) { 517 | // check message 518 | if (!(box!=null && box.length>=(boxoff+boxlen) && boxlen>=boxzerobytesLength)) 519 | return null; 520 | return open(box, boxoff, box.length-boxoff, generateNonce()); 521 | } 522 | 523 | public byte [] open(byte [] box, byte [] theNonce) { 524 | if (box==null) return null; 525 | return open(box, 0, box.length, theNonce); 526 | } 527 | 528 | public byte [] open(byte [] box, final int boxoff, byte [] theNonce) { 529 | if (!(box!=null && box.length>boxoff)) return null; 530 | return open(box, boxoff, box.length-boxoff, theNonce); 531 | } 532 | 533 | public byte [] open(byte [] box, final int boxoff, final int boxlen, byte [] theNonce) { 534 | // check message 535 | if (!(box!=null && box.length>=(boxoff+boxlen) && boxlen>=boxzerobytesLength && 536 | theNonce!=null && theNonce.length==nonceLength)) 537 | return null; 538 | 539 | // cipher buffer 540 | byte [] c = new byte[boxlen + boxzerobytesLength]; 541 | 542 | // message buffer 543 | byte [] m = new byte[c.length]; 544 | 545 | for (int i = 0; i < boxlen; i++) 546 | c[i+boxzerobytesLength] = box[i+boxoff]; 547 | 548 | if (0 != crypto_secretbox_open(m, c, c.length, theNonce, key)) 549 | return null; 550 | 551 | // wrap byte_buf_t on m offset@zerobytesLength 552 | ///return new byte_buf_t(m, zerobytesLength, m.length-zerobytesLength); 553 | byte [] ret = new byte[m.length-zerobytesLength]; 554 | 555 | for (int i = 0; i < ret.length; i ++) 556 | ret[i] = m[i+zerobytesLength]; 557 | 558 | return ret; 559 | } 560 | 561 | /* 562 | * @description 563 | * Length of key in bytes. 564 | * */ 565 | public static final int keyLength = 32; 566 | 567 | /* 568 | * @description 569 | * Length of nonce in bytes. 570 | * */ 571 | public static final int nonceLength = 24; 572 | 573 | /* 574 | * @description 575 | * Length of overhead added to secret box compared to original message. 576 | * */ 577 | public static final int overheadLength = 16; 578 | 579 | /* 580 | * @description 581 | * zero bytes in case box 582 | * */ 583 | public static final int zerobytesLength = 32; 584 | /* 585 | * @description 586 | * zero bytes in case open box 587 | * */ 588 | public static final int boxzerobytesLength = 16; 589 | 590 | } 591 | 592 | /* 593 | * @description 594 | * Scalar multiplication, Implements curve25519. 595 | * */ 596 | public static final class ScalarMult { 597 | 598 | private final static String TAG = "ScalarMult"; 599 | 600 | /* 601 | * @description 602 | * Multiplies an integer n by a group element p and 603 | * returns the resulting group element. 604 | * */ 605 | public static byte [] scalseMult(byte [] n, byte [] p) { 606 | if (!(n.length==scalarLength && p.length==groupElementLength)) 607 | return null; 608 | 609 | byte [] q = new byte [scalarLength]; 610 | 611 | crypto_scalarmult(q, n, p); 612 | 613 | return q; 614 | } 615 | 616 | /* 617 | * @description 618 | * Multiplies an integer n by a standard group element and 619 | * returns the resulting group element. 620 | * */ 621 | public static byte [] scalseMult_base(byte [] n) { 622 | if (!(n.length==scalarLength)) 623 | return null; 624 | 625 | byte [] q = new byte [scalarLength]; 626 | 627 | crypto_scalarmult_base(q, n); 628 | 629 | return q; 630 | } 631 | 632 | /* 633 | * @description 634 | * Length of scalar in bytes. 635 | * */ 636 | public static final int scalarLength = 32; 637 | 638 | /* 639 | * @description 640 | * Length of group element in bytes. 641 | * */ 642 | public static final int groupElementLength = 32; 643 | 644 | } 645 | 646 | 647 | /* 648 | * @description 649 | * Hash algorithm, Implements SHA-512. 650 | * */ 651 | public static final class Hash { 652 | 653 | private final static String TAG = "Hash"; 654 | 655 | /* 656 | * @description 657 | * Returns SHA-512 hash of the message. 658 | * */ 659 | public static byte[] sha512(byte [] message) { 660 | if (!(message!=null && message.length>0)) 661 | return null; 662 | 663 | byte [] out = new byte[hashLength]; 664 | 665 | crypto_hash(out, message); 666 | 667 | return out; 668 | } 669 | public static byte[] sha512(String message) throws UnsupportedEncodingException { 670 | return sha512(message.getBytes("utf-8")); 671 | } 672 | 673 | /* 674 | * @description 675 | * Length of hash in bytes. 676 | * */ 677 | public static final int hashLength = 64; 678 | 679 | } 680 | 681 | 682 | /* 683 | * @description 684 | * Signature algorithm, Implements ed25519. 685 | * */ 686 | public static final class Signature { 687 | 688 | private final static String TAG = "Signature"; 689 | 690 | private byte [] theirPublicKey; 691 | private byte [] mySecretKey; 692 | 693 | public Signature(byte [] theirPublicKey, byte [] mySecretKey) { 694 | this.theirPublicKey = theirPublicKey; 695 | this.mySecretKey = mySecretKey; 696 | } 697 | 698 | /* 699 | * @description 700 | * Signs the message using the secret key and returns a signed message. 701 | * */ 702 | public byte [] sign(byte [] message) { 703 | if (message==null) return null; 704 | 705 | return sign(message, 0, message.length); 706 | } 707 | public byte [] sign(byte [] message, final int moff) { 708 | if (!(message!=null && message.length>moff)) return null; 709 | 710 | return sign(message, moff, message.length-moff); 711 | } 712 | public byte [] sign(byte [] message, final int moff, final int mlen) { 713 | // check message 714 | if (!(message!=null && message.length>=(moff+mlen))) 715 | return null; 716 | 717 | // signed message 718 | byte [] sm = new byte[mlen + signatureLength]; 719 | 720 | crypto_sign(sm, -1, message, moff, mlen, mySecretKey); 721 | 722 | return sm; 723 | } 724 | 725 | /* 726 | * @description 727 | * Verifies the signed message and returns the message without signature. 728 | * Returns null if verification failed. 729 | * */ 730 | public byte [] open(byte [] signedMessage) { 731 | if (signedMessage==null) return null; 732 | 733 | return open(signedMessage, 0, signedMessage.length); 734 | } 735 | public byte [] open(byte [] signedMessage, final int smoff) { 736 | if (!(signedMessage!=null && signedMessage.length>smoff)) return null; 737 | 738 | return open(signedMessage, smoff, signedMessage.length-smoff); 739 | } 740 | public byte [] open(byte [] signedMessage, final int smoff, final int smlen) { 741 | // check sm length 742 | if (!(signedMessage!=null && signedMessage.length>=(smoff+smlen) && smlen>=signatureLength)) 743 | return null; 744 | 745 | // temp buffer 746 | byte [] tmp = new byte[smlen]; 747 | 748 | if (0 != crypto_sign_open(tmp, -1, signedMessage, smoff, smlen, theirPublicKey)) 749 | return null; 750 | 751 | // message 752 | byte [] msg = new byte[smlen-signatureLength]; 753 | for (int i = 0; i < msg.length; i ++) 754 | msg[i] = signedMessage[smoff+ i+signatureLength]; 755 | 756 | return msg; 757 | } 758 | 759 | /* 760 | * @description 761 | * Signs the message using the secret key and returns a signature. 762 | * */ 763 | public byte [] detached(byte [] message) { 764 | byte[] signedMsg = this.sign(message); 765 | byte[] sig = new byte[signatureLength]; 766 | for (int i = 0; i < sig.length; i++) 767 | sig[i] = signedMsg[i]; 768 | return sig; 769 | } 770 | 771 | /* 772 | * @description 773 | * Verifies the signature for the message and 774 | * returns true if verification succeeded or false if it failed. 775 | * */ 776 | public boolean detached_verify(byte [] message, byte [] signature) { 777 | if (signature.length != signatureLength) 778 | return false; 779 | if (theirPublicKey.length != publicKeyLength) 780 | return false; 781 | byte [] sm = new byte[signatureLength + message.length]; 782 | byte [] m = new byte[signatureLength + message.length]; 783 | for (int i = 0; i < signatureLength; i++) 784 | sm[i] = signature[i]; 785 | for (int i = 0; i < message.length; i++) 786 | sm[i + signatureLength] = message[i]; 787 | return (crypto_sign_open(m, -1, sm, 0, sm.length, theirPublicKey) >= 0); 788 | } 789 | 790 | /* 791 | * @description 792 | * Generates new random key pair for signing and 793 | * returns it as an object with publicKey and secretKey members 794 | * */ 795 | public static class KeyPair { 796 | private byte [] publicKey; 797 | private byte [] secretKey; 798 | 799 | public KeyPair() { 800 | publicKey = new byte[publicKeyLength]; 801 | secretKey = new byte[secretKeyLength]; 802 | } 803 | 804 | public byte [] getPublicKey() { 805 | return publicKey; 806 | } 807 | 808 | public byte [] getSecretKey() { 809 | return secretKey; 810 | } 811 | } 812 | 813 | /* 814 | * @description 815 | * Signs the message using the secret key and returns a signed message. 816 | * */ 817 | public static KeyPair keyPair() { 818 | KeyPair kp = new KeyPair(); 819 | 820 | crypto_sign_keypair(kp.getPublicKey(), kp.getSecretKey(), false); 821 | return kp; 822 | } 823 | public static KeyPair keyPair_fromSecretKey(byte [] secretKey) { 824 | KeyPair kp = new KeyPair(); 825 | byte [] pk = kp.getPublicKey(); 826 | byte [] sk = kp.getSecretKey(); 827 | 828 | // copy sk 829 | for (int i = 0; i < kp.getSecretKey().length; i ++) 830 | sk[i] = secretKey[i]; 831 | 832 | // copy pk from sk 833 | for (int i = 0; i < kp.getPublicKey().length; i ++) 834 | pk[i] = secretKey[32+i]; // hard-copy 835 | 836 | return kp; 837 | } 838 | 839 | public static KeyPair keyPair_fromSeed(byte [] seed) { 840 | KeyPair kp = new KeyPair(); 841 | byte [] pk = kp.getPublicKey(); 842 | byte [] sk = kp.getSecretKey(); 843 | 844 | // copy sk 845 | for (int i = 0; i < seedLength; i ++) 846 | sk[i] = seed[i]; 847 | 848 | // generate pk from sk 849 | crypto_sign_keypair(pk, sk, true); 850 | 851 | return kp; 852 | } 853 | 854 | /* 855 | * @description 856 | * Length of signing public key in bytes. 857 | * */ 858 | public static final int publicKeyLength = 32; 859 | 860 | /* 861 | * @description 862 | * Length of signing secret key in bytes. 863 | * */ 864 | public static final int secretKeyLength = 64; 865 | 866 | /* 867 | * @description 868 | * Length of seed for nacl.sign.keyPair.fromSeed in bytes. 869 | * */ 870 | public static final int seedLength = 32; 871 | 872 | /* 873 | * @description 874 | * Length of signature in bytes. 875 | * */ 876 | public static final int signatureLength = 64; 877 | } 878 | 879 | 880 | //////////////////////////////////////////////////////////////////////////////////// 881 | /* 882 | * @description 883 | * Codes below are ported tweetnacl-fast.js from TweetNacl.c/TweetNacl.h 884 | * */ 885 | 886 | private static final byte [] _0 = new byte[16]; 887 | private static final byte [] _9 = new byte[32]; 888 | static { 889 | ///for (int i = 0; i < _0.length; i ++) _0[i] = 0; 890 | 891 | ///for (int i = 0; i < _9.length; i ++) _9[i] = 0; 892 | _9[0] = 9; 893 | } 894 | 895 | private static final long [] gf0 = new long[16]; 896 | private static final long [] gf1 = new long[16]; 897 | private static final long [] _121665 = new long[16]; 898 | static { 899 | ///for (int i = 0; i < gf0.length; i ++) gf0[i] = 0; 900 | 901 | ///for (int i = 0; i < gf1.length; i ++) gf1[i] = 0; 902 | gf1[0] = 1; 903 | 904 | ///for (int i = 0; i < _121665.length; i ++) _121665[i] = 0; 905 | _121665[0] = 0xDB41; _121665[1] = 1; 906 | } 907 | 908 | private static final long [] D = new long [] { 909 | 0x78a3, 0x1359, 0x4dca, 0x75eb, 910 | 0xd8ab, 0x4141, 0x0a4d, 0x0070, 911 | 0xe898, 0x7779, 0x4079, 0x8cc7, 912 | 0xfe73, 0x2b6f, 0x6cee, 0x5203 913 | }; 914 | private static final long [] D2 = new long [] { 915 | 0xf159, 0x26b2, 0x9b94, 0xebd6, 916 | 0xb156, 0x8283, 0x149a, 0x00e0, 917 | 0xd130, 0xeef3, 0x80f2, 0x198e, 918 | 0xfce7, 0x56df, 0xd9dc, 0x2406 919 | }; 920 | private static final long [] X = new long [] { 921 | 0xd51a, 0x8f25, 0x2d60, 0xc956, 922 | 0xa7b2, 0x9525, 0xc760, 0x692c, 923 | 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 924 | 0x53fe, 0xcd6e, 0x36d3, 0x2169 925 | }; 926 | private static final long [] Y = new long [] { 927 | 0x6658, 0x6666, 0x6666, 0x6666, 928 | 0x6666, 0x6666, 0x6666, 0x6666, 929 | 0x6666, 0x6666, 0x6666, 0x6666, 930 | 0x6666, 0x6666, 0x6666, 0x6666 931 | }; 932 | private static final long [] I = new long [] { 933 | 0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 934 | 0xe478, 0xad2f, 0x1806, 0x2f43, 935 | 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 936 | 0xdf0b, 0x4fc1, 0x2480, 0x2b83 937 | }; 938 | 939 | private static void ts64(byte [] x, final int xoff, long u) 940 | { 941 | ///int i; 942 | ///for (i = 7;i >= 0;--i) { x[i+xoff] = (byte)(u&0xff); u >>>= 8; } 943 | 944 | x[7+xoff] = (byte)(u&0xff); u >>>= 8; 945 | x[6+xoff] = (byte)(u&0xff); u >>>= 8; 946 | x[5+xoff] = (byte)(u&0xff); u >>>= 8; 947 | x[4+xoff] = (byte)(u&0xff); u >>>= 8; 948 | x[3+xoff] = (byte)(u&0xff); u >>>= 8; 949 | x[2+xoff] = (byte)(u&0xff); u >>>= 8; 950 | x[1+xoff] = (byte)(u&0xff); u >>>= 8; 951 | x[0+xoff] = (byte)(u&0xff); ///u >>>= 8; 952 | } 953 | 954 | private static int vn( 955 | byte [] x, final int xoff, 956 | byte [] y, final int yoff, 957 | int n) 958 | { 959 | int i,d = 0; 960 | for (i = 0; i < n; i ++) d |= (x[i+xoff]^y[i+yoff]) & 0xff; 961 | return (1 & ((d - 1) >>> 8)) - 1; 962 | } 963 | 964 | private static int crypto_verify_16( 965 | byte [] x, final int xoff, 966 | byte [] y, final int yoff) 967 | { 968 | return vn(x,xoff,y,yoff,16); 969 | } 970 | public static int crypto_verify_16(byte [] x, byte [] y) 971 | { 972 | return crypto_verify_16(x,0, y,0); 973 | } 974 | 975 | private static int crypto_verify_32( 976 | byte [] x, final int xoff, 977 | byte [] y, final int yoff) 978 | { 979 | return vn(x,xoff,y,yoff,32); 980 | } 981 | public static int crypto_verify_32(byte [] x, byte [] y) 982 | { 983 | return crypto_verify_32(x,0, y,0); 984 | } 985 | 986 | private static void core_salsa20(byte [] o, byte [] p, byte [] k, byte [] c) { 987 | int j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24, 988 | j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24, 989 | j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24, 990 | j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24, 991 | j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24, 992 | j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24, 993 | j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24, 994 | j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24, 995 | j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24, 996 | j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24, 997 | j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24, 998 | j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24, 999 | j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24, 1000 | j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24, 1001 | j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24, 1002 | j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24; 1003 | 1004 | int x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, 1005 | x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, 1006 | x15 = j15, u; 1007 | 1008 | for (int i = 0; i < 20; i += 2) { 1009 | u = x0 + x12 | 0; 1010 | x4 ^= u<<7 | u>>>(32-7); 1011 | u = x4 + x0 | 0; 1012 | x8 ^= u<<9 | u>>>(32-9); 1013 | u = x8 + x4 | 0; 1014 | x12 ^= u<<13 | u>>>(32-13); 1015 | u = x12 + x8 | 0; 1016 | x0 ^= u<<18 | u>>>(32-18); 1017 | 1018 | u = x5 + x1 | 0; 1019 | x9 ^= u<<7 | u>>>(32-7); 1020 | u = x9 + x5 | 0; 1021 | x13 ^= u<<9 | u>>>(32-9); 1022 | u = x13 + x9 | 0; 1023 | x1 ^= u<<13 | u>>>(32-13); 1024 | u = x1 + x13 | 0; 1025 | x5 ^= u<<18 | u>>>(32-18); 1026 | 1027 | u = x10 + x6 | 0; 1028 | x14 ^= u<<7 | u>>>(32-7); 1029 | u = x14 + x10 | 0; 1030 | x2 ^= u<<9 | u>>>(32-9); 1031 | u = x2 + x14 | 0; 1032 | x6 ^= u<<13 | u>>>(32-13); 1033 | u = x6 + x2 | 0; 1034 | x10 ^= u<<18 | u>>>(32-18); 1035 | 1036 | u = x15 + x11 | 0; 1037 | x3 ^= u<<7 | u>>>(32-7); 1038 | u = x3 + x15 | 0; 1039 | x7 ^= u<<9 | u>>>(32-9); 1040 | u = x7 + x3 | 0; 1041 | x11 ^= u<<13 | u>>>(32-13); 1042 | u = x11 + x7 | 0; 1043 | x15 ^= u<<18 | u>>>(32-18); 1044 | 1045 | u = x0 + x3 | 0; 1046 | x1 ^= u<<7 | u>>>(32-7); 1047 | u = x1 + x0 | 0; 1048 | x2 ^= u<<9 | u>>>(32-9); 1049 | u = x2 + x1 | 0; 1050 | x3 ^= u<<13 | u>>>(32-13); 1051 | u = x3 + x2 | 0; 1052 | x0 ^= u<<18 | u>>>(32-18); 1053 | 1054 | u = x5 + x4 | 0; 1055 | x6 ^= u<<7 | u>>>(32-7); 1056 | u = x6 + x5 | 0; 1057 | x7 ^= u<<9 | u>>>(32-9); 1058 | u = x7 + x6 | 0; 1059 | x4 ^= u<<13 | u>>>(32-13); 1060 | u = x4 + x7 | 0; 1061 | x5 ^= u<<18 | u>>>(32-18); 1062 | 1063 | u = x10 + x9 | 0; 1064 | x11 ^= u<<7 | u>>>(32-7); 1065 | u = x11 + x10 | 0; 1066 | x8 ^= u<<9 | u>>>(32-9); 1067 | u = x8 + x11 | 0; 1068 | x9 ^= u<<13 | u>>>(32-13); 1069 | u = x9 + x8 | 0; 1070 | x10 ^= u<<18 | u>>>(32-18); 1071 | 1072 | u = x15 + x14 | 0; 1073 | x12 ^= u<<7 | u>>>(32-7); 1074 | u = x12 + x15 | 0; 1075 | x13 ^= u<<9 | u>>>(32-9); 1076 | u = x13 + x12 | 0; 1077 | x14 ^= u<<13 | u>>>(32-13); 1078 | u = x14 + x13 | 0; 1079 | x15 ^= u<<18 | u>>>(32-18); 1080 | } 1081 | x0 = x0 + j0 | 0; 1082 | x1 = x1 + j1 | 0; 1083 | x2 = x2 + j2 | 0; 1084 | x3 = x3 + j3 | 0; 1085 | x4 = x4 + j4 | 0; 1086 | x5 = x5 + j5 | 0; 1087 | x6 = x6 + j6 | 0; 1088 | x7 = x7 + j7 | 0; 1089 | x8 = x8 + j8 | 0; 1090 | x9 = x9 + j9 | 0; 1091 | x10 = x10 + j10 | 0; 1092 | x11 = x11 + j11 | 0; 1093 | x12 = x12 + j12 | 0; 1094 | x13 = x13 + j13 | 0; 1095 | x14 = x14 + j14 | 0; 1096 | x15 = x15 + j15 | 0; 1097 | 1098 | o[ 0] = (byte) (x0 >>> 0 & 0xff); 1099 | o[ 1] = (byte) (x0 >>> 8 & 0xff); 1100 | o[ 2] = (byte) (x0 >>> 16 & 0xff); 1101 | o[ 3] = (byte) (x0 >>> 24 & 0xff); 1102 | 1103 | o[ 4] = (byte) (x1 >>> 0 & 0xff); 1104 | o[ 5] = (byte) (x1 >>> 8 & 0xff); 1105 | o[ 6] = (byte) (x1 >>> 16 & 0xff); 1106 | o[ 7] = (byte) (x1 >>> 24 & 0xff); 1107 | 1108 | o[ 8] = (byte) (x2 >>> 0 & 0xff); 1109 | o[ 9] = (byte) (x2 >>> 8 & 0xff); 1110 | o[10] = (byte) (x2 >>> 16 & 0xff); 1111 | o[11] = (byte) (x2 >>> 24 & 0xff); 1112 | 1113 | o[12] = (byte) (x3 >>> 0 & 0xff); 1114 | o[13] = (byte) (x3 >>> 8 & 0xff); 1115 | o[14] = (byte) (x3 >>> 16 & 0xff); 1116 | o[15] = (byte) (x3 >>> 24 & 0xff); 1117 | 1118 | o[16] = (byte) (x4 >>> 0 & 0xff); 1119 | o[17] = (byte) (x4 >>> 8 & 0xff); 1120 | o[18] = (byte) (x4 >>> 16 & 0xff); 1121 | o[19] = (byte) (x4 >>> 24 & 0xff); 1122 | 1123 | o[20] = (byte) (x5 >>> 0 & 0xff); 1124 | o[21] = (byte) (x5 >>> 8 & 0xff); 1125 | o[22] = (byte) (x5 >>> 16 & 0xff); 1126 | o[23] = (byte) (x5 >>> 24 & 0xff); 1127 | 1128 | o[24] = (byte) (x6 >>> 0 & 0xff); 1129 | o[25] = (byte) (x6 >>> 8 & 0xff); 1130 | o[26] = (byte) (x6 >>> 16 & 0xff); 1131 | o[27] = (byte) (x6 >>> 24 & 0xff); 1132 | 1133 | o[28] = (byte) (x7 >>> 0 & 0xff); 1134 | o[29] = (byte) (x7 >>> 8 & 0xff); 1135 | o[30] = (byte) (x7 >>> 16 & 0xff); 1136 | o[31] = (byte) (x7 >>> 24 & 0xff); 1137 | 1138 | o[32] = (byte) (x8 >>> 0 & 0xff); 1139 | o[33] = (byte) (x8 >>> 8 & 0xff); 1140 | o[34] = (byte) (x8 >>> 16 & 0xff); 1141 | o[35] = (byte) (x8 >>> 24 & 0xff); 1142 | 1143 | o[36] = (byte) (x9 >>> 0 & 0xff); 1144 | o[37] = (byte) (x9 >>> 8 & 0xff); 1145 | o[38] = (byte) (x9 >>> 16 & 0xff); 1146 | o[39] = (byte) (x9 >>> 24 & 0xff); 1147 | 1148 | o[40] = (byte) (x10 >>> 0 & 0xff); 1149 | o[41] = (byte) (x10 >>> 8 & 0xff); 1150 | o[42] = (byte) (x10 >>> 16 & 0xff); 1151 | o[43] = (byte) (x10 >>> 24 & 0xff); 1152 | 1153 | o[44] = (byte) (x11 >>> 0 & 0xff); 1154 | o[45] = (byte) (x11 >>> 8 & 0xff); 1155 | o[46] = (byte) (x11 >>> 16 & 0xff); 1156 | o[47] = (byte) (x11 >>> 24 & 0xff); 1157 | 1158 | o[48] = (byte) (x12 >>> 0 & 0xff); 1159 | o[49] = (byte) (x12 >>> 8 & 0xff); 1160 | o[50] = (byte) (x12 >>> 16 & 0xff); 1161 | o[51] = (byte) (x12 >>> 24 & 0xff); 1162 | 1163 | o[52] = (byte) (x13 >>> 0 & 0xff); 1164 | o[53] = (byte) (x13 >>> 8 & 0xff); 1165 | o[54] = (byte) (x13 >>> 16 & 0xff); 1166 | o[55] = (byte) (x13 >>> 24 & 0xff); 1167 | 1168 | o[56] = (byte) (x14 >>> 0 & 0xff); 1169 | o[57] = (byte) (x14 >>> 8 & 0xff); 1170 | o[58] = (byte) (x14 >>> 16 & 0xff); 1171 | o[59] = (byte) (x14 >>> 24 & 0xff); 1172 | 1173 | o[60] = (byte) (x15 >>> 0 & 0xff); 1174 | o[61] = (byte) (x15 >>> 8 & 0xff); 1175 | o[62] = (byte) (x15 >>> 16 & 0xff); 1176 | o[63] = (byte) (x15 >>> 24 & 0xff); 1177 | 1178 | /*String dbgt = ""; 1179 | for (int dbg = 0; dbg < o.length; dbg ++) dbgt += " "+o[dbg]; 1180 | Log.d(TAG, "core_salsa20 -> "+dbgt); 1181 | */ 1182 | } 1183 | 1184 | private static void core_hsalsa20(byte [] o, byte [] p, byte [] k, byte [] c) { 1185 | int j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24, 1186 | j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24, 1187 | j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24, 1188 | j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24, 1189 | j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24, 1190 | j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24, 1191 | j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24, 1192 | j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24, 1193 | j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24, 1194 | j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24, 1195 | j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24, 1196 | j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24, 1197 | j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24, 1198 | j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24, 1199 | j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24, 1200 | j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24; 1201 | 1202 | int x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, 1203 | x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, 1204 | x15 = j15, u; 1205 | 1206 | for (int i = 0; i < 20; i += 2) { 1207 | u = x0 + x12 | 0; 1208 | x4 ^= u<<7 | u>>>(32-7); 1209 | u = x4 + x0 | 0; 1210 | x8 ^= u<<9 | u>>>(32-9); 1211 | u = x8 + x4 | 0; 1212 | x12 ^= u<<13 | u>>>(32-13); 1213 | u = x12 + x8 | 0; 1214 | x0 ^= u<<18 | u>>>(32-18); 1215 | 1216 | u = x5 + x1 | 0; 1217 | x9 ^= u<<7 | u>>>(32-7); 1218 | u = x9 + x5 | 0; 1219 | x13 ^= u<<9 | u>>>(32-9); 1220 | u = x13 + x9 | 0; 1221 | x1 ^= u<<13 | u>>>(32-13); 1222 | u = x1 + x13 | 0; 1223 | x5 ^= u<<18 | u>>>(32-18); 1224 | 1225 | u = x10 + x6 | 0; 1226 | x14 ^= u<<7 | u>>>(32-7); 1227 | u = x14 + x10 | 0; 1228 | x2 ^= u<<9 | u>>>(32-9); 1229 | u = x2 + x14 | 0; 1230 | x6 ^= u<<13 | u>>>(32-13); 1231 | u = x6 + x2 | 0; 1232 | x10 ^= u<<18 | u>>>(32-18); 1233 | 1234 | u = x15 + x11 | 0; 1235 | x3 ^= u<<7 | u>>>(32-7); 1236 | u = x3 + x15 | 0; 1237 | x7 ^= u<<9 | u>>>(32-9); 1238 | u = x7 + x3 | 0; 1239 | x11 ^= u<<13 | u>>>(32-13); 1240 | u = x11 + x7 | 0; 1241 | x15 ^= u<<18 | u>>>(32-18); 1242 | 1243 | u = x0 + x3 | 0; 1244 | x1 ^= u<<7 | u>>>(32-7); 1245 | u = x1 + x0 | 0; 1246 | x2 ^= u<<9 | u>>>(32-9); 1247 | u = x2 + x1 | 0; 1248 | x3 ^= u<<13 | u>>>(32-13); 1249 | u = x3 + x2 | 0; 1250 | x0 ^= u<<18 | u>>>(32-18); 1251 | 1252 | u = x5 + x4 | 0; 1253 | x6 ^= u<<7 | u>>>(32-7); 1254 | u = x6 + x5 | 0; 1255 | x7 ^= u<<9 | u>>>(32-9); 1256 | u = x7 + x6 | 0; 1257 | x4 ^= u<<13 | u>>>(32-13); 1258 | u = x4 + x7 | 0; 1259 | x5 ^= u<<18 | u>>>(32-18); 1260 | 1261 | u = x10 + x9 | 0; 1262 | x11 ^= u<<7 | u>>>(32-7); 1263 | u = x11 + x10 | 0; 1264 | x8 ^= u<<9 | u>>>(32-9); 1265 | u = x8 + x11 | 0; 1266 | x9 ^= u<<13 | u>>>(32-13); 1267 | u = x9 + x8 | 0; 1268 | x10 ^= u<<18 | u>>>(32-18); 1269 | 1270 | u = x15 + x14 | 0; 1271 | x12 ^= u<<7 | u>>>(32-7); 1272 | u = x12 + x15 | 0; 1273 | x13 ^= u<<9 | u>>>(32-9); 1274 | u = x13 + x12 | 0; 1275 | x14 ^= u<<13 | u>>>(32-13); 1276 | u = x14 + x13 | 0; 1277 | x15 ^= u<<18 | u>>>(32-18); 1278 | } 1279 | 1280 | o[ 0] = (byte) (x0 >>> 0 & 0xff); 1281 | o[ 1] = (byte) (x0 >>> 8 & 0xff); 1282 | o[ 2] = (byte) (x0 >>> 16 & 0xff); 1283 | o[ 3] = (byte) (x0 >>> 24 & 0xff); 1284 | 1285 | o[ 4] = (byte) (x5 >>> 0 & 0xff); 1286 | o[ 5] = (byte) (x5 >>> 8 & 0xff); 1287 | o[ 6] = (byte) (x5 >>> 16 & 0xff); 1288 | o[ 7] = (byte) (x5 >>> 24 & 0xff); 1289 | 1290 | o[ 8] = (byte) (x10 >>> 0 & 0xff); 1291 | o[ 9] = (byte) (x10 >>> 8 & 0xff); 1292 | o[10] = (byte) (x10 >>> 16 & 0xff); 1293 | o[11] = (byte) (x10 >>> 24 & 0xff); 1294 | 1295 | o[12] = (byte) (x15 >>> 0 & 0xff); 1296 | o[13] = (byte) (x15 >>> 8 & 0xff); 1297 | o[14] = (byte) (x15 >>> 16 & 0xff); 1298 | o[15] = (byte) (x15 >>> 24 & 0xff); 1299 | 1300 | o[16] = (byte) (x6 >>> 0 & 0xff); 1301 | o[17] = (byte) (x6 >>> 8 & 0xff); 1302 | o[18] = (byte) (x6 >>> 16 & 0xff); 1303 | o[19] = (byte) (x6 >>> 24 & 0xff); 1304 | 1305 | o[20] = (byte) (x7 >>> 0 & 0xff); 1306 | o[21] = (byte) (x7 >>> 8 & 0xff); 1307 | o[22] = (byte) (x7 >>> 16 & 0xff); 1308 | o[23] = (byte) (x7 >>> 24 & 0xff); 1309 | 1310 | o[24] = (byte) (x8 >>> 0 & 0xff); 1311 | o[25] = (byte) (x8 >>> 8 & 0xff); 1312 | o[26] = (byte) (x8 >>> 16 & 0xff); 1313 | o[27] = (byte) (x8 >>> 24 & 0xff); 1314 | 1315 | o[28] = (byte) (x9 >>> 0 & 0xff); 1316 | o[29] = (byte) (x9 >>> 8 & 0xff); 1317 | o[30] = (byte) (x9 >>> 16 & 0xff); 1318 | o[31] = (byte) (x9 >>> 24 & 0xff); 1319 | 1320 | 1321 | /*String dbgt = ""; 1322 | for (int dbg = 0; dbg < o.length; dbg ++) dbgt += " "+o[dbg]; 1323 | Log.d(TAG, "core_hsalsa20 -> "+dbgt); 1324 | */ 1325 | } 1326 | 1327 | public static int crypto_core_salsa20(byte [] out, byte [] in, byte [] k, byte [] c) 1328 | { 1329 | ///core(out,in,k,c,0); 1330 | core_salsa20(out,in,k,c); 1331 | 1332 | ///String dbgt = ""; 1333 | ///for (int dbg = 0; dbg < out.length; dbg ++) dbgt += " "+out[dbg]; 1334 | ///L/og.d(TAG, "crypto_core_salsa20 -> "+dbgt); 1335 | 1336 | return 0; 1337 | } 1338 | 1339 | public static int crypto_core_hsalsa20(byte [] out, byte [] in, byte [] k, byte [] c) 1340 | { 1341 | ///core(out,in,k,c,1); 1342 | core_hsalsa20(out,in,k,c); 1343 | 1344 | ///String dbgt = ""; 1345 | ///for (int dbg = 0; dbg < out.length; dbg ++) dbgt += " "+out[dbg]; 1346 | ///L/og.d(TAG, "crypto_core_hsalsa20 -> "+dbgt); 1347 | 1348 | return 0; 1349 | } 1350 | 1351 | // "expand 32-byte k" 1352 | private static final byte[] sigma = {101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107}; 1353 | 1354 | /*static { 1355 | try { 1356 | sigma = "expand 32-byte k".getBytes("utf-8"); 1357 | } catch (UnsupportedEncodingException e) { 1358 | e.printStackTrace(); 1359 | } 1360 | }*/ 1361 | 1362 | private static int crypto_stream_salsa20_xor(byte [] c,int cpos, byte [] m,int mpos, long b, byte [] n, byte [] k) 1363 | { 1364 | byte [] z = new byte[16], x = new byte[64]; 1365 | int u, i; 1366 | for (i = 0; i < 16; i++) z[i] = 0; 1367 | for (i = 0; i < 8; i++) z[i] = n[i]; 1368 | while (b >= 64) { 1369 | crypto_core_salsa20(x,z,k,sigma); 1370 | for (i = 0; i < 64; i++) c[cpos+i] = (byte) ((m[mpos+i] ^ x[i]) & 0xff); 1371 | u = 1; 1372 | for (i = 8; i < 16; i++) { 1373 | u = u + (z[i] & 0xff) | 0; 1374 | z[i] = (byte) (u & 0xff); 1375 | u >>>= 8; 1376 | } 1377 | b -= 64; 1378 | cpos += 64; 1379 | mpos += 64; 1380 | } 1381 | if (b > 0) { 1382 | crypto_core_salsa20(x,z,k,sigma); 1383 | for (i = 0; i < b; i++) c[cpos+i] = (byte) ((m[mpos+i] ^ x[i]) & 0xff); 1384 | } 1385 | 1386 | ///String dbgt = ""; 1387 | ///for (int dbg = 0; dbg < c.length-cpos; dbg ++) dbgt += " "+c[dbg +cpos]; 1388 | ///Log.d(TAG, "crypto_stream_salsa20_xor, c -> "+dbgt); 1389 | 1390 | return 0; 1391 | } 1392 | 1393 | public static int crypto_stream_salsa20(byte [] c,int cpos, long b, byte [] n, byte [] k) { 1394 | byte [] z = new byte[16], x = new byte[64]; 1395 | int u, i; 1396 | for (i = 0; i < 16; i++) z[i] = 0; 1397 | for (i = 0; i < 8; i++) z[i] = n[i]; 1398 | while (b >= 64) { 1399 | crypto_core_salsa20(x,z,k,sigma); 1400 | for (i = 0; i < 64; i++) c[cpos+i] = x[i]; 1401 | u = 1; 1402 | for (i = 8; i < 16; i++) { 1403 | u = u + (z[i] & 0xff) | 0; 1404 | z[i] = (byte) (u & 0xff); 1405 | u >>>= 8; 1406 | } 1407 | b -= 64; 1408 | cpos += 64; 1409 | } 1410 | if (b > 0) { 1411 | crypto_core_salsa20(x,z,k,sigma); 1412 | for (i = 0; i < b; i++) c[cpos+i] = x[i]; 1413 | } 1414 | 1415 | ///String dbgt = ""; 1416 | ///for (int dbg = 0; dbg < c.length-cpos; dbg ++) dbgt += " "+c[dbg +cpos]; 1417 | ///Log.d(TAG, "crypto_stream_salsa20, c -> "+dbgt); 1418 | 1419 | return 0; 1420 | } 1421 | 1422 | public static int crypto_stream(byte [] c,int cpos, long d, byte [] n, byte [] k) { 1423 | byte [] s = new byte[32]; 1424 | crypto_core_hsalsa20(s,n,k,sigma); 1425 | byte [] sn = new byte[8]; 1426 | for (int i = 0; i < 8; i++) sn[i] = n[i+16]; 1427 | return crypto_stream_salsa20(c,cpos,d,sn,s); 1428 | } 1429 | 1430 | public static int crypto_stream_xor(byte [] c,int cpos, byte [] m,int mpos, long d, byte [] n, byte [] k) { 1431 | byte [] s = new byte[32]; 1432 | 1433 | /*String dbgt = ""; 1434 | for (int dbg = 0; dbg < n.length; dbg ++) dbgt += " "+n[dbg]; 1435 | Log.d(TAG, "crypto_stream_xor, nonce -> "+dbgt); 1436 | 1437 | dbgt = ""; 1438 | for (int dbg = 0; dbg < k.length; dbg ++) dbgt += " "+k[dbg]; 1439 | Log.d(TAG, "crypto_stream_xor, shk -> "+dbgt); 1440 | */ 1441 | 1442 | crypto_core_hsalsa20(s,n,k,sigma); 1443 | byte [] sn = new byte[8]; 1444 | for (int i = 0; i < 8; i++) sn[i] = n[i+16]; 1445 | return crypto_stream_salsa20_xor(c,cpos,m,mpos,d,sn,s); 1446 | } 1447 | 1448 | /* 1449 | * Port of Andrew Moon's Poly1305-donna-16. Public domain. 1450 | * https://github.com/floodyberry/poly1305-donna 1451 | */ 1452 | public static final class poly1305 { 1453 | 1454 | private byte[] buffer; 1455 | private int[] r; 1456 | private int[] h; 1457 | private int[] pad; 1458 | private int leftover; 1459 | private int fin; 1460 | 1461 | public poly1305(byte [] key) { 1462 | this.buffer = new byte[16]; 1463 | this.r = new int[10]; 1464 | this.h = new int[10]; 1465 | this.pad = new int[8]; 1466 | this.leftover = 0; 1467 | this.fin = 0; 1468 | 1469 | int t0, t1, t2, t3, t4, t5, t6, t7; 1470 | 1471 | t0 = key[ 0] & 0xff | (key[ 1] & 0xff) << 8; this.r[0] = ( t0 ) & 0x1fff; 1472 | t1 = key[ 2] & 0xff | (key[ 3] & 0xff) << 8; this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff; 1473 | t2 = key[ 4] & 0xff | (key[ 5] & 0xff) << 8; this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03; 1474 | t3 = key[ 6] & 0xff | (key[ 7] & 0xff) << 8; this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff; 1475 | t4 = key[ 8] & 0xff | (key[ 9] & 0xff) << 8; this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff; 1476 | this.r[5] = ((t4 >>> 1)) & 0x1ffe; 1477 | t5 = key[10] & 0xff | (key[11] & 0xff) << 8; this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff; 1478 | t6 = key[12] & 0xff | (key[13] & 0xff) << 8; this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81; 1479 | t7 = key[14] & 0xff | (key[15] & 0xff) << 8; this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff; 1480 | this.r[9] = ((t7 >>> 5)) & 0x007f; 1481 | 1482 | this.pad[0] = key[16] & 0xff | (key[17] & 0xff) << 8; 1483 | this.pad[1] = key[18] & 0xff | (key[19] & 0xff) << 8; 1484 | this.pad[2] = key[20] & 0xff | (key[21] & 0xff) << 8; 1485 | this.pad[3] = key[22] & 0xff | (key[23] & 0xff) << 8; 1486 | this.pad[4] = key[24] & 0xff | (key[25] & 0xff) << 8; 1487 | this.pad[5] = key[26] & 0xff | (key[27] & 0xff) << 8; 1488 | this.pad[6] = key[28] & 0xff | (key[29] & 0xff) << 8; 1489 | this.pad[7] = key[30] & 0xff | (key[31] & 0xff) << 8; 1490 | } 1491 | 1492 | public poly1305 blocks(byte [] m, int mpos, int bytes) { 1493 | int hibit = this.fin!=0 ? 0 : (1 << 11); 1494 | int t0, t1, t2, t3, t4, t5, t6, t7, c; 1495 | int d0, d1, d2, d3, d4, d5, d6, d7, d8, d9; 1496 | 1497 | int h0 = this.h[0], 1498 | h1 = this.h[1], 1499 | h2 = this.h[2], 1500 | h3 = this.h[3], 1501 | h4 = this.h[4], 1502 | h5 = this.h[5], 1503 | h6 = this.h[6], 1504 | h7 = this.h[7], 1505 | h8 = this.h[8], 1506 | h9 = this.h[9]; 1507 | 1508 | int r0 = this.r[0], 1509 | r1 = this.r[1], 1510 | r2 = this.r[2], 1511 | r3 = this.r[3], 1512 | r4 = this.r[4], 1513 | r5 = this.r[5], 1514 | r6 = this.r[6], 1515 | r7 = this.r[7], 1516 | r8 = this.r[8], 1517 | r9 = this.r[9]; 1518 | 1519 | while (bytes >= 16) { 1520 | t0 = m[mpos+ 0] & 0xff | (m[mpos+ 1] & 0xff) << 8; h0 += ( t0 ) & 0x1fff; 1521 | t1 = m[mpos+ 2] & 0xff | (m[mpos+ 3] & 0xff) << 8; h1 += ((t0 >>> 13) | (t1 << 3)) & 0x1fff; 1522 | t2 = m[mpos+ 4] & 0xff | (m[mpos+ 5] & 0xff) << 8; h2 += ((t1 >>> 10) | (t2 << 6)) & 0x1fff; 1523 | t3 = m[mpos+ 6] & 0xff | (m[mpos+ 7] & 0xff) << 8; h3 += ((t2 >>> 7) | (t3 << 9)) & 0x1fff; 1524 | t4 = m[mpos+ 8] & 0xff | (m[mpos+ 9] & 0xff) << 8; h4 += ((t3 >>> 4) | (t4 << 12)) & 0x1fff; 1525 | h5 += ((t4 >>> 1)) & 0x1fff; 1526 | t5 = m[mpos+10] & 0xff | (m[mpos+11] & 0xff) << 8; h6 += ((t4 >>> 14) | (t5 << 2)) & 0x1fff; 1527 | t6 = m[mpos+12] & 0xff | (m[mpos+13] & 0xff) << 8; h7 += ((t5 >>> 11) | (t6 << 5)) & 0x1fff; 1528 | t7 = m[mpos+14] & 0xff | (m[mpos+15] & 0xff) << 8; h8 += ((t6 >>> 8) | (t7 << 8)) & 0x1fff; 1529 | h9 += ((t7 >>> 5)) | hibit; 1530 | 1531 | c = 0; 1532 | 1533 | d0 = c; 1534 | d0 += h0 * r0; 1535 | d0 += h1 * (5 * r9); 1536 | d0 += h2 * (5 * r8); 1537 | d0 += h3 * (5 * r7); 1538 | d0 += h4 * (5 * r6); 1539 | c = (d0 >>> 13); d0 &= 0x1fff; 1540 | d0 += h5 * (5 * r5); 1541 | d0 += h6 * (5 * r4); 1542 | d0 += h7 * (5 * r3); 1543 | d0 += h8 * (5 * r2); 1544 | d0 += h9 * (5 * r1); 1545 | c += (d0 >>> 13); d0 &= 0x1fff; 1546 | 1547 | d1 = c; 1548 | d1 += h0 * r1; 1549 | d1 += h1 * r0; 1550 | d1 += h2 * (5 * r9); 1551 | d1 += h3 * (5 * r8); 1552 | d1 += h4 * (5 * r7); 1553 | c = (d1 >>> 13); d1 &= 0x1fff; 1554 | d1 += h5 * (5 * r6); 1555 | d1 += h6 * (5 * r5); 1556 | d1 += h7 * (5 * r4); 1557 | d1 += h8 * (5 * r3); 1558 | d1 += h9 * (5 * r2); 1559 | c += (d1 >>> 13); d1 &= 0x1fff; 1560 | 1561 | d2 = c; 1562 | d2 += h0 * r2; 1563 | d2 += h1 * r1; 1564 | d2 += h2 * r0; 1565 | d2 += h3 * (5 * r9); 1566 | d2 += h4 * (5 * r8); 1567 | c = (d2 >>> 13); d2 &= 0x1fff; 1568 | d2 += h5 * (5 * r7); 1569 | d2 += h6 * (5 * r6); 1570 | d2 += h7 * (5 * r5); 1571 | d2 += h8 * (5 * r4); 1572 | d2 += h9 * (5 * r3); 1573 | c += (d2 >>> 13); d2 &= 0x1fff; 1574 | 1575 | d3 = c; 1576 | d3 += h0 * r3; 1577 | d3 += h1 * r2; 1578 | d3 += h2 * r1; 1579 | d3 += h3 * r0; 1580 | d3 += h4 * (5 * r9); 1581 | c = (d3 >>> 13); d3 &= 0x1fff; 1582 | d3 += h5 * (5 * r8); 1583 | d3 += h6 * (5 * r7); 1584 | d3 += h7 * (5 * r6); 1585 | d3 += h8 * (5 * r5); 1586 | d3 += h9 * (5 * r4); 1587 | c += (d3 >>> 13); d3 &= 0x1fff; 1588 | 1589 | d4 = c; 1590 | d4 += h0 * r4; 1591 | d4 += h1 * r3; 1592 | d4 += h2 * r2; 1593 | d4 += h3 * r1; 1594 | d4 += h4 * r0; 1595 | c = (d4 >>> 13); d4 &= 0x1fff; 1596 | d4 += h5 * (5 * r9); 1597 | d4 += h6 * (5 * r8); 1598 | d4 += h7 * (5 * r7); 1599 | d4 += h8 * (5 * r6); 1600 | d4 += h9 * (5 * r5); 1601 | c += (d4 >>> 13); d4 &= 0x1fff; 1602 | 1603 | d5 = c; 1604 | d5 += h0 * r5; 1605 | d5 += h1 * r4; 1606 | d5 += h2 * r3; 1607 | d5 += h3 * r2; 1608 | d5 += h4 * r1; 1609 | c = (d5 >>> 13); d5 &= 0x1fff; 1610 | d5 += h5 * r0; 1611 | d5 += h6 * (5 * r9); 1612 | d5 += h7 * (5 * r8); 1613 | d5 += h8 * (5 * r7); 1614 | d5 += h9 * (5 * r6); 1615 | c += (d5 >>> 13); d5 &= 0x1fff; 1616 | 1617 | d6 = c; 1618 | d6 += h0 * r6; 1619 | d6 += h1 * r5; 1620 | d6 += h2 * r4; 1621 | d6 += h3 * r3; 1622 | d6 += h4 * r2; 1623 | c = (d6 >>> 13); d6 &= 0x1fff; 1624 | d6 += h5 * r1; 1625 | d6 += h6 * r0; 1626 | d6 += h7 * (5 * r9); 1627 | d6 += h8 * (5 * r8); 1628 | d6 += h9 * (5 * r7); 1629 | c += (d6 >>> 13); d6 &= 0x1fff; 1630 | 1631 | d7 = c; 1632 | d7 += h0 * r7; 1633 | d7 += h1 * r6; 1634 | d7 += h2 * r5; 1635 | d7 += h3 * r4; 1636 | d7 += h4 * r3; 1637 | c = (d7 >>> 13); d7 &= 0x1fff; 1638 | d7 += h5 * r2; 1639 | d7 += h6 * r1; 1640 | d7 += h7 * r0; 1641 | d7 += h8 * (5 * r9); 1642 | d7 += h9 * (5 * r8); 1643 | c += (d7 >>> 13); d7 &= 0x1fff; 1644 | 1645 | d8 = c; 1646 | d8 += h0 * r8; 1647 | d8 += h1 * r7; 1648 | d8 += h2 * r6; 1649 | d8 += h3 * r5; 1650 | d8 += h4 * r4; 1651 | c = (d8 >>> 13); d8 &= 0x1fff; 1652 | d8 += h5 * r3; 1653 | d8 += h6 * r2; 1654 | d8 += h7 * r1; 1655 | d8 += h8 * r0; 1656 | d8 += h9 * (5 * r9); 1657 | c += (d8 >>> 13); d8 &= 0x1fff; 1658 | 1659 | d9 = c; 1660 | d9 += h0 * r9; 1661 | d9 += h1 * r8; 1662 | d9 += h2 * r7; 1663 | d9 += h3 * r6; 1664 | d9 += h4 * r5; 1665 | c = (d9 >>> 13); d9 &= 0x1fff; 1666 | d9 += h5 * r4; 1667 | d9 += h6 * r3; 1668 | d9 += h7 * r2; 1669 | d9 += h8 * r1; 1670 | d9 += h9 * r0; 1671 | c += (d9 >>> 13); d9 &= 0x1fff; 1672 | 1673 | c = (((c << 2) + c)) | 0; 1674 | c = (c + d0) | 0; 1675 | d0 = c & 0x1fff; 1676 | c = (c >>> 13); 1677 | d1 += c; 1678 | 1679 | h0 = d0; 1680 | h1 = d1; 1681 | h2 = d2; 1682 | h3 = d3; 1683 | h4 = d4; 1684 | h5 = d5; 1685 | h6 = d6; 1686 | h7 = d7; 1687 | h8 = d8; 1688 | h9 = d9; 1689 | 1690 | mpos += 16; 1691 | bytes -= 16; 1692 | } 1693 | this.h[0] = h0; 1694 | this.h[1] = h1; 1695 | this.h[2] = h2; 1696 | this.h[3] = h3; 1697 | this.h[4] = h4; 1698 | this.h[5] = h5; 1699 | this.h[6] = h6; 1700 | this.h[7] = h7; 1701 | this.h[8] = h8; 1702 | this.h[9] = h9; 1703 | 1704 | return this; 1705 | } 1706 | 1707 | public poly1305 finish(byte [] mac, int macpos) { 1708 | int [] g = new int[10]; 1709 | int c, mask, f, i; 1710 | 1711 | if (this.leftover != 0) { 1712 | i = this.leftover; 1713 | this.buffer[i++] = 1; 1714 | for (; i < 16; i++) this.buffer[i] = 0; 1715 | this.fin = 1; 1716 | this.blocks(this.buffer, 0, 16); 1717 | } 1718 | 1719 | c = this.h[1] >>> 13; 1720 | this.h[1] &= 0x1fff; 1721 | for (i = 2; i < 10; i++) { 1722 | this.h[i] += c; 1723 | c = this.h[i] >>> 13; 1724 | this.h[i] &= 0x1fff; 1725 | } 1726 | this.h[0] += (c * 5); 1727 | c = this.h[0] >>> 13; 1728 | this.h[0] &= 0x1fff; 1729 | this.h[1] += c; 1730 | c = this.h[1] >>> 13; 1731 | this.h[1] &= 0x1fff; 1732 | this.h[2] += c; 1733 | 1734 | g[0] = this.h[0] + 5; 1735 | c = g[0] >>> 13; 1736 | g[0] &= 0x1fff; 1737 | for (i = 1; i < 10; i++) { 1738 | g[i] = this.h[i] + c; 1739 | c = g[i] >>> 13; 1740 | g[i] &= 0x1fff; 1741 | } 1742 | g[9] -= (1 << 13); g[9] &= 0xffff; 1743 | 1744 | /* 1745 | backport from tweetnacl-fast.js https://github.com/dchest/tweetnacl-js/releases/tag/v0.14.3 1746 | <<< 1747 | "The issue was not properly detecting if st->h was >= 2^130 - 5, 1748 | coupled with [testing mistake] not catching the failure. 1749 | The chance of the bug affecting anything in the real world is essentially zero luckily, 1750 | but it's good to have it fixed." 1751 | >>> 1752 | */ 1753 | ///change mask = (g[9] >>> ((2 * 8) - 1)) - 1; to as 1754 | mask = (c ^ 1) - 1; 1755 | mask &= 0xffff; 1756 | /////////////////////////////////////// 1757 | 1758 | for (i = 0; i < 10; i++) g[i] &= mask; 1759 | mask = ~mask; 1760 | for (i = 0; i < 10; i++) this.h[i] = (this.h[i] & mask) | g[i]; 1761 | 1762 | this.h[0] = ((this.h[0] ) | (this.h[1] << 13) ) & 0xffff; 1763 | this.h[1] = ((this.h[1] >>> 3) | (this.h[2] << 10) ) & 0xffff; 1764 | this.h[2] = ((this.h[2] >>> 6) | (this.h[3] << 7) ) & 0xffff; 1765 | this.h[3] = ((this.h[3] >>> 9) | (this.h[4] << 4) ) & 0xffff; 1766 | this.h[4] = ((this.h[4] >>> 12) | (this.h[5] << 1) | (this.h[6] << 14)) & 0xffff; 1767 | this.h[5] = ((this.h[6] >>> 2) | (this.h[7] << 11) ) & 0xffff; 1768 | this.h[6] = ((this.h[7] >>> 5) | (this.h[8] << 8) ) & 0xffff; 1769 | this.h[7] = ((this.h[8] >>> 8) | (this.h[9] << 5) ) & 0xffff; 1770 | 1771 | f = this.h[0] + this.pad[0]; 1772 | this.h[0] = f & 0xffff; 1773 | for (i = 1; i < 8; i++) { 1774 | f = (((this.h[i] + this.pad[i]) | 0) + (f >>> 16)) | 0; 1775 | this.h[i] = f & 0xffff; 1776 | } 1777 | 1778 | mac[macpos+ 0] = (byte) ((this.h[0] >>> 0) & 0xff); 1779 | mac[macpos+ 1] = (byte) ((this.h[0] >>> 8) & 0xff); 1780 | mac[macpos+ 2] = (byte) ((this.h[1] >>> 0) & 0xff); 1781 | mac[macpos+ 3] = (byte) ((this.h[1] >>> 8) & 0xff); 1782 | mac[macpos+ 4] = (byte) ((this.h[2] >>> 0) & 0xff); 1783 | mac[macpos+ 5] = (byte) ((this.h[2] >>> 8) & 0xff); 1784 | mac[macpos+ 6] = (byte) ((this.h[3] >>> 0) & 0xff); 1785 | mac[macpos+ 7] = (byte) ((this.h[3] >>> 8) & 0xff); 1786 | mac[macpos+ 8] = (byte) ((this.h[4] >>> 0) & 0xff); 1787 | mac[macpos+ 9] = (byte) ((this.h[4] >>> 8) & 0xff); 1788 | mac[macpos+10] = (byte) ((this.h[5] >>> 0) & 0xff); 1789 | mac[macpos+11] = (byte) ((this.h[5] >>> 8) & 0xff); 1790 | mac[macpos+12] = (byte) ((this.h[6] >>> 0) & 0xff); 1791 | mac[macpos+13] = (byte) ((this.h[6] >>> 8) & 0xff); 1792 | mac[macpos+14] = (byte) ((this.h[7] >>> 0) & 0xff); 1793 | mac[macpos+15] = (byte) ((this.h[7] >>> 8) & 0xff); 1794 | 1795 | return this; 1796 | } 1797 | 1798 | public poly1305 update(byte [] m, int mpos, int bytes) { 1799 | int i, want; 1800 | 1801 | if (this.leftover != 0) { 1802 | want = (16 - this.leftover); 1803 | if (want > bytes) 1804 | want = bytes; 1805 | for (i = 0; i < want; i++) 1806 | this.buffer[this.leftover + i] = m[mpos+i]; 1807 | bytes -= want; 1808 | mpos += want; 1809 | this.leftover += want; 1810 | if (this.leftover < 16) 1811 | return this; 1812 | this.blocks(buffer, 0, 16); 1813 | this.leftover = 0; 1814 | } 1815 | 1816 | if (bytes >= 16) { 1817 | want = bytes - (bytes % 16); 1818 | this.blocks(m, mpos, want); 1819 | mpos += want; 1820 | bytes -= want; 1821 | } 1822 | 1823 | if (bytes != 0) { 1824 | for (i = 0; i < bytes; i++) 1825 | this.buffer[this.leftover + i] = m[mpos+i]; 1826 | this.leftover += bytes; 1827 | } 1828 | 1829 | return this; 1830 | } 1831 | 1832 | } 1833 | 1834 | private static int crypto_onetimeauth( 1835 | byte[] out,final int outpos, 1836 | byte[] m,final int mpos, 1837 | int n, 1838 | byte [] k) 1839 | { 1840 | poly1305 s = new poly1305(k); 1841 | s.update(m, mpos, n); 1842 | s.finish(out, outpos); 1843 | 1844 | /*String dbgt = ""; 1845 | for (int dbg = 0; dbg < out.length-outpos; dbg ++) dbgt += " "+out[dbg+outpos]; 1846 | Log.d(TAG, "crypto_onetimeauth -> "+dbgt); 1847 | */ 1848 | 1849 | return 0; 1850 | } 1851 | public static int crypto_onetimeauth(byte [] out, byte [] m, int /*long*/ n , byte [] k) { 1852 | return crypto_onetimeauth(out,0, m,0, n, k); 1853 | } 1854 | 1855 | private static int crypto_onetimeauth_verify( 1856 | byte[] h,final int hoff, 1857 | byte[] m,final int moff, 1858 | int /*long*/ n, 1859 | byte [] k) 1860 | { 1861 | byte [] x = new byte[16]; 1862 | crypto_onetimeauth(x,0,m,moff,n,k); 1863 | return crypto_verify_16(h,hoff,x,0); 1864 | } 1865 | public static int crypto_onetimeauth_verify(byte [] h, byte [] m, int /*long*/ n, byte [] k) { 1866 | return crypto_onetimeauth_verify(h,0, m,0, n, k); 1867 | } 1868 | public static int crypto_onetimeauth_verify(byte [] h, byte [] m, byte [] k) { 1869 | return crypto_onetimeauth_verify(h, m, m!=null? m.length:0, k); 1870 | } 1871 | 1872 | public static int crypto_secretbox(byte [] c, byte [] m, int /*long*/ d, byte [] n, byte [] k) 1873 | { 1874 | int i; 1875 | if (d < 32) return -1; 1876 | crypto_stream_xor(c,0,m,0,d,n,k); 1877 | crypto_onetimeauth(c,16, c,32, d-32, c); 1878 | ///for (i = 0; i < 16; i++) c[i] = 0; 1879 | return 0; 1880 | } 1881 | 1882 | public static int crypto_secretbox_open(byte []m,byte []c,int /*long*/ d,byte []n,byte []k) 1883 | { 1884 | int i; 1885 | byte[] x = new byte[32]; 1886 | if (d < 32) return -1; 1887 | crypto_stream(x,0,32,n,k); 1888 | if (crypto_onetimeauth_verify(c,16, c,32, d-32, x) != 0) return -1; 1889 | crypto_stream_xor(m,0,c,0,d,n,k); 1890 | ///for (i = 0; i < 32; i++) m[i] = 0; 1891 | return 0; 1892 | } 1893 | 1894 | private static void set25519(long [] r, long [] a) 1895 | { 1896 | int i; 1897 | for (i = 0; i < 16; i ++) r[i]=a[i]; 1898 | } 1899 | 1900 | private static void car25519(long [] o) 1901 | { 1902 | int i; 1903 | long v, c = 1; 1904 | for (i = 0; i < 16; i++) { 1905 | v = o[i] + c + 65535; 1906 | c = v>>16; 1907 | o[i] = v - c * 65536; 1908 | } 1909 | o[0] += c-1 + 37 * (c-1); 1910 | } 1911 | 1912 | private static void sel25519( 1913 | long[] p, 1914 | long[] q, 1915 | int b) 1916 | { 1917 | sel25519(p,0, q,0, b); 1918 | } 1919 | private static void sel25519( 1920 | long[] p,final int poff, 1921 | long[] q,final int qoff, 1922 | int b) 1923 | { 1924 | long t, c = ~(b-1); 1925 | for (int i = 0; i < 16; i++) { 1926 | t = c & (p[i+poff] ^ q[i+qoff]); 1927 | p[i+poff] ^= t; 1928 | q[i+qoff] ^= t; 1929 | } 1930 | } 1931 | 1932 | private static void pack25519(byte [] o, long [] n,final int noff) 1933 | { 1934 | int i, j, b; 1935 | long [] m = new long[16], t = new long[16]; 1936 | for (i = 0; i < 16; i++) t[i] = n[i+noff]; 1937 | car25519(t); 1938 | car25519(t); 1939 | car25519(t); 1940 | for (j = 0; j < 2; j++) { 1941 | m[0] = t[0] - 0xffed; 1942 | for (i = 1; i < 15; i++) { 1943 | m[i] = t[i] - 0xffff - ((m[i-1]>>16) & 1); 1944 | m[i-1] &= 0xffff; 1945 | } 1946 | m[15] = t[15] - 0x7fff - ((m[14]>>16) & 1); 1947 | b = (int) ((m[15]>>16) & 1); 1948 | m[14] &= 0xffff; 1949 | sel25519(t,0, m,0, 1-b); 1950 | } 1951 | for (i = 0; i < 16; i++) { 1952 | o[2*i] = (byte) (t[i] & 0xff); 1953 | o[2*i+1] = (byte) (t[i]>>8); 1954 | } 1955 | } 1956 | 1957 | private static int neq25519(long [] a, long [] b) { 1958 | return neq25519(a,0, b,0); 1959 | } 1960 | private static int neq25519(long [] a,final int aoff, long [] b,final int boff) 1961 | { 1962 | byte [] c = new byte[32], d = new byte[32]; 1963 | pack25519(c, a,aoff); 1964 | pack25519(d, b,boff); 1965 | return crypto_verify_32(c, 0, d, 0); 1966 | } 1967 | 1968 | private static byte par25519(long [] a) 1969 | { 1970 | return par25519(a,0); 1971 | } 1972 | private static byte par25519(long [] a,final int aoff) 1973 | { 1974 | byte [] d = new byte[32]; 1975 | pack25519(d, a,aoff); 1976 | return (byte) (d[0] & 1); 1977 | } 1978 | 1979 | private static void unpack25519(long [] o, byte [] n) 1980 | { 1981 | int i; 1982 | for (i = 0; i < 16; i ++) o[i]=(n[2*i]&0xff)+((long)((n[2*i+1]<<8)&0xffff)); 1983 | o[15] &= 0x7fff; 1984 | } 1985 | 1986 | private static void A( 1987 | long [] o, 1988 | long [] a, 1989 | long [] b) 1990 | { 1991 | A(o,0, a,0, b,0); 1992 | } 1993 | private static void A( 1994 | long [] o,final int ooff, 1995 | long [] a,final int aoff, 1996 | long [] b,final int boff) 1997 | { 1998 | int i; 1999 | for (i = 0; i < 16; i ++) o[i+ooff] = a[i+aoff] + b[i+boff]; 2000 | } 2001 | 2002 | private static void Z( 2003 | long [] o, 2004 | long [] a, 2005 | long [] b) 2006 | { 2007 | Z(o,0, a,0, b,0); 2008 | } 2009 | private static void Z( 2010 | long [] o,final int ooff, 2011 | long [] a,final int aoff, 2012 | long [] b,final int boff) 2013 | { 2014 | int i; 2015 | for (i = 0; i < 16; i ++) o[i+ooff] = a[i+aoff] - b[i+boff]; 2016 | } 2017 | 2018 | private static void M( 2019 | long [] o, 2020 | long [] a, 2021 | long [] b) 2022 | { 2023 | M(o,0, a,0, b,0); 2024 | } 2025 | private static void M( 2026 | long [] o,final int ooff, 2027 | long [] a,final int aoff, 2028 | long [] b,final int boff) 2029 | { 2030 | long v, c, 2031 | t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, 2032 | t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, 2033 | t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, 2034 | t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0, 2035 | b0 = b[0 +boff], 2036 | b1 = b[1 +boff], 2037 | b2 = b[2 +boff], 2038 | b3 = b[3 +boff], 2039 | b4 = b[4 +boff], 2040 | b5 = b[5 +boff], 2041 | b6 = b[6 +boff], 2042 | b7 = b[7 +boff], 2043 | b8 = b[8 +boff], 2044 | b9 = b[9 +boff], 2045 | b10 = b[10 +boff], 2046 | b11 = b[11 +boff], 2047 | b12 = b[12 +boff], 2048 | b13 = b[13 +boff], 2049 | b14 = b[14 +boff], 2050 | b15 = b[15 +boff]; 2051 | 2052 | v = a[0 +aoff]; 2053 | t0 += v * b0; 2054 | t1 += v * b1; 2055 | t2 += v * b2; 2056 | t3 += v * b3; 2057 | t4 += v * b4; 2058 | t5 += v * b5; 2059 | t6 += v * b6; 2060 | t7 += v * b7; 2061 | t8 += v * b8; 2062 | t9 += v * b9; 2063 | t10 += v * b10; 2064 | t11 += v * b11; 2065 | t12 += v * b12; 2066 | t13 += v * b13; 2067 | t14 += v * b14; 2068 | t15 += v * b15; 2069 | v = a[1 +aoff]; 2070 | t1 += v * b0; 2071 | t2 += v * b1; 2072 | t3 += v * b2; 2073 | t4 += v * b3; 2074 | t5 += v * b4; 2075 | t6 += v * b5; 2076 | t7 += v * b6; 2077 | t8 += v * b7; 2078 | t9 += v * b8; 2079 | t10 += v * b9; 2080 | t11 += v * b10; 2081 | t12 += v * b11; 2082 | t13 += v * b12; 2083 | t14 += v * b13; 2084 | t15 += v * b14; 2085 | t16 += v * b15; 2086 | v = a[2 +aoff]; 2087 | t2 += v * b0; 2088 | t3 += v * b1; 2089 | t4 += v * b2; 2090 | t5 += v * b3; 2091 | t6 += v * b4; 2092 | t7 += v * b5; 2093 | t8 += v * b6; 2094 | t9 += v * b7; 2095 | t10 += v * b8; 2096 | t11 += v * b9; 2097 | t12 += v * b10; 2098 | t13 += v * b11; 2099 | t14 += v * b12; 2100 | t15 += v * b13; 2101 | t16 += v * b14; 2102 | t17 += v * b15; 2103 | v = a[3 +aoff]; 2104 | t3 += v * b0; 2105 | t4 += v * b1; 2106 | t5 += v * b2; 2107 | t6 += v * b3; 2108 | t7 += v * b4; 2109 | t8 += v * b5; 2110 | t9 += v * b6; 2111 | t10 += v * b7; 2112 | t11 += v * b8; 2113 | t12 += v * b9; 2114 | t13 += v * b10; 2115 | t14 += v * b11; 2116 | t15 += v * b12; 2117 | t16 += v * b13; 2118 | t17 += v * b14; 2119 | t18 += v * b15; 2120 | v = a[4 +aoff]; 2121 | t4 += v * b0; 2122 | t5 += v * b1; 2123 | t6 += v * b2; 2124 | t7 += v * b3; 2125 | t8 += v * b4; 2126 | t9 += v * b5; 2127 | t10 += v * b6; 2128 | t11 += v * b7; 2129 | t12 += v * b8; 2130 | t13 += v * b9; 2131 | t14 += v * b10; 2132 | t15 += v * b11; 2133 | t16 += v * b12; 2134 | t17 += v * b13; 2135 | t18 += v * b14; 2136 | t19 += v * b15; 2137 | v = a[5 +aoff]; 2138 | t5 += v * b0; 2139 | t6 += v * b1; 2140 | t7 += v * b2; 2141 | t8 += v * b3; 2142 | t9 += v * b4; 2143 | t10 += v * b5; 2144 | t11 += v * b6; 2145 | t12 += v * b7; 2146 | t13 += v * b8; 2147 | t14 += v * b9; 2148 | t15 += v * b10; 2149 | t16 += v * b11; 2150 | t17 += v * b12; 2151 | t18 += v * b13; 2152 | t19 += v * b14; 2153 | t20 += v * b15; 2154 | v = a[6 +aoff]; 2155 | t6 += v * b0; 2156 | t7 += v * b1; 2157 | t8 += v * b2; 2158 | t9 += v * b3; 2159 | t10 += v * b4; 2160 | t11 += v * b5; 2161 | t12 += v * b6; 2162 | t13 += v * b7; 2163 | t14 += v * b8; 2164 | t15 += v * b9; 2165 | t16 += v * b10; 2166 | t17 += v * b11; 2167 | t18 += v * b12; 2168 | t19 += v * b13; 2169 | t20 += v * b14; 2170 | t21 += v * b15; 2171 | v = a[7 +aoff]; 2172 | t7 += v * b0; 2173 | t8 += v * b1; 2174 | t9 += v * b2; 2175 | t10 += v * b3; 2176 | t11 += v * b4; 2177 | t12 += v * b5; 2178 | t13 += v * b6; 2179 | t14 += v * b7; 2180 | t15 += v * b8; 2181 | t16 += v * b9; 2182 | t17 += v * b10; 2183 | t18 += v * b11; 2184 | t19 += v * b12; 2185 | t20 += v * b13; 2186 | t21 += v * b14; 2187 | t22 += v * b15; 2188 | v = a[8 +aoff]; 2189 | t8 += v * b0; 2190 | t9 += v * b1; 2191 | t10 += v * b2; 2192 | t11 += v * b3; 2193 | t12 += v * b4; 2194 | t13 += v * b5; 2195 | t14 += v * b6; 2196 | t15 += v * b7; 2197 | t16 += v * b8; 2198 | t17 += v * b9; 2199 | t18 += v * b10; 2200 | t19 += v * b11; 2201 | t20 += v * b12; 2202 | t21 += v * b13; 2203 | t22 += v * b14; 2204 | t23 += v * b15; 2205 | v = a[9 +aoff]; 2206 | t9 += v * b0; 2207 | t10 += v * b1; 2208 | t11 += v * b2; 2209 | t12 += v * b3; 2210 | t13 += v * b4; 2211 | t14 += v * b5; 2212 | t15 += v * b6; 2213 | t16 += v * b7; 2214 | t17 += v * b8; 2215 | t18 += v * b9; 2216 | t19 += v * b10; 2217 | t20 += v * b11; 2218 | t21 += v * b12; 2219 | t22 += v * b13; 2220 | t23 += v * b14; 2221 | t24 += v * b15; 2222 | v = a[10 +aoff]; 2223 | t10 += v * b0; 2224 | t11 += v * b1; 2225 | t12 += v * b2; 2226 | t13 += v * b3; 2227 | t14 += v * b4; 2228 | t15 += v * b5; 2229 | t16 += v * b6; 2230 | t17 += v * b7; 2231 | t18 += v * b8; 2232 | t19 += v * b9; 2233 | t20 += v * b10; 2234 | t21 += v * b11; 2235 | t22 += v * b12; 2236 | t23 += v * b13; 2237 | t24 += v * b14; 2238 | t25 += v * b15; 2239 | v = a[11 +aoff]; 2240 | t11 += v * b0; 2241 | t12 += v * b1; 2242 | t13 += v * b2; 2243 | t14 += v * b3; 2244 | t15 += v * b4; 2245 | t16 += v * b5; 2246 | t17 += v * b6; 2247 | t18 += v * b7; 2248 | t19 += v * b8; 2249 | t20 += v * b9; 2250 | t21 += v * b10; 2251 | t22 += v * b11; 2252 | t23 += v * b12; 2253 | t24 += v * b13; 2254 | t25 += v * b14; 2255 | t26 += v * b15; 2256 | v = a[12 +aoff]; 2257 | t12 += v * b0; 2258 | t13 += v * b1; 2259 | t14 += v * b2; 2260 | t15 += v * b3; 2261 | t16 += v * b4; 2262 | t17 += v * b5; 2263 | t18 += v * b6; 2264 | t19 += v * b7; 2265 | t20 += v * b8; 2266 | t21 += v * b9; 2267 | t22 += v * b10; 2268 | t23 += v * b11; 2269 | t24 += v * b12; 2270 | t25 += v * b13; 2271 | t26 += v * b14; 2272 | t27 += v * b15; 2273 | v = a[13 +aoff]; 2274 | t13 += v * b0; 2275 | t14 += v * b1; 2276 | t15 += v * b2; 2277 | t16 += v * b3; 2278 | t17 += v * b4; 2279 | t18 += v * b5; 2280 | t19 += v * b6; 2281 | t20 += v * b7; 2282 | t21 += v * b8; 2283 | t22 += v * b9; 2284 | t23 += v * b10; 2285 | t24 += v * b11; 2286 | t25 += v * b12; 2287 | t26 += v * b13; 2288 | t27 += v * b14; 2289 | t28 += v * b15; 2290 | v = a[14 +aoff]; 2291 | t14 += v * b0; 2292 | t15 += v * b1; 2293 | t16 += v * b2; 2294 | t17 += v * b3; 2295 | t18 += v * b4; 2296 | t19 += v * b5; 2297 | t20 += v * b6; 2298 | t21 += v * b7; 2299 | t22 += v * b8; 2300 | t23 += v * b9; 2301 | t24 += v * b10; 2302 | t25 += v * b11; 2303 | t26 += v * b12; 2304 | t27 += v * b13; 2305 | t28 += v * b14; 2306 | t29 += v * b15; 2307 | v = a[15 +aoff]; 2308 | t15 += v * b0; 2309 | t16 += v * b1; 2310 | t17 += v * b2; 2311 | t18 += v * b3; 2312 | t19 += v * b4; 2313 | t20 += v * b5; 2314 | t21 += v * b6; 2315 | t22 += v * b7; 2316 | t23 += v * b8; 2317 | t24 += v * b9; 2318 | t25 += v * b10; 2319 | t26 += v * b11; 2320 | t27 += v * b12; 2321 | t28 += v * b13; 2322 | t29 += v * b14; 2323 | t30 += v * b15; 2324 | 2325 | t0 += 38 * t16; 2326 | t1 += 38 * t17; 2327 | t2 += 38 * t18; 2328 | t3 += 38 * t19; 2329 | t4 += 38 * t20; 2330 | t5 += 38 * t21; 2331 | t6 += 38 * t22; 2332 | t7 += 38 * t23; 2333 | t8 += 38 * t24; 2334 | t9 += 38 * t25; 2335 | t10 += 38 * t26; 2336 | t11 += 38 * t27; 2337 | t12 += 38 * t28; 2338 | t13 += 38 * t29; 2339 | t14 += 38 * t30; 2340 | // t15 left as is 2341 | 2342 | // first car 2343 | c = 1; 2344 | v = t0 + c + 65535; c = v >> 16; t0 = v - c * 65536; 2345 | v = t1 + c + 65535; c = v >> 16; t1 = v - c * 65536; 2346 | v = t2 + c + 65535; c = v >> 16; t2 = v - c * 65536; 2347 | v = t3 + c + 65535; c = v >> 16; t3 = v - c * 65536; 2348 | v = t4 + c + 65535; c = v >> 16; t4 = v - c * 65536; 2349 | v = t5 + c + 65535; c = v >> 16; t5 = v - c * 65536; 2350 | v = t6 + c + 65535; c = v >> 16; t6 = v - c * 65536; 2351 | v = t7 + c + 65535; c = v >> 16; t7 = v - c * 65536; 2352 | v = t8 + c + 65535; c = v >> 16; t8 = v - c * 65536; 2353 | v = t9 + c + 65535; c = v >> 16; t9 = v - c * 65536; 2354 | v = t10 + c + 65535; c = v >> 16; t10 = v - c * 65536; 2355 | v = t11 + c + 65535; c = v >> 16; t11 = v - c * 65536; 2356 | v = t12 + c + 65535; c = v >> 16; t12 = v - c * 65536; 2357 | v = t13 + c + 65535; c = v >> 16; t13 = v - c * 65536; 2358 | v = t14 + c + 65535; c = v >> 16; t14 = v - c * 65536; 2359 | v = t15 + c + 65535; c = v >> 16; t15 = v - c * 65536; 2360 | t0 += c-1 + 37 * (c-1); 2361 | 2362 | // second car 2363 | c = 1; 2364 | v = t0 + c + 65535; c = v >> 16; t0 = v - c * 65536; 2365 | v = t1 + c + 65535; c = v >> 16; t1 = v - c * 65536; 2366 | v = t2 + c + 65535; c = v >> 16; t2 = v - c * 65536; 2367 | v = t3 + c + 65535; c = v >> 16; t3 = v - c * 65536; 2368 | v = t4 + c + 65535; c = v >> 16; t4 = v - c * 65536; 2369 | v = t5 + c + 65535; c = v >> 16; t5 = v - c * 65536; 2370 | v = t6 + c + 65535; c = v >> 16; t6 = v - c * 65536; 2371 | v = t7 + c + 65535; c = v >> 16; t7 = v - c * 65536; 2372 | v = t8 + c + 65535; c = v >> 16; t8 = v - c * 65536; 2373 | v = t9 + c + 65535; c = v >> 16; t9 = v - c * 65536; 2374 | v = t10 + c + 65535; c = v >> 16; t10 = v - c * 65536; 2375 | v = t11 + c + 65535; c = v >> 16; t11 = v - c * 65536; 2376 | v = t12 + c + 65535; c = v >> 16; t12 = v - c * 65536; 2377 | v = t13 + c + 65535; c = v >> 16; t13 = v - c * 65536; 2378 | v = t14 + c + 65535; c = v >> 16; t14 = v - c * 65536; 2379 | v = t15 + c + 65535; c = v >> 16; t15 = v - c * 65536; 2380 | t0 += c-1 + 37 * (c-1); 2381 | 2382 | o[ 0 +ooff] = t0; 2383 | o[ 1 +ooff] = t1; 2384 | o[ 2 +ooff] = t2; 2385 | o[ 3 +ooff] = t3; 2386 | o[ 4 +ooff] = t4; 2387 | o[ 5 +ooff] = t5; 2388 | o[ 6 +ooff] = t6; 2389 | o[ 7 +ooff] = t7; 2390 | o[ 8 +ooff] = t8; 2391 | o[ 9 +ooff] = t9; 2392 | o[10 +ooff] = t10; 2393 | o[11 +ooff] = t11; 2394 | o[12 +ooff] = t12; 2395 | o[13 +ooff] = t13; 2396 | o[14 +ooff] = t14; 2397 | o[15 +ooff] = t15; 2398 | } 2399 | 2400 | private static void S( 2401 | long [] o, 2402 | long [] a) 2403 | { 2404 | S(o,0, a,0); 2405 | } 2406 | private static void S( 2407 | long [] o,final int ooff, 2408 | long [] a,final int aoff) 2409 | { 2410 | M(o,ooff, a,aoff, a,aoff); 2411 | } 2412 | 2413 | private static void inv25519( 2414 | long [] o,final int ooff, 2415 | long [] i,final int ioff) 2416 | { 2417 | long [] c = new long[16]; 2418 | int a; 2419 | for (a = 0; a < 16; a++) c[a] = i[a+ioff]; 2420 | for (a = 253; a >= 0; a--) { 2421 | S(c,0, c,0); 2422 | if(a != 2 && a != 4) M(c,0, c,0, i,ioff); 2423 | } 2424 | for (a = 0; a < 16; a++) o[a+ooff] = c[a]; 2425 | } 2426 | 2427 | private static void pow2523(long [] o,long [] i) 2428 | { 2429 | long [] c = new long[16]; 2430 | int a; 2431 | 2432 | for (a = 0; a < 16; a ++) c[a]=i[a]; 2433 | 2434 | for(a=250;a>=0;a--) { 2435 | S(c,0, c,0); 2436 | if(a!=1) M(c,0, c,0, i,0); 2437 | } 2438 | 2439 | for (a = 0; a < 16; a ++) o[a]=c[a]; 2440 | } 2441 | 2442 | public static int crypto_scalarmult(byte []q,byte []n,byte []p) 2443 | { 2444 | byte [] z = new byte[32]; 2445 | long [] x = new long[80]; 2446 | int r, i; 2447 | long [] a = new long[16], b = new long[16], c = new long[16], 2448 | d = new long[16], e = new long[16], f = new long[16]; 2449 | for (i = 0; i < 31; i++) z[i] = n[i]; 2450 | z[31]=(byte) (((n[31]&127)|64) & 0xff); 2451 | z[0]&=248; 2452 | unpack25519(x,p); 2453 | for (i = 0; i < 16; i++) { 2454 | b[i]=x[i]; 2455 | d[i]=a[i]=c[i]=0; 2456 | } 2457 | a[0]=d[0]=1; 2458 | for (i=254;i>=0;--i) { 2459 | r=(z[i>>>3]>>>(i&7))&1; 2460 | sel25519(a,b,r); 2461 | sel25519(c,d,r); 2462 | A(e,a,c); 2463 | Z(a,a,c); 2464 | A(c,b,d); 2465 | Z(b,b,d); 2466 | S(d,e); 2467 | S(f,a); 2468 | M(a,c,a); 2469 | M(c,b,e); 2470 | A(e,a,c); 2471 | Z(a,a,c); 2472 | S(b,a); 2473 | Z(c,d,f); 2474 | M(a,c,_121665); 2475 | A(a,a,d); 2476 | M(c,c,a); 2477 | M(a,d,f); 2478 | M(d,b,x); 2479 | S(b,e); 2480 | sel25519(a,b,r); 2481 | sel25519(c,d,r); 2482 | } 2483 | for (i = 0; i < 16; i++) { 2484 | x[i+16]=a[i]; 2485 | x[i+32]=c[i]; 2486 | x[i+48]=b[i]; 2487 | x[i+64]=d[i]; 2488 | } 2489 | inv25519(x,32, x,32); 2490 | M(x,16, x,16, x,32); 2491 | pack25519(q, x,16); 2492 | 2493 | return 0; 2494 | } 2495 | 2496 | public static int crypto_scalarmult_base(byte []q,byte []n) 2497 | { 2498 | return crypto_scalarmult(q,n,_9); 2499 | } 2500 | 2501 | public static int crypto_box_keypair(byte [] y, byte [] x) 2502 | { 2503 | randombytes(x,32); 2504 | return crypto_scalarmult_base(y,x); 2505 | } 2506 | 2507 | public static int crypto_box_beforenm(byte []k,byte []y,byte []x) 2508 | { 2509 | byte[] s = new byte[32]; 2510 | crypto_scalarmult(s,x,y); 2511 | 2512 | /*String dbgt = ""; 2513 | for (int dbg = 0; dbg < s.length; dbg ++) dbgt += " "+s[dbg]; 2514 | Log.d(TAG, "crypto_box_beforenm -> "+dbgt); 2515 | 2516 | dbgt = ""; 2517 | for (int dbg = 0; dbg < x.length; dbg ++) dbgt += " "+x[dbg]; 2518 | Log.d(TAG, "crypto_box_beforenm, x -> "+dbgt); 2519 | dbgt = ""; 2520 | for (int dbg = 0; dbg < y.length; dbg ++) dbgt += " "+y[dbg]; 2521 | Log.d(TAG, "crypto_box_beforenm, y -> "+dbgt); 2522 | */ 2523 | 2524 | return crypto_core_hsalsa20(k, _0, s, sigma); 2525 | } 2526 | 2527 | public static int crypto_box_afternm(byte []c,byte []m,int /*long*/ d,byte []n,byte []k) 2528 | { 2529 | return crypto_secretbox(c,m,d,n,k); 2530 | } 2531 | 2532 | public static int crypto_box_open_afternm(byte []m,byte []c,int /*long*/ d,byte []n,byte []k) 2533 | { 2534 | return crypto_secretbox_open(m,c,d,n,k); 2535 | } 2536 | 2537 | public static int crypto_box(byte []c,byte []m,int /*long*/ d,byte []n,byte []y,byte []x) 2538 | { 2539 | byte[] k = new byte[32]; 2540 | 2541 | ///L/og.d(TAG, "crypto_box start ..."); 2542 | 2543 | crypto_box_beforenm(k,y,x); 2544 | return crypto_box_afternm(c,m,d,n,k); 2545 | } 2546 | 2547 | public static int crypto_box_open(byte []m,byte []c,int /*long*/ d,byte []n,byte []y,byte []x) 2548 | { 2549 | byte[] k = new byte[32]; 2550 | crypto_box_beforenm(k,y,x); 2551 | return crypto_box_open_afternm(m,c,d,n,k); 2552 | } 2553 | 2554 | private static final long K[] = { 2555 | 0x428a2f98d728ae22L, 0x7137449123ef65cdL, 0xb5c0fbcfec4d3b2fL, 0xe9b5dba58189dbbcL, 2556 | 0x3956c25bf348b538L, 0x59f111f1b605d019L, 0x923f82a4af194f9bL, 0xab1c5ed5da6d8118L, 2557 | 0xd807aa98a3030242L, 0x12835b0145706fbeL, 0x243185be4ee4b28cL, 0x550c7dc3d5ffb4e2L, 2558 | 0x72be5d74f27b896fL, 0x80deb1fe3b1696b1L, 0x9bdc06a725c71235L, 0xc19bf174cf692694L, 2559 | 0xe49b69c19ef14ad2L, 0xefbe4786384f25e3L, 0x0fc19dc68b8cd5b5L, 0x240ca1cc77ac9c65L, 2560 | 0x2de92c6f592b0275L, 0x4a7484aa6ea6e483L, 0x5cb0a9dcbd41fbd4L, 0x76f988da831153b5L, 2561 | 0x983e5152ee66dfabL, 0xa831c66d2db43210L, 0xb00327c898fb213fL, 0xbf597fc7beef0ee4L, 2562 | 0xc6e00bf33da88fc2L, 0xd5a79147930aa725L, 0x06ca6351e003826fL, 0x142929670a0e6e70L, 2563 | 0x27b70a8546d22ffcL, 0x2e1b21385c26c926L, 0x4d2c6dfc5ac42aedL, 0x53380d139d95b3dfL, 2564 | 0x650a73548baf63deL, 0x766a0abb3c77b2a8L, 0x81c2c92e47edaee6L, 0x92722c851482353bL, 2565 | 0xa2bfe8a14cf10364L, 0xa81a664bbc423001L, 0xc24b8b70d0f89791L, 0xc76c51a30654be30L, 2566 | 0xd192e819d6ef5218L, 0xd69906245565a910L, 0xf40e35855771202aL, 0x106aa07032bbd1b8L, 2567 | 0x19a4c116b8d2d0c8L, 0x1e376c085141ab53L, 0x2748774cdf8eeb99L, 0x34b0bcb5e19b48a8L, 2568 | 0x391c0cb3c5c95a63L, 0x4ed8aa4ae3418acbL, 0x5b9cca4f7763e373L, 0x682e6ff3d6b2b8a3L, 2569 | 0x748f82ee5defb2fcL, 0x78a5636f43172f60L, 0x84c87814a1f0ab72L, 0x8cc702081a6439ecL, 2570 | 0x90befffa23631e28L, 0xa4506cebde82bde9L, 0xbef9a3f7b2c67915L, 0xc67178f2e372532bL, 2571 | 0xca273eceea26619cL, 0xd186b8c721c0c207L, 0xeada7dd6cde0eb1eL, 0xf57d4f7fee6ed178L, 2572 | 0x06f067aa72176fbaL, 0x0a637dc5a2c898a6L, 0x113f9804bef90daeL, 0x1b710b35131c471bL, 2573 | 0x28db77f523047d84L, 0x32caab7b40c72493L, 0x3c9ebe0a15c9bebcL, 0x431d67c49c100d4cL, 2574 | 0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL, 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L 2575 | }; 2576 | 2577 | private static int crypto_hashblocks_hl(int [] hh,int [] hl, byte [] m,final int moff, int n) { 2578 | 2579 | ///String dbgt = ""; 2580 | ///for (int dbg = 0; dbg < n; dbg ++) dbgt += " "+m[dbg+moff]; 2581 | ///Log.d(TAG, "crypto_hashblocks_hl m/"+n + "-> "+dbgt); 2582 | 2583 | int [] wh = new int[16], wl = new int[16]; 2584 | int bh0, bh1, bh2, bh3, bh4, bh5, bh6, bh7, 2585 | bl0, bl1, bl2, bl3, bl4, bl5, bl6, bl7, 2586 | th, tl, h, l, i, j, a, b, c, d; 2587 | 2588 | int ah0 = hh[0], 2589 | ah1 = hh[1], 2590 | ah2 = hh[2], 2591 | ah3 = hh[3], 2592 | ah4 = hh[4], 2593 | ah5 = hh[5], 2594 | ah6 = hh[6], 2595 | ah7 = hh[7], 2596 | 2597 | al0 = hl[0], 2598 | al1 = hl[1], 2599 | al2 = hl[2], 2600 | al3 = hl[3], 2601 | al4 = hl[4], 2602 | al5 = hl[5], 2603 | al6 = hl[6], 2604 | al7 = hl[7]; 2605 | 2606 | int pos = 0; 2607 | while (n >= 128) { 2608 | for (i = 0; i < 16; i++) { 2609 | j = 8 * i + pos; 2610 | wh[i] = ((m[j+0+moff]&0xff) << 24) | ((m[j+1+moff]&0xff) << 16) | ((m[j+2+moff]&0xff) << 8) | ((m[j+3+moff]&0xff) << 0); 2611 | wl[i] = ((m[j+4+moff]&0xff) << 24) | ((m[j+5+moff]&0xff) << 16) | ((m[j+6+moff]&0xff) << 8) | ((m[j+7+moff]&0xff) << 0); 2612 | } 2613 | for (i = 0; i < 80; i++) { 2614 | bh0 = ah0; 2615 | bh1 = ah1; 2616 | bh2 = ah2; 2617 | bh3 = ah3; 2618 | bh4 = ah4; 2619 | bh5 = ah5; 2620 | bh6 = ah6; 2621 | bh7 = ah7; 2622 | 2623 | bl0 = al0; 2624 | bl1 = al1; 2625 | bl2 = al2; 2626 | bl3 = al3; 2627 | bl4 = al4; 2628 | bl5 = al5; 2629 | bl6 = al6; 2630 | bl7 = al7; 2631 | 2632 | // add 2633 | h = ah7; 2634 | l = al7; 2635 | 2636 | a = l & 0xffff; b = l >>> 16; 2637 | c = h & 0xffff; d = h >>> 16; 2638 | 2639 | // Sigma1 2640 | h = ((ah4 >>> 14) | (al4 << (32-14))) ^ ((ah4 >>> 18) | (al4 << (32-18))) ^ ((al4 >>> (41-32)) | (ah4 << (32-(41-32)))); 2641 | l = ((al4 >>> 14) | (ah4 << (32-14))) ^ ((al4 >>> 18) | (ah4 << (32-18))) ^ ((ah4 >>> (41-32)) | (al4 << (32-(41-32)))); 2642 | 2643 | a += l & 0xffff; b += l >>> 16; 2644 | c += h & 0xffff; d += h >>> 16; 2645 | 2646 | // Ch 2647 | h = (ah4 & ah5) ^ (~ah4 & ah6); 2648 | l = (al4 & al5) ^ (~al4 & al6); 2649 | 2650 | a += l & 0xffff; b += l >>> 16; 2651 | c += h & 0xffff; d += h >>> 16; 2652 | 2653 | // K 2654 | ///h = K[i*2]; 2655 | ///l = K[i*2+1]; 2656 | h = (int) ((K[i]>>>32) & 0xffffffff); 2657 | l = (int) ((K[i]>>> 0) & 0xffffffff); 2658 | 2659 | ///Log.d(TAG, "i"+i + ",h:0x"+Integer.toHexString(h) + ",l:0x"+Integer.toHexString(l)); 2660 | 2661 | a += l & 0xffff; b += l >>> 16; 2662 | c += h & 0xffff; d += h >>> 16; 2663 | 2664 | // w 2665 | h = wh[i%16]; 2666 | l = wl[i%16]; 2667 | 2668 | a += l & 0xffff; b += l >>> 16; 2669 | c += h & 0xffff; d += h >>> 16; 2670 | 2671 | b += a >>> 16; 2672 | c += b >>> 16; 2673 | d += c >>> 16; 2674 | 2675 | th = c & 0xffff | d << 16; 2676 | tl = a & 0xffff | b << 16; 2677 | 2678 | // add 2679 | h = th; 2680 | l = tl; 2681 | 2682 | a = l & 0xffff; b = l >>> 16; 2683 | c = h & 0xffff; d = h >>> 16; 2684 | 2685 | // Sigma0 2686 | h = ((ah0 >>> 28) | (al0 << (32-28))) ^ ((al0 >>> (34-32)) | (ah0 << (32-(34-32)))) ^ ((al0 >>> (39-32)) | (ah0 << (32-(39-32)))); 2687 | l = ((al0 >>> 28) | (ah0 << (32-28))) ^ ((ah0 >>> (34-32)) | (al0 << (32-(34-32)))) ^ ((ah0 >>> (39-32)) | (al0 << (32-(39-32)))); 2688 | 2689 | a += l & 0xffff; b += l >>> 16; 2690 | c += h & 0xffff; d += h >>> 16; 2691 | 2692 | // Maj 2693 | h = (ah0 & ah1) ^ (ah0 & ah2) ^ (ah1 & ah2); 2694 | l = (al0 & al1) ^ (al0 & al2) ^ (al1 & al2); 2695 | 2696 | a += l & 0xffff; b += l >>> 16; 2697 | c += h & 0xffff; d += h >>> 16; 2698 | 2699 | b += a >>> 16; 2700 | c += b >>> 16; 2701 | d += c >>> 16; 2702 | 2703 | bh7 = (c & 0xffff) | (d << 16); 2704 | bl7 = (a & 0xffff) | (b << 16); 2705 | 2706 | // add 2707 | h = bh3; 2708 | l = bl3; 2709 | 2710 | a = l & 0xffff; b = l >>> 16; 2711 | c = h & 0xffff; d = h >>> 16; 2712 | 2713 | h = th; 2714 | l = tl; 2715 | 2716 | a += l & 0xffff; b += l >>> 16; 2717 | c += h & 0xffff; d += h >>> 16; 2718 | 2719 | b += a >>> 16; 2720 | c += b >>> 16; 2721 | d += c >>> 16; 2722 | 2723 | bh3 = (c & 0xffff) | (d << 16); 2724 | bl3 = (a & 0xffff) | (b << 16); 2725 | 2726 | ah1 = bh0; 2727 | ah2 = bh1; 2728 | ah3 = bh2; 2729 | ah4 = bh3; 2730 | ah5 = bh4; 2731 | ah6 = bh5; 2732 | ah7 = bh6; 2733 | ah0 = bh7; 2734 | 2735 | al1 = bl0; 2736 | al2 = bl1; 2737 | al3 = bl2; 2738 | al4 = bl3; 2739 | al5 = bl4; 2740 | al6 = bl5; 2741 | al7 = bl6; 2742 | al0 = bl7; 2743 | 2744 | if (i%16 == 15) { 2745 | for (j = 0; j < 16; j++) { 2746 | // add 2747 | h = wh[j]; 2748 | l = wl[j]; 2749 | 2750 | a = l & 0xffff; b = l >>> 16; 2751 | c = h & 0xffff; d = h >>> 16; 2752 | 2753 | h = wh[(j+9)%16]; 2754 | l = wl[(j+9)%16]; 2755 | 2756 | a += l & 0xffff; b += l >>> 16; 2757 | c += h & 0xffff; d += h >>> 16; 2758 | 2759 | // sigma0 2760 | th = wh[(j+1)%16]; 2761 | tl = wl[(j+1)%16]; 2762 | h = ((th >>> 1) | (tl << (32-1))) ^ ((th >>> 8) | (tl << (32-8))) ^ (th >>> 7); 2763 | l = ((tl >>> 1) | (th << (32-1))) ^ ((tl >>> 8) | (th << (32-8))) ^ ((tl >>> 7) | (th << (32-7))); 2764 | 2765 | a += l & 0xffff; b += l >>> 16; 2766 | c += h & 0xffff; d += h >>> 16; 2767 | 2768 | // sigma1 2769 | th = wh[(j+14)%16]; 2770 | tl = wl[(j+14)%16]; 2771 | h = ((th >>> 19) | (tl << (32-19))) ^ ((tl >>> (61-32)) | (th << (32-(61-32)))) ^ (th >>> 6); 2772 | l = ((tl >>> 19) | (th << (32-19))) ^ ((th >>> (61-32)) | (tl << (32-(61-32)))) ^ ((tl >>> 6) | (th << (32-6))); 2773 | 2774 | a += l & 0xffff; b += l >>> 16; 2775 | c += h & 0xffff; d += h >>> 16; 2776 | 2777 | b += a >>> 16; 2778 | c += b >>> 16; 2779 | d += c >>> 16; 2780 | 2781 | wh[j] = (c & 0xffff) | (d << 16); 2782 | wl[j] = (a & 0xffff) | (b << 16); 2783 | } 2784 | } 2785 | } 2786 | 2787 | // add 2788 | h = ah0; 2789 | l = al0; 2790 | 2791 | a = l & 0xffff; b = l >>> 16; 2792 | c = h & 0xffff; d = h >>> 16; 2793 | 2794 | h = hh[0]; 2795 | l = hl[0]; 2796 | 2797 | a += l & 0xffff; b += l >>> 16; 2798 | c += h & 0xffff; d += h >>> 16; 2799 | 2800 | b += a >>> 16; 2801 | c += b >>> 16; 2802 | d += c >>> 16; 2803 | 2804 | hh[0] = ah0 = (c & 0xffff) | (d << 16); 2805 | hl[0] = al0 = (a & 0xffff) | (b << 16); 2806 | 2807 | h = ah1; 2808 | l = al1; 2809 | 2810 | a = l & 0xffff; b = l >>> 16; 2811 | c = h & 0xffff; d = h >>> 16; 2812 | 2813 | h = hh[1]; 2814 | l = hl[1]; 2815 | 2816 | a += l & 0xffff; b += l >>> 16; 2817 | c += h & 0xffff; d += h >>> 16; 2818 | 2819 | b += a >>> 16; 2820 | c += b >>> 16; 2821 | d += c >>> 16; 2822 | 2823 | hh[1] = ah1 = (c & 0xffff) | (d << 16); 2824 | hl[1] = al1 = (a & 0xffff) | (b << 16); 2825 | 2826 | h = ah2; 2827 | l = al2; 2828 | 2829 | a = l & 0xffff; b = l >>> 16; 2830 | c = h & 0xffff; d = h >>> 16; 2831 | 2832 | h = hh[2]; 2833 | l = hl[2]; 2834 | 2835 | a += l & 0xffff; b += l >>> 16; 2836 | c += h & 0xffff; d += h >>> 16; 2837 | 2838 | b += a >>> 16; 2839 | c += b >>> 16; 2840 | d += c >>> 16; 2841 | 2842 | hh[2] = ah2 = (c & 0xffff) | (d << 16); 2843 | hl[2] = al2 = (a & 0xffff) | (b << 16); 2844 | 2845 | h = ah3; 2846 | l = al3; 2847 | 2848 | a = l & 0xffff; b = l >>> 16; 2849 | c = h & 0xffff; d = h >>> 16; 2850 | 2851 | h = hh[3]; 2852 | l = hl[3]; 2853 | 2854 | a += l & 0xffff; b += l >>> 16; 2855 | c += h & 0xffff; d += h >>> 16; 2856 | 2857 | b += a >>> 16; 2858 | c += b >>> 16; 2859 | d += c >>> 16; 2860 | 2861 | hh[3] = ah3 = (c & 0xffff) | (d << 16); 2862 | hl[3] = al3 = (a & 0xffff) | (b << 16); 2863 | 2864 | h = ah4; 2865 | l = al4; 2866 | 2867 | a = l & 0xffff; b = l >>> 16; 2868 | c = h & 0xffff; d = h >>> 16; 2869 | 2870 | h = hh[4]; 2871 | l = hl[4]; 2872 | 2873 | a += l & 0xffff; b += l >>> 16; 2874 | c += h & 0xffff; d += h >>> 16; 2875 | 2876 | b += a >>> 16; 2877 | c += b >>> 16; 2878 | d += c >>> 16; 2879 | 2880 | hh[4] = ah4 = (c & 0xffff) | (d << 16); 2881 | hl[4] = al4 = (a & 0xffff) | (b << 16); 2882 | 2883 | h = ah5; 2884 | l = al5; 2885 | 2886 | a = l & 0xffff; b = l >>> 16; 2887 | c = h & 0xffff; d = h >>> 16; 2888 | 2889 | h = hh[5]; 2890 | l = hl[5]; 2891 | 2892 | a += l & 0xffff; b += l >>> 16; 2893 | c += h & 0xffff; d += h >>> 16; 2894 | 2895 | b += a >>> 16; 2896 | c += b >>> 16; 2897 | d += c >>> 16; 2898 | 2899 | hh[5] = ah5 = (c & 0xffff) | (d << 16); 2900 | hl[5] = al5 = (a & 0xffff) | (b << 16); 2901 | 2902 | h = ah6; 2903 | l = al6; 2904 | 2905 | a = l & 0xffff; b = l >>> 16; 2906 | c = h & 0xffff; d = h >>> 16; 2907 | 2908 | h = hh[6]; 2909 | l = hl[6]; 2910 | 2911 | a += l & 0xffff; b += l >>> 16; 2912 | c += h & 0xffff; d += h >>> 16; 2913 | 2914 | b += a >>> 16; 2915 | c += b >>> 16; 2916 | d += c >>> 16; 2917 | 2918 | hh[6] = ah6 = (c & 0xffff) | (d << 16); 2919 | hl[6] = al6 = (a & 0xffff) | (b << 16); 2920 | 2921 | h = ah7; 2922 | l = al7; 2923 | 2924 | a = l & 0xffff; b = l >>> 16; 2925 | c = h & 0xffff; d = h >>> 16; 2926 | 2927 | h = hh[7]; 2928 | l = hl[7]; 2929 | 2930 | a += l & 0xffff; b += l >>> 16; 2931 | c += h & 0xffff; d += h >>> 16; 2932 | 2933 | b += a >>> 16; 2934 | c += b >>> 16; 2935 | d += c >>> 16; 2936 | 2937 | hh[7] = ah7 = (c & 0xffff) | (d << 16); 2938 | hl[7] = al7 = (a & 0xffff) | (b << 16); 2939 | 2940 | pos += 128; 2941 | n -= 128; 2942 | 2943 | /*dbgt = ""; 2944 | for (int dbg = 0; dbg < hh.length; dbg ++) dbgt += " "+hh[dbg]; 2945 | Log.d(TAG, "\ncrypto_hashblocks_hl hh -> "+dbgt); 2946 | 2947 | dbgt = ""; 2948 | for (int dbg = 0; dbg < hl.length; dbg ++) dbgt += " "+hl[dbg]; 2949 | Log.d(TAG, "\ncrypto_hashblocks_hl hl -> "+dbgt);*/ 2950 | } 2951 | 2952 | return n; 2953 | } 2954 | 2955 | // TBD 64bits of n 2956 | ///int crypto_hash(byte [] out, byte [] m, long n) 2957 | public static int crypto_hash(byte [] out, byte [] m,final int moff, int n) 2958 | { 2959 | int [] hh = new int[8], 2960 | hl = new int[8]; 2961 | byte [] x = new byte[256]; 2962 | int i, b = n; 2963 | long u; 2964 | 2965 | hh[0] = 0x6a09e667; 2966 | hh[1] = 0xbb67ae85; 2967 | hh[2] = 0x3c6ef372; 2968 | hh[3] = 0xa54ff53a; 2969 | hh[4] = 0x510e527f; 2970 | hh[5] = 0x9b05688c; 2971 | hh[6] = 0x1f83d9ab; 2972 | hh[7] = 0x5be0cd19; 2973 | 2974 | hl[0] = 0xf3bcc908; 2975 | hl[1] = 0x84caa73b; 2976 | hl[2] = 0xfe94f82b; 2977 | hl[3] = 0x5f1d36f1; 2978 | hl[4] = 0xade682d1; 2979 | hl[5] = 0x2b3e6c1f; 2980 | hl[6] = 0xfb41bd6b; 2981 | hl[7] = 0x137e2179; 2982 | 2983 | if (n >= 128) { 2984 | crypto_hashblocks_hl(hh, hl, m,moff, n); 2985 | n %= 128; 2986 | } 2987 | 2988 | for (i = 0; i < n; i++) x[i] = m[b-n+i +moff]; 2989 | x[n] = (byte) 128; 2990 | 2991 | n = 256-128*(n<112?1:0); 2992 | x[n-9] = 0; 2993 | 2994 | ts64(x, n-8, b<<3/*(b / 0x20000000) | 0, b << 3*/); 2995 | 2996 | crypto_hashblocks_hl(hh, hl, x,0, n); 2997 | 2998 | for (i = 0; i < 8; i++) { 2999 | u = hh[i]; u <<= 32; u |= hl[i]&0xffffffffL; 3000 | ts64(out, 8*i, u); 3001 | } 3002 | 3003 | return 0; 3004 | } 3005 | public static int crypto_hash(byte [] out, byte [] m) { 3006 | return crypto_hash(out, m,0, m!=null? m.length : 0); 3007 | } 3008 | 3009 | // gf: long[16] 3010 | ///private static void add(gf p[4],gf q[4]) 3011 | private static void add(long [] p[], long [] q[]) 3012 | { 3013 | long [] a = new long[16]; 3014 | long [] b = new long[16]; 3015 | long [] c = new long[16]; 3016 | long [] d = new long[16]; 3017 | long [] t = new long[16]; 3018 | long [] e = new long[16]; 3019 | long [] f = new long[16]; 3020 | long [] g = new long[16]; 3021 | long [] h = new long[16]; 3022 | 3023 | 3024 | long [] p0 = p[0]; 3025 | long [] p1 = p[1]; 3026 | long [] p2 = p[2]; 3027 | long [] p3 = p[3]; 3028 | 3029 | long [] q0 = q[0]; 3030 | long [] q1 = q[1]; 3031 | long [] q2 = q[2]; 3032 | long [] q3 = q[3]; 3033 | 3034 | Z(a,0, p1,0, p0,0); 3035 | Z(t,0, q1,0, q0,0); 3036 | M(a,0, a,0, t,0); 3037 | A(b,0, p0,0, p1,0); 3038 | A(t,0, q0,0, q1,0); 3039 | M(b,0, b,0, t,0); 3040 | M(c,0, p3,0, q3,0); 3041 | M(c,0, c,0, D2,0); 3042 | M(d,0, p2,0, q2,0); 3043 | 3044 | A(d,0, d,0, d,0); 3045 | Z(e,0, b,0, a,0); 3046 | Z(f,0, d,0, c,0); 3047 | A(g,0, d,0, c,0); 3048 | A(h,0, b,0, a,0); 3049 | 3050 | M(p0,0, e,0, f,0); 3051 | M(p1,0, h,0, g,0); 3052 | M(p2,0, g,0, f,0); 3053 | M(p3,0, e,0, h,0); 3054 | } 3055 | 3056 | private static void cswap(long [] p[], long [] q[], byte b) 3057 | { 3058 | int i; 3059 | 3060 | for (i = 0; i < 4; i ++) 3061 | sel25519(p[i],0, q[i],0, b); 3062 | } 3063 | 3064 | private static void pack(byte [] r, long [] p[]) 3065 | { 3066 | long [] tx = new long[16]; 3067 | long [] ty = new long[16]; 3068 | long [] zi = new long[16]; 3069 | 3070 | inv25519(zi,0, p[2],0); 3071 | 3072 | M(tx,0, p[0],0, zi,0); 3073 | M(ty,0, p[1],0, zi,0); 3074 | 3075 | pack25519(r, ty,0); 3076 | 3077 | r[31] ^= par25519(tx,0) << 7; 3078 | } 3079 | 3080 | private static void scalarmult(long [] p[], long [] q[], byte[] s,final int soff) 3081 | { 3082 | int i; 3083 | 3084 | set25519(p[0],gf0); 3085 | set25519(p[1],gf1); 3086 | set25519(p[2],gf1); 3087 | set25519(p[3],gf0); 3088 | 3089 | for (i = 255;i >= 0;--i) { 3090 | byte b = (byte) ((s[i/8+soff] >>> (i&7))&1); 3091 | 3092 | cswap(p,q,b); 3093 | add(q,p); 3094 | add(p,p); 3095 | cswap(p,q,b); 3096 | } 3097 | 3098 | ///String dbgt = ""; 3099 | ///for (int dbg = 0; dbg < p.length; dbg ++) for (int dd = 0; dd < p[dbg].length; dd ++) dbgt += " "+p[dbg][dd]; 3100 | ///L/og.d(TAG, "scalarmult -> "+dbgt); 3101 | } 3102 | 3103 | private static void scalarbase(long[][] p, byte[] s, final int soff) 3104 | { 3105 | long [] [] q = new long [4] []; 3106 | 3107 | q[0] = new long [16]; 3108 | q[1] = new long [16]; 3109 | q[2] = new long [16]; 3110 | q[3] = new long [16]; 3111 | 3112 | set25519(q[0],X); 3113 | set25519(q[1],Y); 3114 | set25519(q[2],gf1); 3115 | M(q[3],0, X,0, Y,0); 3116 | scalarmult(p,q, s,soff); 3117 | } 3118 | 3119 | public static int crypto_sign_keypair(byte [] pk, byte [] sk, boolean seeded) { 3120 | byte [] d = new byte[64]; 3121 | long [] [] p = new long [4] []; 3122 | 3123 | p[0] = new long [16]; 3124 | p[1] = new long [16]; 3125 | p[2] = new long [16]; 3126 | p[3] = new long [16]; 3127 | 3128 | int i; 3129 | 3130 | if (!seeded) { 3131 | randombytes(sk, 32); 3132 | } 3133 | crypto_hash(d, sk,0, 32); 3134 | d[0] &= 248; 3135 | d[31] &= 127; 3136 | d[31] |= 64; 3137 | 3138 | scalarbase(p, d,0); 3139 | pack(pk, p); 3140 | 3141 | for (i = 0; i < 32; i++) sk[i+32] = pk[i]; 3142 | return 0; 3143 | } 3144 | 3145 | private static final long L[] = { 3146 | 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 3147 | 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 3148 | 0, 0, 0, 0, 0, 0, 0, 0, 3149 | 0, 0, 0, 0, 0, 0, 0, 0x10 3150 | }; 3151 | 3152 | private static void modL(byte[] r,final int roff, long x[]) 3153 | { 3154 | long carry; 3155 | int i, j; 3156 | 3157 | for (i = 63;i >= 32;--i) { 3158 | carry = 0; 3159 | for (j = i - 32;j < i - 12;++j) { 3160 | x[j] += carry - 16 * x[i] * L[j - (i - 32)]; 3161 | carry = (x[j] + 128) >> 8; 3162 | x[j] -= carry << 8; 3163 | } 3164 | x[j] += carry; 3165 | x[i] = 0; 3166 | } 3167 | carry = 0; 3168 | 3169 | for (j = 0; j < 32; j ++) { 3170 | x[j] += carry - (x[31] >> 4) * L[j]; 3171 | carry = x[j] >> 8; 3172 | x[j] &= 255; 3173 | } 3174 | 3175 | for (j = 0; j < 32; j ++) x[j] -= carry * L[j]; 3176 | 3177 | for (i = 0; i < 32; i ++) { 3178 | x[i+1] += x[i] >> 8; 3179 | r[i+roff] = (byte) (x[i] & 255); 3180 | } 3181 | } 3182 | 3183 | private static void reduce(byte [] r) 3184 | { 3185 | long[] x = new long [64]; 3186 | int i; 3187 | 3188 | for (i = 0; i < 64; i ++) x[i] = (long) (r[i]&0xff); 3189 | 3190 | for (i = 0; i < 64; i ++) r[i] = 0; 3191 | 3192 | modL(r,0, x); 3193 | } 3194 | 3195 | // TBD... 64bits of n 3196 | ///int crypto_sign(byte [] sm, long * smlen, byte [] m, long n, byte [] sk) 3197 | public static int crypto_sign(byte [] sm, long dummy /* *smlen not used*/, byte [] m,final int moff, int/*long*/ n, byte [] sk) 3198 | { 3199 | byte[] d = new byte[64], h = new byte[64], r = new byte[64]; 3200 | 3201 | int i, j; 3202 | long [] x = new long[64]; 3203 | 3204 | long [] [] p = new long [4] []; 3205 | p[0] = new long [16]; 3206 | p[1] = new long [16]; 3207 | p[2] = new long [16]; 3208 | p[3] = new long [16]; 3209 | 3210 | crypto_hash(d, sk,0, 32); 3211 | d[0] &= 248; 3212 | d[31] &= 127; 3213 | d[31] |= 64; 3214 | 3215 | ///*smlen = n+64; 3216 | 3217 | for (i = 0; i < n; i ++) sm[64 + i] = m[i+moff]; 3218 | 3219 | for (i = 0; i < 32; i ++) sm[32 + i] = d[32 + i]; 3220 | 3221 | crypto_hash(r, sm,32, n+32); 3222 | reduce(r); 3223 | scalarbase(p, r,0); 3224 | pack(sm,p); 3225 | 3226 | for (i = 0; i < 32; i ++) sm[i+32] = sk[i+32]; 3227 | crypto_hash(h, sm,0, n + 64); 3228 | reduce(h); 3229 | 3230 | for (i = 0; i < 64; i ++) x[i] = 0; 3231 | 3232 | for (i = 0; i < 32; i ++) x[i] = (long) (r[i]&0xff); 3233 | 3234 | for (i = 0; i < 32; i ++) for (j = 0; j < 32; j ++) x[i+j] += (h[i]&0xff) * (long) (d[j]&0xff); 3235 | 3236 | modL(sm,32, x); 3237 | 3238 | return 0; 3239 | } 3240 | 3241 | private static int unpackneg(long [] r[], byte p[]) 3242 | { 3243 | long [] t = new long [16]; 3244 | long [] chk = new long [16]; 3245 | long [] num = new long [16]; 3246 | long [] den = new long [16]; 3247 | long [] den2 = new long [16]; 3248 | long [] den4 = new long [16]; 3249 | long [] den6 = new long [16]; 3250 | 3251 | set25519(r[2], gf1); 3252 | unpack25519(r[1], p); 3253 | S(num, r[1]); 3254 | M(den, num, D); 3255 | Z(num, num, r[2]); 3256 | A(den, r[2], den); 3257 | 3258 | S(den2, den); 3259 | S(den4, den2); 3260 | M(den6, den4, den2); 3261 | M(t, den6, num); 3262 | M(t, t, den); 3263 | 3264 | pow2523(t, t); 3265 | M(t, t, num); 3266 | M(t, t, den); 3267 | M(t, t, den); 3268 | M(r[0], t, den); 3269 | 3270 | S(chk, r[0]); 3271 | M(chk, chk, den); 3272 | if (neq25519(chk, num)!=0) M(r[0], r[0], I); 3273 | 3274 | S(chk, r[0]); 3275 | M(chk, chk, den); 3276 | if (neq25519(chk, num)!=0) return -1; 3277 | 3278 | if (par25519(r[0]) == ((p[31]&0xFF)>>>7)) Z(r[0], gf0, r[0]); 3279 | 3280 | M(r[3], r[0], r[1]); 3281 | 3282 | return 0; 3283 | } 3284 | 3285 | /// TBD 64bits of mlen 3286 | ///int crypto_sign_open(byte []m,long *mlen,byte []sm,long n,byte []pk) 3287 | public static int crypto_sign_open(byte [] m, long dummy /* *mlen not used*/, byte [] sm,final int smoff, int/*long*/ n, byte []pk) 3288 | { 3289 | int i; 3290 | byte[] t = new byte[32], h = new byte[64]; 3291 | 3292 | long [] [] p = new long [4] []; 3293 | p[0] = new long [16]; 3294 | p[1] = new long [16]; 3295 | p[2] = new long [16]; 3296 | p[3] = new long [16]; 3297 | 3298 | long [] [] q = new long [4] []; 3299 | q[0] = new long [16]; 3300 | q[1] = new long [16]; 3301 | q[2] = new long [16]; 3302 | q[3] = new long [16]; 3303 | 3304 | ///*mlen = -1; 3305 | 3306 | if (n < 64) return -1; 3307 | 3308 | if (unpackneg(q,pk)!=0) return -1; 3309 | 3310 | for (i = 0; i < n; i ++) m[i] = sm[i+smoff]; 3311 | 3312 | for (i = 0; i < 32; i ++) m[i+32] = pk[i]; 3313 | 3314 | crypto_hash(h, m,0, n); 3315 | 3316 | reduce(h); 3317 | scalarmult(p,q, h,0); 3318 | 3319 | scalarbase(q, sm,32+smoff); 3320 | add(p,q); 3321 | pack(t,p); 3322 | 3323 | n -= 64; 3324 | if (crypto_verify_32(sm,smoff, t,0)!=0) { 3325 | // optimizing it 3326 | ///for (i = 0; i < n; i ++) m[i] = 0; 3327 | return -1; 3328 | } 3329 | 3330 | // TBD optimizing ... 3331 | ///for (i = 0; i < n; i ++) m[i] = sm[i + 64 + smoff]; 3332 | ///*mlen = n; 3333 | 3334 | return 0; 3335 | } 3336 | 3337 | /* 3338 | * @description 3339 | * Java SecureRandom generator 3340 | * */ 3341 | private static final SecureRandom jrandom = new SecureRandom(); 3342 | 3343 | public static byte[] randombytes(byte [] x) { 3344 | jrandom.nextBytes(x); 3345 | return x; 3346 | } 3347 | 3348 | public static byte[] randombytes(int len) { 3349 | return randombytes(new byte[len]); 3350 | } 3351 | 3352 | public static byte[] randombytes(byte [] x, int len) { 3353 | byte [] b = randombytes(len); 3354 | System.arraycopy(b, 0, x, 0, len); 3355 | return x; 3356 | } 3357 | 3358 | /* 3359 | public static byte[] randombytes(byte [] x, int len) { 3360 | int ret = len % 8; 3361 | long rnd; 3362 | for (int i = 0; i < len-ret; i += 8) { 3363 | rnd = jrandom.nextLong(); 3364 | x[i+0] = (byte) (rnd >>> 0); 3365 | x[i+1] = (byte) (rnd >>> 8); 3366 | x[i+2] = (byte) (rnd >>> 16); 3367 | x[i+3] = (byte) (rnd >>> 24); 3368 | x[i+4] = (byte) (rnd >>> 32); 3369 | x[i+5] = (byte) (rnd >>> 40); 3370 | x[i+6] = (byte) (rnd >>> 48); 3371 | x[i+7] = (byte) (rnd >>> 56); 3372 | } 3373 | if (ret > 0) { 3374 | rnd = jrandom.nextLong(); 3375 | for (int i = len-ret; i < len; i ++) 3376 | x[i] = (byte) (rnd >>> 8*i); 3377 | } 3378 | return x; 3379 | } 3380 | */ 3381 | 3382 | public static byte[] makeBoxNonce() { 3383 | return randombytes(Box.nonceLength); 3384 | } 3385 | 3386 | public static byte[] makeSecretBoxNonce() { 3387 | return randombytes(SecretBox.nonceLength); 3388 | } 3389 | 3390 | public static String hexEncodeToString( byte [] raw ) { 3391 | String HEXES = "0123456789ABCDEF"; 3392 | final StringBuilder hex = new StringBuilder( 2 * raw.length ); 3393 | for ( final byte b : raw ) { 3394 | hex.append(HEXES.charAt((b & 0xF0) >> 4)) 3395 | .append(HEXES.charAt((b & 0x0F))); 3396 | } 3397 | return hex.toString(); 3398 | } 3399 | 3400 | public static byte[] hexDecode(String s) { 3401 | byte[] b = new byte[s.length() / 2]; 3402 | for (int i = 0; i < s.length(); i += 2) { 3403 | b[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 3404 | + Character.digit(s.charAt(i+1), 16)); 3405 | } 3406 | return b; 3407 | } 3408 | 3409 | // public static boolean java.util.Arrays.equals(array1, array2); 3410 | 3411 | // Check that a pubkey is on the curve. 3412 | public static int is_on_curve(byte p[]) { 3413 | long[] r[] = { new long [16], new long [16], new long [16], new long [16] }; 3414 | 3415 | long [] t = new long [16]; 3416 | long [] chk = new long [16]; 3417 | long [] num = new long [16]; 3418 | long [] den = new long [16]; 3419 | long [] den2 = new long [16]; 3420 | long [] den4 = new long [16]; 3421 | long [] den6 = new long [16]; 3422 | 3423 | set25519(r[2], gf1); 3424 | unpack25519(r[1], p); 3425 | S(num, r[1]); 3426 | M(den, num, D); 3427 | Z(num, num, r[2]); 3428 | A(den, r[2], den); 3429 | 3430 | S(den2, den); 3431 | S(den4, den2); 3432 | M(den6, den4, den2); 3433 | M(t, den6, num); 3434 | M(t, t, den); 3435 | 3436 | pow2523(t, t); 3437 | M(t, t, num); 3438 | M(t, t, den); 3439 | M(t, t, den); 3440 | M(r[0], t, den); 3441 | 3442 | S(chk, r[0]); 3443 | M(chk, chk, den); 3444 | if (neq25519(chk, num)!=0) M(r[0], r[0], I); 3445 | 3446 | S(chk, r[0]); 3447 | M(chk, chk, den); 3448 | if (neq25519(chk, num)!=0) return 0; 3449 | return 1; 3450 | } 3451 | 3452 | } -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/ws/SignatureNotification.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.ws; 2 | 3 | public class SignatureNotification { 4 | private Object error; 5 | 6 | public SignatureNotification(Object error) { 7 | this.error = error; 8 | } 9 | 10 | public Object getError() { 11 | return error; 12 | } 13 | 14 | public boolean hasError() { 15 | return error != null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/ws/SubscriptionWebSocketClient.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.ws; 2 | 3 | import java.net.URI; 4 | import java.net.URISyntaxException; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import com.squareup.moshi.JsonAdapter; 11 | import com.squareup.moshi.Moshi; 12 | import com.squareup.moshi.Types; 13 | 14 | import org.java_websocket.client.WebSocketClient; 15 | import org.java_websocket.handshake.ServerHandshake; 16 | import org.p2p.solanaj.rpc.types.RpcNotificationResult; 17 | import org.p2p.solanaj.rpc.types.RpcRequest; 18 | import org.p2p.solanaj.rpc.types.RpcResponse; 19 | import org.p2p.solanaj.ws.listeners.NotificationEventListener; 20 | 21 | public class SubscriptionWebSocketClient extends WebSocketClient { 22 | 23 | private class SubscriptionParams { 24 | RpcRequest request; 25 | NotificationEventListener listener; 26 | 27 | SubscriptionParams(RpcRequest request, NotificationEventListener listener) { 28 | this.request = request; 29 | this.listener = listener; 30 | } 31 | } 32 | 33 | private static SubscriptionWebSocketClient instance; 34 | 35 | private Map subscriptions = new HashMap<>(); 36 | private Map subscriptionIds = new HashMap<>(); 37 | private Map subscriptionLinsteners = new HashMap<>(); 38 | 39 | public static SubscriptionWebSocketClient getInstance(String endpoint) { 40 | URI endpointURI; 41 | URI serverURI; 42 | 43 | try { 44 | endpointURI = new URI(endpoint); 45 | serverURI = new URI(endpointURI.getScheme() == "https" ? "wss" : "ws" + "://" + endpointURI.getHost()); 46 | } catch (URISyntaxException e) { 47 | throw new IllegalArgumentException(e); 48 | } 49 | 50 | if (instance == null) { 51 | instance = new SubscriptionWebSocketClient(serverURI); 52 | } 53 | 54 | if (!instance.isOpen()) { 55 | instance.connect(); 56 | } 57 | 58 | return instance; 59 | 60 | } 61 | 62 | public SubscriptionWebSocketClient(URI serverURI) { 63 | super(serverURI); 64 | 65 | } 66 | 67 | public void accountSubscribe(String key, NotificationEventListener listener) { 68 | List params = new ArrayList(); 69 | params.add(key); 70 | 71 | RpcRequest rpcRequest = new RpcRequest("accountSubscribe", params); 72 | 73 | subscriptions.put(rpcRequest.getId(), new SubscriptionParams(rpcRequest, listener)); 74 | subscriptionIds.put(rpcRequest.getId(), null); 75 | 76 | updateSubscriptions(); 77 | } 78 | 79 | public void signatureSubscribe(String signature, NotificationEventListener listener) { 80 | List params = new ArrayList(); 81 | params.add(signature); 82 | 83 | RpcRequest rpcRequest = new RpcRequest("signatureSubscribe", params); 84 | 85 | subscriptions.put(rpcRequest.getId(), new SubscriptionParams(rpcRequest, listener)); 86 | subscriptionIds.put(rpcRequest.getId(), null); 87 | 88 | updateSubscriptions(); 89 | } 90 | 91 | @Override 92 | public void onOpen(ServerHandshake handshakedata) { 93 | updateSubscriptions(); 94 | } 95 | 96 | @SuppressWarnings({ "rawtypes" }) 97 | @Override 98 | public void onMessage(String message) { 99 | JsonAdapter> resultAdapter = new Moshi.Builder().build() 100 | .adapter(Types.newParameterizedType(RpcResponse.class, Long.class)); 101 | 102 | try { 103 | RpcResponse rpcResult = resultAdapter.fromJson(message); 104 | String rpcResultId = rpcResult.getId(); 105 | if (rpcResultId != null) { 106 | if (subscriptionIds.containsKey(rpcResultId)) { 107 | subscriptionIds.put(rpcResultId, rpcResult.getResult()); 108 | subscriptionLinsteners.put(rpcResult.getResult(), subscriptions.get(rpcResultId).listener); 109 | subscriptions.remove(rpcResultId); 110 | } 111 | } else { 112 | JsonAdapter notificationResultAdapter = new Moshi.Builder().build() 113 | .adapter(RpcNotificationResult.class); 114 | RpcNotificationResult result = notificationResultAdapter.fromJson(message); 115 | NotificationEventListener listener = subscriptionLinsteners.get(result.getParams().getSubscription()); 116 | 117 | Map value = (Map) result.getParams().getResult().getValue(); 118 | 119 | switch (result.getMethod()) { 120 | case "signatureNotification": 121 | listener.onNotifiacationEvent(new SignatureNotification(value.get("err"))); 122 | break; 123 | case "accountNotification": 124 | listener.onNotifiacationEvent(value); 125 | break; 126 | } 127 | } 128 | } catch (Exception ex) { 129 | System.out.println(ex); 130 | } 131 | } 132 | 133 | @Override 134 | public void onClose(int code, String reason, boolean remote) { 135 | System.out.println( 136 | "Connection closed by " + (remote ? "remote peer" : "us") + " Code: " + code + " Reason: " + reason); 137 | 138 | } 139 | 140 | @Override 141 | public void onError(Exception ex) { 142 | ex.printStackTrace(); 143 | } 144 | 145 | private void updateSubscriptions() { 146 | if (isOpen() && subscriptions.size() > 0) { 147 | JsonAdapter rpcRequestJsonAdapter = new Moshi.Builder().build().adapter(RpcRequest.class); 148 | 149 | for (SubscriptionParams sub : subscriptions.values()) { 150 | send(rpcRequestJsonAdapter.toJson(sub.request)); 151 | } 152 | } 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/org/p2p/solanaj/ws/listeners/NotificationEventListener.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.ws.listeners; 2 | 3 | public interface NotificationEventListener { 4 | public void onNotifiacationEvent(Object data); 5 | } 6 | -------------------------------------------------------------------------------- /src/test/java/org/p2p/solanaj/core/AccountKeysListTest.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import org.junit.Test; 9 | import org.p2p.solanaj.programs.SystemProgram; 10 | 11 | public class AccountKeysListTest { 12 | 13 | @Test 14 | public void getSortedList() { 15 | AccountKeysList list = new AccountKeysList(); 16 | 17 | list.addAll( 18 | Arrays.asList(new AccountMeta(new PublicKey("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3"), true, true), 19 | new AccountMeta(new PublicKey("7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7"), false, true), 20 | new AccountMeta(new PublicKey("HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds"), false, false), 21 | new AccountMeta(new PublicKey("6Cust2JhvweKLh4CVo1dt21s2PJ86uNGkziudpkNPaCj"), false, false), 22 | new AccountMeta(SystemProgram.PROGRAM_ID, false, false), 23 | new AccountMeta(new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), false, false), 24 | new AccountMeta(Sysvar.SYSVAR_RENT_ADDRESS, false, false), 25 | new AccountMeta(new PublicKey("8VBafTNv1F8k5Bg7DTVwhitw3MGAMTmekHsgLuMJxLC8"), false, false), 26 | new AccountMeta(new PublicKey("8VBafTNv1F8k5Bg7DTVwhitw3MGAMTmekHsgLuMJxLC8"), false, true), 27 | new AccountMeta(new PublicKey("6Cust2JhvweKLh4CVo1dt21s2PJ86uNGkziudpkNPaCj"), false, false), 28 | new AccountMeta(new PublicKey("7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7"), false, true), 29 | new AccountMeta(new PublicKey("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT"), false, false), 30 | new AccountMeta(new PublicKey("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT"), true, false), 31 | new AccountMeta(new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"), false, false), 32 | new AccountMeta(new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), false, false))); 33 | 34 | List accountKeysList = list.getList(); 35 | 36 | assertEquals(10, accountKeysList.size()); 37 | 38 | assertEquals("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3", accountKeysList.get(0).getPublicKey().toBase58()); 39 | assertEquals("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT", accountKeysList.get(1).getPublicKey().toBase58()); 40 | assertEquals("7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7", accountKeysList.get(2).getPublicKey().toBase58()); 41 | assertEquals("8VBafTNv1F8k5Bg7DTVwhitw3MGAMTmekHsgLuMJxLC8", accountKeysList.get(3).getPublicKey().toBase58()); 42 | assertEquals("HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds", accountKeysList.get(4).getPublicKey().toBase58()); 43 | assertEquals("6Cust2JhvweKLh4CVo1dt21s2PJ86uNGkziudpkNPaCj", accountKeysList.get(5).getPublicKey().toBase58()); 44 | assertEquals("11111111111111111111111111111111", accountKeysList.get(6).getPublicKey().toBase58()); 45 | assertEquals("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", accountKeysList.get(7).getPublicKey().toBase58()); 46 | assertEquals("SysvarRent111111111111111111111111111111111", accountKeysList.get(8).getPublicKey().toBase58()); 47 | assertEquals("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", accountKeysList.get(9).getPublicKey().toBase58()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/org/p2p/solanaj/core/AccountTest.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import org.junit.Test; 4 | import static org.junit.Assert.*; 5 | 6 | import java.util.Arrays; 7 | 8 | import org.bitcoinj.core.Base58; 9 | 10 | public class AccountTest { 11 | 12 | @Test 13 | public void accountFromSecretKey() { 14 | byte[] secretKey = Base58 15 | .decode("4Z7cXSyeFR8wNGMVXUE1TwtKn5D5Vu7FzEv69dokLv7KrQk7h6pu4LF8ZRR9yQBhc7uSM6RTTZtU1fmaxiNrxXrs"); 16 | assertEquals("QqCCvshxtqMAL2CVALqiJB7uEeE5mjSPsseQdDzsRUo", new Account(secretKey).getPublicKey().toString()); 17 | 18 | assertEquals(64, new Account(secretKey).getSecretKey().length); 19 | } 20 | 21 | @Test 22 | public void generateNewAccount() { 23 | Account account = new Account(); 24 | assertEquals(64, account.getSecretKey().length); 25 | } 26 | 27 | @Test 28 | public void fromMnemonic() { 29 | Account acc = Account.fromMnemonic(Arrays.asList("spider", "federal", "bleak", "unable", "ask", "weasel", 30 | "diamond", "electric", "illness", "wheat", "uphold", "mind"), ""); 31 | 32 | assertEquals("BQWWFhzBdw2vKKBUX17NHeFbCoFQHfRARpdztPE2tDJ", acc.getPublicKey().toString()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/org/p2p/solanaj/core/MainnetTest.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import org.junit.Test; 4 | import org.p2p.solanaj.rpc.Cluster; 5 | import org.p2p.solanaj.rpc.RpcClient; 6 | import org.p2p.solanaj.rpc.RpcException; 7 | import org.p2p.solanaj.rpc.types.AccountInfo; 8 | 9 | import java.util.List; 10 | 11 | import static org.junit.Assert.assertTrue; 12 | 13 | public class MainnetTest { 14 | 15 | @Test 16 | public void connectToMainnet() { 17 | 18 | final RpcClient client = new RpcClient(Cluster.MAINNET); 19 | final PublicKey publicKey = new PublicKey("skynetDj29GH6o6bAqoixCpDuYtWqi1rm8ZNx1hB3vq"); 20 | 21 | try { 22 | // Get account Info 23 | final AccountInfo accountInfo = client.getApi().getAccountInfo(publicKey); 24 | final double balance = (double) accountInfo.getValue().getLamports()/ 100000000; 25 | 26 | // Account data list 27 | final List accountData = accountInfo.getValue().getData(); 28 | 29 | // Verify "base64" string in accountData 30 | assertTrue(accountData.stream().anyMatch(s -> s.equalsIgnoreCase("base64"))); 31 | } catch (RpcException e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/org/p2p/solanaj/core/MessageTest.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import org.bitcoinj.core.Base58; 4 | import org.junit.Test; 5 | import org.p2p.solanaj.programs.SystemProgram; 6 | 7 | import static org.junit.Assert.assertArrayEquals; 8 | 9 | public class MessageTest { 10 | 11 | @Test 12 | public void serializeMessage() { 13 | PublicKey fromPublicKey = new PublicKey("QqCCvshxtqMAL2CVALqiJB7uEeE5mjSPsseQdDzsRUo"); 14 | PublicKey toPublickKey = new PublicKey("GrDMoeqMLFjeXQ24H56S1RLgT4R76jsuWCd6SvXyGPQ5"); 15 | int lamports = 3000; 16 | 17 | Account signer = new Account(Base58 18 | .decode("4Z7cXSyeFR8wNGMVXUE1TwtKn5D5Vu7FzEv69dokLv7KrQk7h6pu4LF8ZRR9yQBhc7uSM6RTTZtU1fmaxiNrxXrs")); 19 | 20 | Message message = new Message(); 21 | message.addInstruction(SystemProgram.transfer(fromPublicKey, toPublickKey, lamports)); 22 | message.setRecentBlockHash("Eit7RCyhUixAe2hGBS8oqnw59QK3kgMMjfLME5bm9wRn"); 23 | message.setFeePayer(signer.getPublicKey()); 24 | 25 | assertArrayEquals(new int[] { 1, 0, 1, 3, 6, 26, 217, 208, 83, 135, 21, 72, 83, 126, 222, 62, 38, 24, 73, 163, 26 | 223, 183, 253, 2, 250, 188, 117, 178, 35, 200, 228, 106, 219, 133, 61, 12, 235, 122, 188, 208, 216, 117, 27 | 235, 194, 109, 161, 177, 129, 163, 51, 155, 62, 242, 163, 22, 149, 187, 122, 189, 188, 103, 130, 115, 28 | 188, 173, 205, 229, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29 | 0, 0, 0, 0, 0, 0, 203, 226, 136, 193, 153, 148, 240, 50, 230, 98, 9, 79, 221, 179, 243, 174, 90, 67, 30 | 104, 169, 6, 187, 165, 72, 36, 156, 19, 57, 132, 38, 69, 245, 1, 2, 2, 0, 1, 12, 2, 0, 0, 0, 184, 11, 0, 31 | 0, 0, 0, 0, 0 }, toUnsignedByteArray(message.serialize())); 32 | } 33 | 34 | int[] toUnsignedByteArray(byte[] in) { 35 | int[] out = new int[in.length]; 36 | 37 | for (int i = 0; i < in.length; i++) { 38 | out[i] = in[i] & 0xff; 39 | } 40 | 41 | return out; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/org/p2p/solanaj/core/PublicKeyTest.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import org.junit.Test; 4 | import org.p2p.solanaj.core.PublicKey.ProgramDerivedAddress; 5 | 6 | import static org.junit.Assert.*; 7 | 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.IOException; 10 | import java.util.Arrays; 11 | 12 | public class PublicKeyTest { 13 | 14 | @Test(expected = IllegalArgumentException.class) 15 | public void ivalidKeys() { 16 | new PublicKey(new byte[] { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17 | 0, 0, 0, 0, 0 }); 18 | new PublicKey("300000000000000000000000000000000000000000000000000000000000000000000"); 19 | new PublicKey("300000000000000000000000000000000000000000000000000000000000000"); 20 | } 21 | 22 | @Test 23 | public void validKeys() { 24 | PublicKey key = new PublicKey(new byte[] { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25 | 0, 0, 0, 0, 0, 0, 0, 0, 0, }); 26 | assertEquals("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3", key.toString()); 27 | 28 | PublicKey key1 = new PublicKey("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3"); 29 | assertEquals("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3", key1.toBase58()); 30 | 31 | PublicKey key2 = new PublicKey("11111111111111111111111111111111"); 32 | assertEquals("11111111111111111111111111111111", key2.toBase58()); 33 | 34 | byte[] byteKey = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35 | 0, 0, 0, 0, 1, }; 36 | PublicKey key3 = new PublicKey(byteKey); 37 | assertArrayEquals(byteKey, new PublicKey(key3.toBase58()).toByteArray()); 38 | } 39 | 40 | @Test 41 | public void equals() { 42 | PublicKey key = new PublicKey("11111111111111111111111111111111"); 43 | assertTrue(key.equals(key)); 44 | 45 | assertFalse(key.equals(new PublicKey("11111111111111111111111111111112"))); 46 | } 47 | 48 | @Test 49 | public void readPubkey() throws IOException { 50 | PublicKey key = new PublicKey("11111111111111111111111111111111"); 51 | 52 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 53 | bos.write(1); 54 | bos.write(key.toByteArray()); 55 | 56 | byte[] bytes = bos.toByteArray(); 57 | assertEquals(key.toString(), PublicKey.readPubkey(bytes, 1).toString()); 58 | } 59 | 60 | @Test 61 | public void createProgramAddress() throws Exception { 62 | PublicKey programId = new PublicKey("BPFLoader1111111111111111111111111111111111"); 63 | 64 | PublicKey programAddress = PublicKey.createProgramAddress( 65 | Arrays.asList(new PublicKey("SeedPubey1111111111111111111111111111111111").toByteArray()), programId); 66 | assertTrue(programAddress.equals(new PublicKey("GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K"))); 67 | 68 | programAddress = PublicKey.createProgramAddress(Arrays.asList("".getBytes(), new byte[] { 1 }), programId); 69 | assertTrue(programAddress.equals(new PublicKey("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT"))); 70 | 71 | programAddress = PublicKey.createProgramAddress(Arrays.asList("☉".getBytes()), programId); 72 | assertTrue(programAddress.equals(new PublicKey("7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7"))); 73 | 74 | programAddress = PublicKey.createProgramAddress(Arrays.asList("Talking".getBytes(), "Squirrels".getBytes()), 75 | programId); 76 | assertTrue(programAddress.equals(new PublicKey("HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds"))); 77 | 78 | PublicKey programAddress2 = PublicKey.createProgramAddress(Arrays.asList("Talking".getBytes()), programId); 79 | assertFalse(programAddress.equals(programAddress2)); 80 | } 81 | 82 | @Test 83 | public void findProgramAddress() throws Exception { 84 | PublicKey programId = new PublicKey("BPFLoader1111111111111111111111111111111111"); 85 | 86 | ProgramDerivedAddress programAddress = PublicKey.findProgramAddress(Arrays.asList("".getBytes()), programId); 87 | assertTrue(programAddress.getAddress().equals(PublicKey.createProgramAddress( 88 | Arrays.asList("".getBytes(), new byte[] { (byte) programAddress.getNonce() }), programId))); 89 | 90 | } 91 | 92 | @Test 93 | public void findProgramAddress1() throws Exception { 94 | PublicKey programId = new PublicKey("6Cust2JhvweKLh4CVo1dt21s2PJ86uNGkziudpkNPaCj"); 95 | PublicKey programId2 = new PublicKey("BPFLoader1111111111111111111111111111111111"); 96 | 97 | ProgramDerivedAddress programAddress = PublicKey.findProgramAddress( 98 | Arrays.asList(new PublicKey("8VBafTNv1F8k5Bg7DTVwhitw3MGAMTmekHsgLuMJxLC8").toByteArray()), programId); 99 | assertTrue(programAddress.getAddress().equals(new PublicKey("FGnnqkzkXUGKD7wtgJCqTemU3WZ6yYqkYJ8xoQoXVvUG"))); 100 | 101 | ProgramDerivedAddress programAddress2 = PublicKey 102 | .findProgramAddress( 103 | Arrays.asList(new PublicKey("SeedPubey1111111111111111111111111111111111").toByteArray(), 104 | new PublicKey("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT").toByteArray(), 105 | new PublicKey("HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds").toByteArray()), 106 | programId2); 107 | assertTrue(programAddress2.getAddress().equals(new PublicKey("GXLbx3CbJuTTtJDZeS1PGzwJJ5jGYVEqcXum7472kpUp"))); 108 | assertEquals(programAddress2.getNonce(), 254); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/test/java/org/p2p/solanaj/core/TransactionTest.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.core; 2 | 3 | import org.p2p.solanaj.programs.SystemProgram; 4 | 5 | import org.junit.Test; 6 | import static org.junit.Assert.*; 7 | 8 | import java.util.Base64; 9 | 10 | import org.bitcoinj.core.Base58; 11 | 12 | public class TransactionTest { 13 | 14 | @Test 15 | public void signAndSerialize() { 16 | PublicKey fromPublicKey = new PublicKey("QqCCvshxtqMAL2CVALqiJB7uEeE5mjSPsseQdDzsRUo"); 17 | PublicKey toPublickKey = new PublicKey("GrDMoeqMLFjeXQ24H56S1RLgT4R76jsuWCd6SvXyGPQ5"); 18 | int lamports = 3000; 19 | 20 | Account signer = new Account(Base58 21 | .decode("4Z7cXSyeFR8wNGMVXUE1TwtKn5D5Vu7FzEv69dokLv7KrQk7h6pu4LF8ZRR9yQBhc7uSM6RTTZtU1fmaxiNrxXrs")); 22 | 23 | Transaction transaction = new Transaction(); 24 | transaction.addInstruction(SystemProgram.transfer(fromPublicKey, toPublickKey, lamports)); 25 | transaction.setRecentBlockHash("Eit7RCyhUixAe2hGBS8oqnw59QK3kgMMjfLME5bm9wRn"); 26 | transaction.sign(signer); 27 | byte[] serializedTransaction = transaction.serialize(); 28 | 29 | assertEquals( 30 | "ASdDdWBaKXVRA+6flVFiZokic9gK0+r1JWgwGg/GJAkLSreYrGF4rbTCXNJvyut6K6hupJtm72GztLbWNmRF1Q4BAAEDBhrZ0FOHFUhTft4+JhhJo9+3/QL6vHWyI8jkatuFPQzrerzQ2HXrwm2hsYGjM5s+8qMWlbt6vbxngnO8rc3lqgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAy+KIwZmU8DLmYglP3bPzrlpDaKkGu6VIJJwTOYQmRfUBAgIAAQwCAAAAuAsAAAAAAAA=", 31 | Base64.getEncoder().encodeToString(serializedTransaction)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/org/p2p/solanaj/programs/SystemProgramTest.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.programs; 2 | 3 | import org.p2p.solanaj.core.PublicKey; 4 | import org.p2p.solanaj.core.TransactionInstruction; 5 | 6 | import org.junit.Test; 7 | import static org.junit.Assert.*; 8 | 9 | import org.bitcoinj.core.Base58; 10 | 11 | public class SystemProgramTest { 12 | 13 | @Test 14 | public void transferInstruction() { 15 | PublicKey fromPublicKey = new PublicKey("QqCCvshxtqMAL2CVALqiJB7uEeE5mjSPsseQdDzsRUo"); 16 | PublicKey toPublickKey = new PublicKey("GrDMoeqMLFjeXQ24H56S1RLgT4R76jsuWCd6SvXyGPQ5"); 17 | int lamports = 3000; 18 | 19 | TransactionInstruction instruction = SystemProgram.transfer(fromPublicKey, toPublickKey, lamports); 20 | 21 | assertEquals(SystemProgram.PROGRAM_ID, instruction.getProgramId()); 22 | assertEquals(2, instruction.getKeys().size()); 23 | assertEquals(toPublickKey, instruction.getKeys().get(1).getPublicKey()); 24 | 25 | assertArrayEquals(new byte[] { 2, 0, 0, 0, -72, 11, 0, 0, 0, 0, 0, 0 }, instruction.getData()); 26 | } 27 | 28 | @Test 29 | public void createAccountInstruction() { 30 | TransactionInstruction instruction = SystemProgram.createAccount(SystemProgram.PROGRAM_ID, 31 | SystemProgram.PROGRAM_ID, 2039280, 165, SystemProgram.PROGRAM_ID); 32 | 33 | assertEquals("11119os1e9qSs2u7TsThXqkBSRUo9x7kpbdqtNNbTeaxHGPdWbvoHsks9hpp6mb2ed1NeB", 34 | Base58.encode(instruction.getData())); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/org/p2p/solanaj/utils/ByteUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.utils; 2 | 3 | import org.junit.Test; 4 | import static org.junit.Assert.*; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.IOException; 8 | import java.math.BigInteger; 9 | 10 | public class ByteUtilsTest { 11 | 12 | @Test 13 | public void readBytes() { 14 | byte[] buf = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; 15 | 16 | assertArrayEquals(new byte[] { 1, 2, 3, 4 }, ByteUtils.readBytes(buf, 0, 4)); 17 | assertArrayEquals(new byte[] { 5, 6, 7, 8, 9, 10, 11, 12 }, ByteUtils.readBytes(buf, 4, 8)); 18 | } 19 | 20 | @Test 21 | public void readUint64() throws IOException { 22 | String bigIntValue = "96351551052682965"; 23 | 24 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 25 | ByteUtils.uint64ToByteStreamLE(new BigInteger(bigIntValue), bos); 26 | 27 | BigInteger bn = ByteUtils.readUint64(bos.toByteArray(), 0); 28 | 29 | assertEquals(bigIntValue, bn.toString()); 30 | } 31 | 32 | @Test(expected = RuntimeException.class) 33 | public void uint64ToByteStreamLE() { 34 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 35 | try { 36 | ByteUtils.uint64ToByteStreamLE(new BigInteger("137001898677442802701"), bos); 37 | } catch (IOException e) { 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/p2p/solanaj/utils/ShortvecEncodingTest.java: -------------------------------------------------------------------------------- 1 | package org.p2p.solanaj.utils; 2 | 3 | import org.junit.Test; 4 | import static org.junit.Assert.*; 5 | 6 | public class ShortvecEncodingTest { 7 | 8 | @Test 9 | public void encodeLength() { 10 | assertArrayEquals(new byte[] { 0 } /* [0] */, ShortvecEncoding.encodeLength(0)); 11 | assertArrayEquals(new byte[] { 1 } /* [1] */, ShortvecEncoding.encodeLength(1)); 12 | assertArrayEquals(new byte[] { 5 } /* [5] */, ShortvecEncoding.encodeLength(5)); 13 | assertArrayEquals(new byte[] { 127 } /* [0x7f] */, ShortvecEncoding.encodeLength(127)); // 0x7f 14 | assertArrayEquals(new byte[] { -128, 1 }/* [0x80, 0x01] */, ShortvecEncoding.encodeLength(128)); // 0x80 15 | assertArrayEquals(new byte[] { -1, 1 } /* [0xff, 0x01] */, ShortvecEncoding.encodeLength(255)); // 0xff 16 | assertArrayEquals(new byte[] { -128, 2 } /* [0x80, 0x02] */, ShortvecEncoding.encodeLength(256)); // 0x100 17 | assertArrayEquals(new byte[] { -1, -1, 1 } /* [0xff, 0xff, 0x01] */, ShortvecEncoding.encodeLength(32767)); // 0x7fff 18 | assertArrayEquals(new byte[] { -128, -128, -128, 1 } /* [0x80, 0x80, 0x80, 0x01] */, 19 | ShortvecEncoding.encodeLength(2097152)); // 0x200000 20 | } 21 | } 22 | --------------------------------------------------------------------------------