Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions QORTIUM-CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ own chain.

## Change Entries

### 2026-05-28 - security: replace java.util.Random with SecureRandom (Q4 + Q5)

Replaced predictable random number generation in QDN request IDs and online-account nonce setup with shared secure random generators. This makes peer-facing request identifiers and minting-related nonce starting points harder to predict without changing the surrounding transaction, block, or QDN behavior.

### 2026-05-28 - security: validate backup name before SQL string interpolation

Added a strict backup-name check before the repository builds its HSQLDB backup command. Current backup callers already use fixed safe names, but this prevents future code from passing names with quotes, path separators, or other unsafe characters into a database command or backup path.
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/qortium/block/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;
Expand Down Expand Up @@ -114,6 +115,7 @@ public static ValidationResult valueOf(int value) {

// Other properties
private static final Logger LOGGER = LogManager.getLogger(Block.class);
private static final SecureRandom SECURE_RANDOM = new SecureRandom();

public static final int CURRENT_VERSION = 1;

Expand Down Expand Up @@ -434,7 +436,7 @@ else if (isOnlineAccountsBlock(height)) {
if (onlineAccounts.isEmpty()) {
// new v5.1.0, don't fail (25 blocks before payout) when isSingleNodeTestnet == true
if (Settings.getInstance().isSingleNodeTestnet()) {
Integer nonce = new Random().nextInt(500000);
Integer nonce = SECURE_RANDOM.nextInt(500000);
byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp);
byte[] signature = Ed25519Extras.signForAggregation(minter.getPrivateKey(), timestampBytes);
byte[] publicKey = minter.getPublicKey();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

public class OnlineAccountsManager {

private static final Logger LOGGER = LogManager.getLogger(OnlineAccountsManager.class);
private static final SecureRandom SECURE_RANDOM = new SecureRandom();

// 'Current' as in 'now'

Expand Down Expand Up @@ -187,7 +189,7 @@ public void ensureTestingAccountsOnline(PrivateKeyAccount... onlineAccounts) {
byte[] signature = Ed25519Extras.signForAggregation(onlineAccount.getPrivateKey(), timestampBytes);
byte[] publicKey = onlineAccount.getPublicKey();

Integer nonce = new Random().nextInt(500000);
Integer nonce = SECURE_RANDOM.nextInt(500000);

OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey, nonce);
replacementAccounts.add(ourOnlineAccountData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -12,7 +13,6 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
Expand Down Expand Up @@ -50,6 +50,7 @@
public class ArbitraryDataFileListManager {

private static final Logger LOGGER = LogManager.getLogger(ArbitraryDataFileListManager.class);
private static final SecureRandom SECURE_RANDOM = new SecureRandom();

private static ArbitraryDataFileListManager instance;

Expand Down Expand Up @@ -419,7 +420,7 @@ public boolean fetchArbitraryDataFileList(ArbitraryTransactionData arbitraryTran
// Assign random ID to this message
int id;
do {
id = new Random().nextInt(Integer.MAX_VALUE - 1) + 1;
id = SECURE_RANDOM.nextInt(Integer.MAX_VALUE - 1) + 1;

// Put queue into map (keyed by message ID) so we can poll for a response
// If putIfAbsent() doesn't return null, then this ID is already taken
Expand Down Expand Up @@ -477,7 +478,7 @@ public boolean fetchArbitraryDataFileList(Peer peer, byte[] signature) {
// Assign random ID to this message
int id;
do {
id = new Random().nextInt(Integer.MAX_VALUE - 1) + 1;
id = SECURE_RANDOM.nextInt(Integer.MAX_VALUE - 1) + 1;

// Put queue into map (keyed by message ID) so we can poll for a response
// If putIfAbsent() doesn't return null, then this ID is already taken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.qortium.utils.Triple;

import java.io.IOException;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
Expand All @@ -37,6 +38,7 @@
public class ArbitraryMetadataManager {

private static final Logger LOGGER = LogManager.getLogger(ArbitraryMetadataManager.class);
private static final SecureRandom SECURE_RANDOM = new SecureRandom();

private static ArbitraryMetadataManager instance;

Expand Down Expand Up @@ -286,7 +288,7 @@ private byte[] fetchArbitraryMetadata(byte[] signature, byte[] metadataHash, boo
// Assign random ID to this message
int id;
do {
id = new Random().nextInt(Integer.MAX_VALUE - 1) + 1;
id = SECURE_RANDOM.nextInt(Integer.MAX_VALUE - 1) + 1;

// Put queue into map (keyed by message ID) so we can poll for a response
// If putIfAbsent() doesn't return null, then this ID is already taken
Expand Down