Skip to main content

Hash Class

software.sava.core.crypto.Hash Provides cryptographic hashing functions used throughout Solana.

SHA-256 Methods

Get Digest

static MessageDigest sha256Digest()
return
MessageDigest
SHA-256 message digest instance from Sun security provider

Hash Data

static byte[] sha256(byte[] input)
input
byte[]
required
Data to hash
return
byte[]
32-byte SHA-256 hash

Double Hash

static byte[] sha256Twice(byte[] bytes)
bytes
byte[]
required
Data to hash twice
return
byte[]
32-byte double SHA-256 hash (SHA256(SHA256(bytes)))
static byte[] sha256Twice(byte[] bytes, int offset, int length)
offset
int
required
Starting offset in bytes array
length
int
required
Number of bytes to hash

SHA-512 Methods

static MessageDigest sha512Digest()
return
MessageDigest
SHA-512 message digest instance

Hash160 (RIPEMD-160)

static byte[] h160(byte[] input)
input
byte[]
required
Data to hash
return
byte[]
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)
privateKey
byte[]
required
32-byte private key
publicKey
byte[]
required
Destination for 32-byte public key
static void generatePublicKey(
    byte[] privateKey,
    int privateKeyOffset,
    byte[] publicKey,
    int publicKeyOffset
)

Curve Validation

static boolean isNotOnCurve(byte[] hash)
hash
byte[]
required
32-byte hash to check
return
boolean
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
msg
byte[]
required
Message that was signed
signature
byte[]
required
64-byte Ed25519 signature
return
boolean
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
);

Build docs developers (and LLMs) love