Hash Class
software.sava.core.crypto.Hash
Provides cryptographic hashing functions used throughout Solana.
SHA-256 Methods
Get Digest
static MessageDigest sha256Digest()
SHA-256 message digest instance from Sun security provider
Hash Data
static byte[] sha256(byte[] input)
Double Hash
static byte[] sha256Twice(byte[] bytes)
32-byte double SHA-256 hash (SHA256(SHA256(bytes)))
static byte[] sha256Twice(byte[] bytes, int offset, int length)
Starting offset in bytes array
SHA-512 Methods
static MessageDigest sha512Digest()
SHA-512 message digest instance
Hash160 (RIPEMD-160)
static byte[] h160(byte[] input)
20-byte hash (RIPEMD-160 of SHA-256)
This is equivalent to: RIPEMD160(SHA256(input))
Example Usage
import software.sava.core.crypto.Hash;
// SHA-256 hash
byte[] data = "Hello Solana".getBytes();
byte[] hash = Hash.sha256(data);
// Double SHA-256 (commonly used in Bitcoin)
byte[] doubleHash = Hash.sha256Twice(data);
// Hash160 for addresses
byte[] hash160 = Hash.h160(publicKeyBytes);
// Incremental hashing
var digest = Hash.sha256Digest();
digest.update(data1);
digest.update(data2);
byte[] result = digest.digest();
Ed25519Util Class
software.sava.core.crypto.ed25519.Ed25519Util
Utilities for Ed25519 elliptic curve operations.
Public Key Generation
static void generatePublicKey(byte[] privateKey, byte[] publicKey)
Destination for 32-byte public key
static void generatePublicKey(
byte[] privateKey,
int privateKeyOffset,
byte[] publicKey,
int publicKeyOffset
)
Curve Validation
static boolean isNotOnCurve(byte[] hash)
True if the hash represents a point off the Ed25519 curve (valid for PDA)
Used internally by PDA derivation to ensure derived addresses are off-curve.
Example Usage
import software.sava.core.crypto.ed25519.Ed25519Util;
// Generate public key from private key
byte[] privateKey = new byte[32];
// ... initialize private key ...
byte[] publicKey = new byte[32];
Ed25519Util.generatePublicKey(privateKey, publicKey);
// Check if hash is off-curve (for PDA validation)
byte[] hash = Hash.sha256(someData);
if (Ed25519Util.isNotOnCurve(hash)) {
System.out.println("Valid PDA address");
}
Signature Verification
PublicKey Signature Methods
See PublicKey API for full signature verification documentation.
boolean verifySignature(byte[] msg, byte[] signature)
boolean verifySignature(String msg, byte[] signature)
Static Verification
static boolean verifySignature(
java.security.PublicKey publicKey,
byte[] msg,
byte[] signature
)
publicKey
java.security.PublicKey
required
Java security PublicKey instance
64-byte Ed25519 signature
True if signature is valid
static boolean verifySignature(
byte[] publicKey,
int publicKeyOffset,
byte[] msg,
int msgOffset,
int msgLength,
byte[] signature
)
Example Usage
import software.sava.core.accounts.PublicKey;
import software.sava.core.accounts.Signer;
// Create signer and sign message
var signer = Signer.createFromKeyPair(keyPairBytes);
byte[] message = "Sign this message".getBytes();
byte[] signature = signer.sign(message);
// Verify with PublicKey instance
var pubKey = signer.publicKey();
boolean valid = pubKey.verifySignature(message, signature);
// Verify with raw bytes
boolean valid2 = PublicKey.verifySignature(
pubKey.toByteArray(),
0,
message,
0,
message.length,
signature
);
// Verify with Java security PublicKey
java.security.PublicKey javaPubKey = pubKey.toJavaPublicKey();
boolean valid3 = PublicKey.verifySignature(
javaPubKey,
message,
signature
);
SunCrypto Class
software.sava.core.crypto.SunCrypto
Provides access to Java security providers and secure random number generation.
Constants
static final Provider SUN_SECURITY_PROVIDER
static final KeyFactory ED_25519_KEY_FACTORY
static final SecureRandom SECURE_RANDOM
Example Usage
import software.sava.core.crypto.SunCrypto;
// Get secure random bytes
byte[] randomBytes = new byte[32];
SunCrypto.SECURE_RANDOM.nextBytes(randomBytes);
// Use in key generation
import software.sava.core.accounts.Signer;
byte[] privateKey = new byte[32];
SunCrypto.SECURE_RANDOM.nextBytes(privateKey);
var signer = Signer.createFromPrivateKey(privateKey);
Common Cryptographic Workflows
Generate Keypair and Sign
import software.sava.core.accounts.Signer;
import software.sava.core.accounts.PublicKey;
// Generate keypair
var keyPair = Signer.generatePrivateKeyPairBytes();
var signer = Signer.createFromKeyPair(keyPair);
// Get public key
PublicKey pubKey = signer.publicKey();
System.out.println("Address: " + pubKey.toBase58());
// Sign message
byte[] message = "Hello Solana".getBytes();
byte[] signature = signer.sign(message);
// Verify
boolean valid = pubKey.verifySignature(message, signature);
assert valid;
Derive PDA with Hash
import software.sava.core.accounts.PublicKey;
import software.sava.core.crypto.Hash;
import software.sava.core.crypto.ed25519.Ed25519Util;
import java.util.List;
// Create PDA seeds
var seeds = List.of(
"metadata".getBytes(),
programId.toByteArray(),
mint.toByteArray()
);
// Find valid PDA
var pda = PublicKey.findProgramAddress(seeds, programId);
// The process internally uses:
// 1. Concatenate seeds + program ID + nonce
// 2. SHA-256 hash
// 3. Check if off-curve with Ed25519Util.isNotOnCurve()
// 4. Repeat with decremented nonce until valid
Transaction Signature
import software.sava.core.tx.Transaction;
import software.sava.core.accounts.Signer;
// Create transaction
var tx = Transaction.createTx(feePayer, instructions);
// Sign (internally uses Ed25519)
var signer = Signer.createFromKeyPair(keyPairBytes);
tx.sign(blockHash, signer);
// Get signature (transaction ID)
byte[] txId = tx.getId();
String txIdBase58 = tx.getBase58Id();
// Signature is first 64 bytes of serialized transaction
Verify Transaction Signature
// Extract signature and message from transaction
byte[] serializedTx = tx.serialized();
byte[] signature = Arrays.copyOfRange(serializedTx, 1, 65);
int sigLen = 1 + (64 * tx.numSigners());
byte[] message = Arrays.copyOfRange(
serializedTx,
sigLen,
serializedTx.length
);
// Verify signature
boolean valid = signer.publicKey().verifySignature(
message,
signature
);