/api/wallet/encryption/key-struct

GET

Returns the Encryption Key Structure - a special 25-element array showing which coin's Authentication Numbers (ANs) are used for encrypted RAIDA communications per RAIDA server.

Description

The /api/wallet/encryption/key-struct endpoint reveals the internal encryption key structure used by CloudCoin Console for secure RAIDA communications. When you send encrypted requests to RAIDA servers, each of the 25 RAIDAs requires a specific Authentication Number (AN) from a coin in your wallet to decrypt the request.

This endpoint returns a detailed map showing:

  • Which coin provides the AN for each RAIDA (serial number and denomination)
  • The actual AN used for that RAIDA (16 bytes as 32 hex characters)
  • The encryption mode - whether keys are healthy ("bank"), fractured ("fracked"), or missing ("none")
šŸ“Š Understanding the Key Structure

The encryption key structure is critical for debugging encrypted operations. It answers questions like:

  • "Why is encrypted Echo failing on RAIDA 5?" - Check if the coin providing RAIDA 5's AN is valid
  • "Are my encryption keys fracked?" - Look at the mode: "bank" = healthy, "fracked" = needs fixing
  • "Which coin do I need to fix to restore encryption?" - See which serial numbers appear in the key struct
šŸ”’ Security Warning

IMPORTANT: Authentication Numbers (ANs) are highly sensitive cryptographic secrets. The ANs returned by this endpoint are equivalent to passwords that control your coins. This endpoint should only be used for:

  • Debugging: Diagnosing encryption issues with RAIDA communications
  • Internal tools: Administrative or development purposes only
  • Secure environments: Never expose this data over unsecured connections

Always protect the API response containing ANs. Do not log, transmit, or display ANs in production systems.

Encryption Modes

The endpoint automatically detects and reports one of three encryption modes based on the wallet's current state:

Mode 1: Bank Mode (Ideal)

Indicator: "mode": "bank"

Meaning: All 25 RAIDAs use the same coin's ANs for encryption. This is the best-case scenario.

Characteristics:

  • All 25 RAIDAs have identical serial_number and denomination
  • The coin is randomly selected from the Bank folder
  • Encrypted operations work reliably across all RAIDAs
  • No encryption key loss - wallet is healthy

Action Required: None - encryption keys are in optimal state

Mode 2: Fracked Mode (Needs Attention)

Indicator: "mode": "fracked"

Meaning: Different RAIDAs use ANs from different coins. The encryption key structure is fragmented across multiple coins.

Characteristics:

  • RAIDAs have different serial_number or denomination values
  • ANs are sourced from multiple coins in the Fracked folder
  • Indicates past POWN operations only partially succeeded
  • Encrypted operations may fail inconsistently across RAIDAs

Action Required: Run /api/program/fix-encryption to consolidate encryption keys back to a single coin

Mode 3: None (Critical)

Indicator: "mode": "none"

Meaning: No coins are available to provide encryption keys. Encrypted operations cannot work.

Characteristics:

  • All serial_number values are 0
  • All denomination values are 0
  • No coins in Bank or Fracked folders
  • All encrypted RAIDA requests will fail

Action Required: Import coins into the wallet or recover from backup. Without coins, encryption is not possible.

Parameters

This endpoint does not require any parameters.

Response

Returns a JSON object containing the encryption key structure with 25 elements (one per RAIDA server).

Response Properties

status string
Always "success" if the key structure was retrieved.
operation string
Always "encryption-key-struct".
mode string
One of: "bank" (healthy), "fracked" (fragmented), or "none" (no keys available).
message string
Human-readable description of the encryption mode.
key_struct array[object]
Array of 25 objects (one per RAIDA) with the following properties:
  • raida_id (integer): RAIDA server ID (0-24)
  • denomination (integer): Denomination of the coin providing the AN (-8 to 11)
  • serial_number (integer): Serial number of the coin providing the AN
  • an (string): The 16-byte Authentication Number as 32 hex characters

Example Response (Bank Mode)

{
  "status": "success",
  "operation": "encryption-key-struct",
  "mode": "bank",
  "message": "Using random coin from Bank folder",
  "key_struct": [
    {
      "raida_id": 0,
      "denomination": 3,
      "serial_number": 12345,
      "an": "a1b2c3d4e5f67890a1b2c3d4e5f67890"
    },
    {
      "raida_id": 1,
      "denomination": 3,
      "serial_number": 12345,
      "an": "f1e2d3c4b5a67890f1e2d3c4b5a67890"
    },
    {
      "raida_id": 2,
      "denomination": 3,
      "serial_number": 12345,
      "an": "0123456789abcdef0123456789abcdef"
    },
    ...
    {
      "raida_id": 24,
      "denomination": 3,
      "serial_number": 12345,
      "an": "fedcba9876543210fedcba9876543210"
    }
  ]
}

Note: In Bank mode, all 25 RAIDAs have the same serial_number (12345) and denomination (3 = 100 CloudCoin). Each RAIDA uses a different AN from the same coin (coins have 25 ANs, one per RAIDA).

Example Response (Fracked Mode)

{
  "status": "success",
  "operation": "encryption-key-struct",
  "mode": "fracked",
  "message": "Using combined good ANs from Fracked coins",
  "key_struct": [
    {
      "raida_id": 0,
      "denomination": 3,
      "serial_number": 12345,
      "an": "a1b2c3d4e5f67890a1b2c3d4e5f67890"
    },
    {
      "raida_id": 1,
      "denomination": 4,
      "serial_number": 67890,
      "an": "f1e2d3c4b5a67890f1e2d3c4b5a67890"
    },
    {
      "raida_id": 2,
      "denomination": 3,
      "serial_number": 12345,
      "an": "0123456789abcdef0123456789abcdef"
    },
    ...
    {
      "raida_id": 24,
      "denomination": 5,
      "serial_number": 99999,
      "an": "fedcba9876543210fedcba9876543210"
    }
  ]
}

Note: In Fracked mode, different RAIDAs have different serial_numbers and denominations. RAIDA 0 uses SN 12345, RAIDA 1 uses SN 67890, RAIDA 24 uses SN 99999. This indicates the encryption key structure is fragmented across multiple coins.

Example Response (None Mode)

{
  "status": "success",
  "operation": "encryption-key-struct",
  "mode": "none",
  "message": "No coins available for encryption key struct",
  "key_struct": [
    {
      "raida_id": 0,
      "denomination": 0,
      "serial_number": 0,
      "an": "00000000000000000000000000000000"
    },
    {
      "raida_id": 1,
      "denomination": 0,
      "serial_number": 0,
      "an": "00000000000000000000000000000000"
    },
    ...
    {
      "raida_id": 24,
      "denomination": 0,
      "serial_number": 0,
      "an": "00000000000000000000000000000000"
    }
  ]
}

Note: In None mode, all serial_numbers and denominations are 0. No coins are available in the wallet to provide encryption keys.

Error Responses

500 Internal Server Error

Returned if the system fails to retrieve the encryption key structure.

Example Error Response

{
  "status": "error",
  "message": "Failed to get encryption key struct"
}

Examples

JavaScript (fetch)

const API_HOST = 'http://localhost:8080';

async function getEncryptionKeyStruct() {
    try {
        const response = await fetch(`${API_HOST}/api/wallet/encryption/key-struct`);
        const result = await response.json();

        console.log(`Encryption Mode: ${result.mode}`);
        console.log(`Message: ${result.message}`);

        // Check if keys are fragmented
        if (result.mode === 'fracked') {
            console.warn('āš ļø Encryption keys are fracked across multiple coins!');

            // Find unique serial numbers
            const uniqueSNs = new Set(
                result.key_struct.map(item => item.serial_number)
            );
            console.log(`Using ${uniqueSNs.size} different coins for encryption`);

            // Show which RAIDAs use which coins
            result.key_struct.forEach(item => {
                console.log(`RAIDA ${item.raida_id}: SN ${item.serial_number}, Denom ${item.denomination}`);
            });

            console.log('šŸ’” Run /api/program/fix-encryption to consolidate keys');
        } else if (result.mode === 'bank') {
            console.log('āœ“ Encryption keys are healthy');
            const coin = result.key_struct[0];
            console.log(`Using coin SN ${coin.serial_number}, Denom ${coin.denomination}`);
        } else if (result.mode === 'none') {
            console.error('āœ— No encryption keys available - wallet has no coins');
        }

        return result;
    } catch (error) {
        console.error('Error fetching encryption key struct:', error);
        throw error;
    }
}

// Example: Debug encrypted Echo failures
async function debugEncryptedEcho() {
    console.log('Step 1: Get encryption key structure...');
    const keyStruct = await getEncryptionKeyStruct();

    console.log('\nStep 2: Test encrypted Echo...');
    const echoResponse = await fetch(`${API_HOST}/api/program/echo?encrypted=true`);
    const echoResult = await echoResponse.json();

    console.log('\nStep 3: Compare results...');
    echoResult.raida_responses.forEach((raida, index) => {
        if (!raida.success) {
            const key = keyStruct.key_struct[index];
            console.error(
                `RAIDA ${index} failed - using coin SN ${key.serial_number}, ` +
                `AN: ${key.an.substring(0, 8)}...`
            );
        }
    });
}

getEncryptionKeyStruct();

cURL

# Get encryption key structure
curl -X GET "http://localhost:8080/api/wallet/encryption/key-struct"

# Get with formatted output using jq
curl -s "http://localhost:8080/api/wallet/encryption/key-struct" | jq '.'

# Check encryption mode only
curl -s "http://localhost:8080/api/wallet/encryption/key-struct" | jq '.mode'

# Count unique serial numbers (detect fracked keys)
curl -s "http://localhost:8080/api/wallet/encryption/key-struct" \
  | jq '.key_struct | map(.serial_number) | unique | length'

# Show which coin is used per RAIDA
curl -s "http://localhost:8080/api/wallet/encryption/key-struct" \
  | jq '.key_struct[] | "RAIDA \(.raida_id): SN \(.serial_number), Denom \(.denomination)"'

# Example: Full diagnostic script
#!/bin/bash
echo "=== Encryption Key Diagnostic ==="
RESPONSE=$(curl -s "http://localhost:8080/api/wallet/encryption/key-struct")

MODE=$(echo "$RESPONSE" | jq -r '.mode')
echo "Mode: $MODE"

if [ "$MODE" = "fracked" ]; then
    echo "āš ļø WARNING: Encryption keys are fracked!"
    UNIQUE_COUNT=$(echo "$RESPONSE" | jq '.key_struct | map(.serial_number) | unique | length')
    echo "Using $UNIQUE_COUNT different coins"
    echo "Run: curl -X GET 'http://localhost:8080/api/program/fix-encryption'"
elif [ "$MODE" = "bank" ]; then
    echo "āœ“ Encryption keys are healthy"
    SN=$(echo "$RESPONSE" | jq -r '.key_struct[0].serial_number')
    echo "Using coin SN: $SN"
elif [ "$MODE" = "none" ]; then
    echo "āœ— ERROR: No encryption keys available"
fi

Go

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

const ApiHost = "http://localhost:8080"

type KeyStructItem struct {
    RaidaID      int    `json:"raida_id"`
    Denomination int    `json:"denomination"`
    SerialNumber int    `json:"serial_number"`
    AN           string `json:"an"`
}

type EncryptionKeyStructResponse struct {
    Status    string          `json:"status"`
    Operation string          `json:"operation"`
    Mode      string          `json:"mode"`
    Message   string          `json:"message"`
    KeyStruct []KeyStructItem `json:"key_struct"`
}

func getEncryptionKeyStruct() (*EncryptionKeyStructResponse, error) {
    resp, err := http.Get(fmt.Sprintf("%s/api/wallet/encryption/key-struct", ApiHost))
    if err != nil {
        return nil, fmt.Errorf("request failed: %w", err)
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("read failed: %w", err)
    }

    var result EncryptionKeyStructResponse
    if err := json.Unmarshal(body, &result); err != nil {
        return nil, fmt.Errorf("JSON parse failed: %w", err)
    }

    return &result, nil
}

func analyzeEncryptionKeys(result *EncryptionKeyStructResponse) {
    fmt.Printf("Encryption Mode: %s\n", result.Mode)
    fmt.Printf("Message: %s\n", result.Message)

    switch result.Mode {
    case "fracked":
        fmt.Println("āš ļø WARNING: Encryption keys are fracked!")

        // Count unique serial numbers
        uniqueSNs := make(map[int]bool)
        for _, item := range result.KeyStruct {
            uniqueSNs[item.SerialNumber] = true
        }
        fmt.Printf("Using %d different coins for encryption\n", len(uniqueSNs))

        // Show breakdown per RAIDA
        fmt.Println("\nRAIDA Breakdown:")
        for _, item := range result.KeyStruct {
            fmt.Printf("  RAIDA %2d: SN %d, Denom %d, AN %s...\n",
                item.RaidaID,
                item.SerialNumber,
                item.Denomination,
                item.AN[:16])
        }

        fmt.Println("\nšŸ’” Run /api/program/fix-encryption to consolidate keys")

    case "bank":
        fmt.Println("āœ“ Encryption keys are healthy")
        coin := result.KeyStruct[0]
        fmt.Printf("Using coin SN %d, Denomination %d\n",
            coin.SerialNumber,
            coin.Denomination)

    case "none":
        fmt.Println("āœ— ERROR: No encryption keys available")
        fmt.Println("Wallet has no coins - import coins to enable encryption")
    }
}

func main() {
    fmt.Println("Fetching encryption key structure...")

    result, err := getEncryptionKeyStruct()
    if err != nil {
        panic(err)
    }

    fmt.Println("\n=== Encryption Key Analysis ===")
    analyzeEncryptionKeys(result)

    // Optional: Save full structure to file for debugging
    if result.Mode == "fracked" {
        fmt.Println("\nSaving full key structure to key_struct.json...")
        jsonData, _ := json.MarshalIndent(result, "", "  ")
        if err := ioutil.WriteFile("key_struct.json", jsonData, 0644); err != nil {
            fmt.Printf("Warning: Failed to save file: %v\n", err)
        } else {
            fmt.Println("Saved to key_struct.json")
        }
    }
}

Understanding Authentication Numbers (ANs)

Each CloudCoin contains 25 Authentication Numbers (ANs) - one for each RAIDA server. When you send an encrypted request:

  1. Client encrypts: Your client uses the AN for RAIDA X to encrypt the request payload using AES-128-CTR
  2. RAIDA decrypts: RAIDA X uses its stored AN for your coin to decrypt the request
  3. Verification: If decryption succeeds and CRC matches, the request is authenticated
šŸ’” Why 25 Different ANs?

CloudCoin's security model uses distributed trust - no single RAIDA knows all 25 ANs for a coin. Each RAIDA only knows one AN (its own). This prevents any single RAIDA from compromising the entire coin. The encryption key structure shows which AN from which coin is used for each RAIDA.

Common Use Cases

1. Debugging Encrypted Echo Failures

If encrypted Echo requests fail on specific RAIDAs, check the key structure to see which coin's AN is being used for those RAIDAs. The coin may be fracked or counterfeit on those RAIDAs.

2. Diagnosing Fracked Encryption Keys

Run this endpoint to see if your encryption keys are fragmented. If mode: "fracked", you'll see different serial numbers across RAIDAs. Use /api/program/fix-encryption to consolidate.

3. Verifying Encryption Key Health

After running fix-encryption or importing new coins, check that mode: "bank" and all 25 RAIDAs use the same serial number.

4. Identifying Which Coin to Fix

If encrypted operations are failing, the key struct reveals which coin(s) are providing the ANs. You can then focus on fixing those specific coins in the Fracked folder.

āš ļø Production Security

DO NOT expose this endpoint to untrusted clients. The ANs returned are equivalent to private keys for your coins. This endpoint is intended for:

  • Administrative dashboards (secured with authentication)
  • Development/debugging tools (local environments only)
  • Internal monitoring systems (behind firewalls)

In production APIs, consider requiring authentication tokens and rate limiting.

Related Endpoints

/api/program/login

Set the encryption key for accessing encrypted files. Must be called before working with encrypted coins.

/api/program/fix-encryption

Fix fracked encryption keys by consolidating fragmented ANs back to a single coin using RAIDA healing.

/api/program/detect-lost-encryption

Detect which RAIDAs have lost encryption connections by scanning wallet POWN strings.

/api/program/echo

Test RAIDA connectivity with optional encryption. Use ?encrypted=true to test encrypted communications.

Technical Implementation

The /api/wallet/encryption/key-struct endpoint internally calls cc_get_encryption_key_struct() from utils.c, which implements the following algorithm:

// Simplified algorithm from utils.c
cc_result_t cc_get_encryption_key_struct(EncryptionKeyStruct* out_struct) {
    // Step 1: Try to find a single Bank coin with all 'p' (pass) status
    for each coin in Bank folder {
        if (all_25_raidas_have_pass_status(coin)) {
            // Bank mode: Use this coin's 25 ANs
            for (int raida = 0; raida < 25; raida++) {
                out_struct->serial_number[raida] = coin.serial_number;
                out_struct->denomination[raida] = coin.denomination;
                memcpy(out_struct->ans[raida], coin.ans[raida], 16);
            }
            return CC_SUCCESS;  // Mode: "bank"
        }
    }

    // Step 2: Build key struct from Fracked coins
    // For each RAIDA, find the best coin with 'p' status on that RAIDA
    for (int raida = 0; raida < 25; raida++) {
        CloudCoin* best_coin = NULL;
        int best_pass_count = 0;

        for each coin in Fracked folder {
            if (coin.pown_string[raida] == 'p') {
                int pass_count = count_passes(coin.pown_string);
                if (pass_count > best_pass_count) {
                    best_coin = &coin;
                    best_pass_count = pass_count;
                }
            }
        }

        if (best_coin != NULL) {
            // Found a coin with 'p' on this RAIDA
            out_struct->serial_number[raida] = best_coin->serial_number;
            out_struct->denomination[raida] = best_coin->denomination;
            memcpy(out_struct->ans[raida], best_coin->ans[raida], 16);
        } else {
            // No coin found - set to zeros
            out_struct->serial_number[raida] = 0;
            out_struct->denomination[raida] = 0;
            memset(out_struct->ans[raida], 0, 16);
        }
    }

    return CC_SUCCESS;  // Mode: "fracked" or "none"
}

The mode is determined by examining the resulting structure:

  • Bank: All 25 RAIDAs have the same serial_number and denomination
  • Fracked: RAIDAs have different serial_numbers or denominations
  • None: All serial_numbers and denominations are 0