Serialization and Ed25519 #2

Merged
zxq9 merged 11 commits from no-bigint into master 2026-08-20 21:19:17 +09:00
7 changed files with 37 additions and 78 deletions
Showing only changes of commit 81cc80ae00 - Show all commits
@@ -23,10 +23,10 @@ package swiss.qpq.gajumaru.core.crypto;
import java.util.Arrays;
import swiss.qpq.gajumaru.core.tools.CryptoUtils;
/**
* Pure Java implementation of BLAKE2b.
* Based on eblake2.erl.
*/
// Pure Java implementation of BLAKE2b.
// Based on eblake2.erl.
public final class Blake2b {
private static final long[] IV = {
@@ -35,32 +35,26 @@ public final class Blake2b {
};
private static final byte[][] SIGMA = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, // Round 10: sigma[0]
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, // Round 10: sigma[0]
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } // Round 11: sigma[1]
};
private Blake2b() {}
/**
* Computes a 32-byte BLAKE2b hash.
*/
public static byte[] hash(byte[] data) {
return hash(data, 32);
}
/**
* Computes a BLAKE2b hash with the specified output length.
*/
public static byte[] hash(byte[] data, int hashLen) {
long[] h = Arrays.copyOf(IV, 8);
h[0] ^= 0x01010000L ^ hashLen;
@@ -105,7 +99,7 @@ public final class Blake2b {
}
long[] v = new long[16];
System.arraycopy(h, 0, v, 0, 8);
System.arraycopy( h, 0, v, 0, 8);
System.arraycopy(IV, 0, v, 8, 8);
v[12] ^= t0;
@@ -116,14 +110,14 @@ public final class Blake2b {
for (int round = 0; round < 12; round++) {
byte[] s = SIGMA[round];
g(v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
g(v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
g(v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
g(v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
g(v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
g(v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
g(v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
g(v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
g(v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
g(v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
g(v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
g(v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
g(v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
g(v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
g(v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
}
for (int i = 0; i < 8; i++) {
@@ -23,10 +23,10 @@ package swiss.qpq.gajumaru.core.crypto;
import java.util.Arrays;
import swiss.qpq.gajumaru.core.tools.CryptoUtils;
/**
* Pure Java implementation of Keccak-256 (Keccak[c=512] sponge).
* Based on sha3.erl.
*/
// Pure Java implementation of Keccak-256 (Keccak[c=512] sponge).
// Based on sha3.erl.
public final class Keccak256 {
private static final int LANE_SIZE = 64;
@@ -45,18 +45,15 @@ public final class Keccak256 {
};
private static final int[] ROTATION_OFFSETS = {
0, 1, 62, 28, 27,
0, 1, 62, 28, 27,
36, 44, 6, 55, 20,
3, 10, 43, 25, 39,
3, 10, 43, 25, 39,
41, 45, 15, 21, 8,
18, 2, 61, 56, 14
};
private Keccak256() {}
/**
* Computes the Keccak-256 hash of the input data.
*/
public static byte[] hash(byte[] data) {
long[] state = new long[STATE_SIZE];
@@ -24,10 +24,10 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Port of gmser_id.erl.
* Represents a 33-byte identifier (1-byte tag + 32-byte value).
*/
// Port of gmser_id.erl.
// Represents a 33-byte identifier (1-byte tag + 32-byte value).
public final class Id {
public enum Tag {
@@ -74,9 +74,6 @@ public final class Id {
public Tag getTag() { return tag; }
public byte[] getValue() { return Arrays.copyOf(value, 32); }
/**
* Serializes the ID to a 33-byte array.
*/
public byte[] serialize() {
byte[] result = new byte[33];
result[0] = (byte) tag.value;
@@ -84,9 +81,6 @@ public final class Id {
return result;
}
/**
* Deserializes a 33-byte array into an Id object.
*/
public static Id deserialize(byte[] data) {
if (data.length != 33) {
throw new IllegalArgumentException("Serialized ID must be exactly 33 bytes");
@@ -26,10 +26,10 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Port of gmser_api_encoder.erl.
* Handles Gajumaru API encoding (prefixed Base58Check).
*/
// Port of gmser_api_encoder.erl.
// Handles Gajumaru API encoding (prefixed Base58Check).
public final class ApiEncoder {
public enum Type {
@@ -63,9 +63,6 @@ public final class ApiEncoder {
private ApiEncoder() {}
/**
* Encodes a payload with the given type and prefix.
*/
public static String encode(Type type, byte[] payload) {
if (type.size != -1 && payload.length != type.size) {
throw new IllegalArgumentException("Invalid payload size for " + type + ": " + payload.length);
@@ -73,9 +70,6 @@ public final class ApiEncoder {
return type.prefix + "_" + Base58.checkEncode(payload);
}
/**
* Decodes a prefixed Base58Check string.
*/
public static DecodeResult decode(String input) {
String[] parts = input.split("_");
if (parts.length != 2) {
@@ -24,6 +24,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
// Stateless Base58 and Base58Check implementation.
public final class Base58 {
@@ -26,10 +26,7 @@ import java.util.List;
public final class RLP {
// Integrated data models
// RLP only has two types: byte arrays and lists of lists of byte arrays
// TODO: Comment this better with references.
public static class RLPException extends RuntimeException {
public RLPException(String message) {
super(message);
@@ -110,12 +107,10 @@ public final class RLP {
int prefix = buffer[offset] & 0xFF;
// 1. Single byte [0x00, 0x7F] (itself)
if (prefix <= 0x7F) {
return new DecodeResult(new RLP_Item(new byte[] { (byte) prefix }), 1);
}
// 2. Short string [0x80, 0xB7] (length 0-55)
if (prefix <= 0xB7) {
int payloadLen = prefix - 0x80;
checkBounds(buffer, offset + 1, payloadLen);
@@ -123,7 +118,6 @@ public final class RLP {
return new DecodeResult(new RLP_Item(payload), 1 + payloadLen);
}
// 3. Long string [0xB8, 0xBF] (length > 55)
if (prefix <= 0xBF) {
int lenLen = prefix - 0xB7;
checkBounds(buffer, offset + 1, lenLen);
@@ -133,7 +127,6 @@ public final class RLP {
return new DecodeResult(new RLP_Item(payload), 1 + lenLen + payloadLen);
}
// 4. Short list [0xC0, 0xF7] (total length 0-55)
if (prefix <= 0xF7) {
int payloadLen = prefix - 0xC0;
checkBounds(buffer, offset + 1, payloadLen);
@@ -141,7 +134,6 @@ public final class RLP {
return new DecodeResult(list, 1 + payloadLen);
}
// 5. Long list [0xF8, 0xFF] (total length > 55)
int lenLen = prefix - 0xF7;
checkBounds(buffer, offset + 1, lenLen);
int payloadLen = bigEndianToInt(buffer, offset + 1, offset + 1 + lenLen);
@@ -22,36 +22,26 @@ package swiss.qpq.gajumaru.core.tools;
import java.util.Arrays;
/**
* Utility class for cryptographic operations and memory hygiene.
*/
// Utility class for cryptographic operations and memory hygiene.
public final class CryptoUtils {
private CryptoUtils() {}
/**
* Wipes a byte array by filling it with zeros.
*
* @param data The byte array to wipe.
*/
// Wipes a byte array by filling it with zeros.
public static void wipe(byte[] data) {
if (data != null) {
Arrays.fill(data, (byte) 0);
}
}
/**
* Wipes a long array by filling it with zeros.
*/
public static void wipe(long[] data) {
if (data != null) {
Arrays.fill(data, 0L);
}
}
/**
* Encodes a byte array to a hex string.
*/
public static String binToHex(byte[] data) {
StringBuilder sb = new StringBuilder();
for (byte b : data) {
@@ -60,9 +50,6 @@ public final class CryptoUtils {
return sb.toString();
}
/**
* Decodes a hex string to a byte array.
*/
public static byte[] hexToBin(String s) {
int len = s.length();
byte[] data = new byte[len / 2];