Last active
November 14, 2025 15:59
-
-
Save mingyang91/4d95186e6b97e22d3fb3c984f32b201f to your computer and use it in GitHub Desktop.
Paranoid entropy generator: combines 6 independent sources (OS, random.org, Solana, Google TLS, AWS KMS HSM, system state) into 256-bit secure random. Generates BIP39 24-word mnemonics. Pure bash, no Python needed. Defense-in-depth: even if 5 sources fail, output stays secure.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # | |
| # Secure Random Number Generator | |
| # | |
| # Combines 6 independent entropy sources to generate a cryptographically | |
| # secure 256-bit random number. Even if 5 out of 6 sources are compromised, | |
| # the output remains secure. | |
| # | |
| # Sources: | |
| # 1. OS entropy (/dev/urandom) | |
| # 2. System state (hardware RNG or process state) | |
| # 3. random.org (atmospheric noise) | |
| # 4. Solana blockchain (latest transaction hash or error page) | |
| # 5. TLS handshakes (Google server random data) | |
| # 6. AWS KMS (FIPS 140-2 validated HSM) | |
| # | |
| # BIP39 Note: For OneKey compatibility, the hex entropy is treated as UTF-8 text | |
| # and hashed with SHA256 before BIP39 encoding. This matches OneKey's behavior. | |
| # | |
| # Usage: | |
| # ./generate-entropy.sh - Output hex string | |
| # ./generate-entropy.sh --bip39 - Also output BIP39 mnemonic (24 words) | |
| # ./generate-entropy.sh -b - Short form for --bip39 | |
| # ./generate-entropy.sh --dotmap - BIP39 with visual dot-map grid | |
| # | |
| # Output: 64-character hex string (256 bits) | |
| # | |
| set -euo pipefail | |
| # Colors for output | |
| GREEN='\033[0;32m' | |
| BLUE='\033[0;34m' | |
| YELLOW='\033[1;33m' | |
| NC='\033[0m' # No Color | |
| # Create temp directory | |
| TEMP_DIR=$(mktemp -d) | |
| trap "rm -rf ${TEMP_DIR}" EXIT | |
| echo -e "${BLUE}=== Secure Random Number Generator ===${NC}" | |
| echo -e "${BLUE}Combining 6 independent entropy sources...${NC}\n" | |
| # Source 1: OS entropy (/dev/urandom) | |
| echo -e "${YELLOW}[1/6]${NC} Collecting OS entropy (/dev/urandom)..." | |
| dd if=/dev/urandom bs=32 count=1 2>/dev/null > "${TEMP_DIR}/entropy1.bin" | |
| echo -e "${GREEN}✓${NC} OS entropy collected\n" | |
| # Source 2: Hardware RNG or system state | |
| echo -e "${YELLOW}[2/6]${NC} Collecting system entropy..." | |
| if [ -e /dev/hwrng ]; then | |
| dd if=/dev/hwrng bs=32 count=1 2>/dev/null > "${TEMP_DIR}/entropy2.bin" | |
| echo -e "${GREEN}✓${NC} Hardware RNG entropy collected\n" | |
| else | |
| # Portable system state collection (works on Linux and macOS) | |
| { | |
| uname -a | |
| uptime | |
| df | |
| ps aux 2>/dev/null | head -20 || true | |
| date | |
| hostname | |
| } | openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy2.bin" | |
| echo -e "${GREEN}✓${NC} System state entropy collected\n" | |
| fi | |
| # Source 3: random.org atmospheric noise | |
| echo -e "${YELLOW}[3/6]${NC} Collecting atmospheric noise from random.org..." | |
| if curl -s --max-time 10 "https://www.random.org/cgi-bin/randbyte?nbytes=32&format=h" | \ | |
| xxd -r -p > "${TEMP_DIR}/entropy3.bin" 2>/dev/null && \ | |
| [ -s "${TEMP_DIR}/entropy3.bin" ]; then | |
| echo -e "${GREEN}✓${NC} random.org entropy collected\n" | |
| else | |
| echo -e "${YELLOW}⚠${NC} random.org unavailable, using timestamp fallback\n" | |
| date +%s%N | openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy3.bin" | |
| fi | |
| # Source 4: Solana blockchain latest transaction | |
| echo -e "${YELLOW}[4/6]${NC} Collecting Solana blockchain entropy..." | |
| # Try to fetch Solana API (success or error page, both contain random data) | |
| SOLANA_RESPONSE=$(curl -s --connect-timeout 5 --max-time 10 \ | |
| 'https://api-v2.solscan.io/v2/transaction/list' \ | |
| -H 'accept: application/json' \ | |
| -H 'origin: https://solscan.io' \ | |
| -H 'referer: https://solscan.io/' 2>&1 || echo "curl_failed_$(date +%s%N)") | |
| if [ -n "$SOLANA_RESPONSE" ] && [ "$SOLANA_RESPONSE" != "curl_failed"* ]; then | |
| # Try to extract transaction hash if successful | |
| if command -v jq &> /dev/null; then | |
| TX_HASH=$(echo "$SOLANA_RESPONSE" | jq -r '.data[0].txHash // empty' 2>/dev/null || echo "") | |
| if [ -n "$TX_HASH" ] && [ "$TX_HASH" != "empty" ] && [ "$TX_HASH" != "null" ]; then | |
| echo "$TX_HASH" | xxd -r -p > "${TEMP_DIR}/entropy4.bin" 2>/dev/null && \ | |
| echo -e "${GREEN}✓${NC} Solana transaction hash collected\n" || { | |
| # Failed to decode, use raw response | |
| echo "$SOLANA_RESPONSE" | openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy4.bin" | |
| echo -e "${GREEN}✓${NC} Solana response data collected (trace IDs, timestamps)\n" | |
| } | |
| else | |
| # API denied or error, but use the response page (contains trace IDs, timestamps) | |
| echo "$SOLANA_RESPONSE" | openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy4.bin" | |
| echo -e "${GREEN}✓${NC} Solana response data collected (trace IDs, timestamps)\n" | |
| fi | |
| else | |
| # No jq, just hash the entire response (contains random data regardless) | |
| echo "$SOLANA_RESPONSE" | openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy4.bin" | |
| echo -e "${GREEN}✓${NC} Solana response data collected\n" | |
| fi | |
| else | |
| # Complete failure, use system state | |
| echo -e "${YELLOW}⚠${NC} Solana unreachable, using system state fallback\n" | |
| { | |
| ps aux 2>/dev/null | head -30 || true | |
| netstat -an 2>/dev/null | head -50 || ss -an 2>/dev/null | head -50 || true | |
| date +%s%N | |
| } | openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy4.bin" | |
| fi | |
| # Source 5: TLS handshake entropy from Google | |
| echo -e "${YELLOW}[5/6]${NC} Collecting TLS handshake entropy..." | |
| # Use gtimeout on macOS (brew install coreutils) or fallback | |
| TIMEOUT_CMD="timeout" | |
| if ! command -v timeout &> /dev/null && command -v gtimeout &> /dev/null; then | |
| TIMEOUT_CMD="gtimeout" | |
| fi | |
| if command -v $TIMEOUT_CMD &> /dev/null; then | |
| if echo | $TIMEOUT_CMD 5 openssl s_client -connect google.com:443 2>&1 | \ | |
| openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy5.bin" 2>/dev/null && \ | |
| [ -s "${TEMP_DIR}/entropy5.bin" ]; then | |
| echo -e "${GREEN}✓${NC} TLS entropy collected\n" | |
| else | |
| echo -e "${YELLOW}⚠${NC} TLS connection failed, using hostname fallback\n" | |
| (hostname; who 2>/dev/null; w 2>/dev/null; date +%s%N) | \ | |
| openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy5.bin" | |
| fi | |
| else | |
| # No timeout command, just try the connection | |
| if echo | openssl s_client -connect google.com:443 -timeout 5 2>&1 | head -100 | \ | |
| openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy5.bin" 2>/dev/null && \ | |
| [ -s "${TEMP_DIR}/entropy5.bin" ]; then | |
| echo -e "${GREEN}✓${NC} TLS entropy collected\n" | |
| else | |
| echo -e "${YELLOW}⚠${NC} TLS connection failed, using hostname fallback\n" | |
| (hostname; who 2>/dev/null; w 2>/dev/null; date +%s%N) | \ | |
| openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy5.bin" | |
| fi | |
| fi | |
| # Source 6: AWS KMS (HSM-backed) | |
| echo -e "${YELLOW}[6/6]${NC} Collecting AWS KMS entropy (HSM-backed)..." | |
| if command -v aws &> /dev/null; then | |
| # AWS KMS returns base64-encoded random bytes | |
| if aws kms generate-random \ | |
| --number-of-bytes 32 \ | |
| --output text \ | |
| --query Plaintext 2>/dev/null | \ | |
| base64 -d > "${TEMP_DIR}/entropy6.bin" 2>/dev/null && \ | |
| [ -s "${TEMP_DIR}/entropy6.bin" ]; then | |
| echo -e "${GREEN}✓${NC} AWS KMS HSM entropy collected\n" | |
| else | |
| echo -e "${YELLOW}⚠${NC} AWS KMS unavailable (credentials/permissions), using timestamp fallback\n" | |
| date +%s%N | openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy6.bin" | |
| fi | |
| else | |
| echo -e "${YELLOW}⚠${NC} AWS CLI not installed, using timestamp fallback\n" | |
| date +%s%N | openssl dgst -sha256 -binary > "${TEMP_DIR}/entropy6.bin" | |
| fi | |
| # Combine all entropy sources | |
| echo -e "${BLUE}Combining all entropy sources with SHA-256...${NC}" | |
| RESULT=$(cat "${TEMP_DIR}/entropy1.bin" \ | |
| "${TEMP_DIR}/entropy2.bin" \ | |
| "${TEMP_DIR}/entropy3.bin" \ | |
| "${TEMP_DIR}/entropy4.bin" \ | |
| "${TEMP_DIR}/entropy5.bin" \ | |
| "${TEMP_DIR}/entropy6.bin" | \ | |
| openssl dgst -sha256 -binary | \ | |
| xxd -p -c 32) | |
| echo -e "${GREEN}✓${NC} Entropy combined\n" | |
| # Output result | |
| echo -e "${BLUE}=== Result ===${NC}" | |
| echo -e "${GREEN}${RESULT}${NC}" | |
| echo "" | |
| # Generate BIP39 mnemonic if requested | |
| if [ "${1}" = "--bip39" ] || [ "${1}" = "-b" ] || [ "${1}" = "--dotmap" ]; then | |
| echo -e "${BLUE}=== BIP39 Mnemonic (24 words) ===${NC}" | |
| # Check if wordlist exists | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| WORDLIST="${SCRIPT_DIR}/bip39-english.txt" | |
| if [ ! -f "$WORDLIST" ]; then | |
| echo -e "${YELLOW}Downloading BIP39 wordlist...${NC}" | |
| curl -sL "https://github.com/bitcoin/bips/raw/refs/heads/master/bip-0039/english.txt" \ | |
| -o "$WORDLIST" 2>/dev/null || { | |
| echo -e "${YELLOW}⚠${NC} Failed to download wordlist" | |
| WORDLIST="" | |
| } | |
| fi | |
| if [ -f "$WORDLIST" ]; then | |
| # Pure bash BIP39 implementation (OneKey compatible) | |
| # OneKey treats hex string as UTF-8 text, then hashes it | |
| # 1. Hash the hex string as text (OneKey behavior) | |
| ENTROPY_FOR_BIP39=$(echo -n "${RESULT}" | openssl dgst -sha256 -binary | xxd -p -c 32) | |
| # 2. Calculate checksum (first 8 bits of SHA256) | |
| ENTROPY_BYTES=$(echo "${ENTROPY_FOR_BIP39}" | xxd -r -p) | |
| CHECKSUM_FULL=$(echo -n "${ENTROPY_BYTES}" | openssl dgst -sha256 -binary | xxd -p -c 32) | |
| CHECKSUM_BYTE=$(echo "${CHECKSUM_FULL}" | cut -c1-2) | |
| # 3. Combine entropy + checksum first byte (256 bits + 8 bits = 264 bits) | |
| FULL_BITS="${ENTROPY_FOR_BIP39}${CHECKSUM_BYTE}" | |
| # 4. Convert hex to binary string (portable method) | |
| BINARY="" | |
| for ((i=0; i<${#FULL_BITS}; i+=2)); do | |
| HEX_BYTE="${FULL_BITS:$i:2}" | |
| # Convert hex to decimal, then to binary | |
| DEC=$((16#${HEX_BYTE})) | |
| BIN=$(echo "obase=2; ${DEC}" | bc) | |
| # Pad to 8 bits | |
| BINARY="${BINARY}$(printf "%08d" ${BIN})" | |
| done | |
| # 5. Split into 11-bit chunks and map to words | |
| MNEMONIC="" | |
| for ((i=0; i<264; i+=11)); do | |
| # Extract 11 bits | |
| BITS="${BINARY:$i:11}" | |
| # Convert to decimal (word index) | |
| INDEX=$((2#${BITS})) | |
| # Get word from wordlist (line INDEX+1 since lines are 1-indexed) | |
| WORD=$(sed -n "$((INDEX + 1))p" "$WORDLIST") | |
| MNEMONIC="${MNEMONIC}${WORD} " | |
| done | |
| MNEMONIC_CLEAN="${MNEMONIC% }" # Remove trailing space | |
| echo "${MNEMONIC_CLEAN}" | |
| # Generate dot-map if requested | |
| if [ "${1}" = "--dotmap" ]; then | |
| echo "" | |
| echo -e "${BLUE}=== BIP39 Binary Dot-Map ===${NC}" | |
| echo "12 dots per word represent: 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1" | |
| echo "● = filled, ○ = empty (punch filled dots on metal plate)" | |
| echo "" | |
| echo "Word# Word [BIP39] | Col1 | Col2 | Col3 |" | |
| echo "------|------------|--------|------|------|------|" | |
| # Convert mnemonic to array | |
| read -ra WORDS <<< "${MNEMONIC_CLEAN}" | |
| # Print binary dot-map | |
| for i in "${!WORDS[@]}"; do | |
| WORD="${WORDS[$i]}" | |
| # Find word line number in wordlist (OneKey uses 1-based indexing) | |
| WORD_LINE=$(grep -n "^${WORD}$" "$WORDLIST" | cut -d: -f1) | |
| # Convert line number to 12-bit binary (OneKey format uses line numbers, not 0-based indices) | |
| BINARY=$(printf "%012d" $(echo "obase=2; ${WORD_LINE}" | bc)) | |
| # Split into 3 columns of 4 bits each | |
| COL1="" | |
| COL2="" | |
| COL3="" | |
| for ((j=0; j<4; j++)); do | |
| # Column 1: bits 0-3 (most significant) | |
| [ "${BINARY:$j:1}" = "1" ] && COL1="${COL1}●" || COL1="${COL1}○" | |
| # Column 2: bits 4-7 | |
| [ "${BINARY:$((j+4)):1}" = "1" ] && COL2="${COL2}●" || COL2="${COL2}○" | |
| # Column 3: bits 8-11 (least significant) | |
| [ "${BINARY:$((j+8)):1}" = "1" ] && COL3="${COL3}●" || COL3="${COL3}○" | |
| done | |
| # Format output | |
| # Word position in mnemonic (1-24), word text, line number (1-2048), binary dots | |
| printf " %2d %-12s [%04d] | %s | %s | %s |\n" \ | |
| $((i + 1)) "$WORD" "$WORD_LINE" "$COL1" "$COL2" "$COL3" | |
| done | |
| echo "" | |
| echo "Reference: https://github.com/OneKeyHQ/bip39-dotmap" | |
| fi | |
| else | |
| echo -e "${YELLOW}⚠${NC} BIP39 wordlist not available" | |
| fi | |
| echo "" | |
| fi | |
| echo -e "${BLUE}=== Details ===${NC}" | |
| echo "Length: 64 hex characters (256 bits)" | |
| echo "Format: Hexadecimal" | |
| echo "Sources: 6 independent entropy sources" | |
| echo " - Local: OS (/dev/urandom), System state" | |
| echo " - External: random.org, Solana, Google TLS, AWS KMS HSM" | |
| echo "" | |
| if [ "${1}" = "--bip39" ] || [ "${1}" = "-b" ] || [ "${1}" = "--dotmap" ]; then | |
| echo "BIP39: 24-word mnemonic phrase generated" | |
| if [ "${1}" = "--dotmap" ]; then | |
| echo "Dot-map: Grid coordinates for metal backup" | |
| fi | |
| echo "" | |
| fi | |
| echo -e "${GREEN}✓ Secure random number generated successfully${NC}" | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment