Q Safe Wallet
  • Getting Started
    • Overview
    • FAQs
  • Technical Details
    • Account Creation in Wallet
    • Quranium Chain Transaction Signing
    • ML-KEM Encryption for Backups
    • Backup and Recovery
  • All Features
    • Onboarding
    • Activity Section
    • Add Account
    • Multichain
    • Sending Transaction
    • Deposit Funds
    • Swapping
  • MULTICHAIN COMPATIBILITY
    • Quranium
    • Bitcoin & Derivatives
    • EVM
    • Solana
    • Other Chains
  • BEST PRACTICES
    • Secure Your Backup and Mnemonic
    • Validate Transaction Details Before Signing
    • Monitor Account Activity Across Chains
    • Safe Interaction with DApps
Powered by GitBook
On this page
  1. Technical Details

ML-KEM Encryption for Backups

Backup Encryption Workflow (ML-KEM)

Step 1: Data Preparation

  • Input: Wallet data (accounts, keys, metadata) is serialized into a JSON string.

  • Hashing: A SHA-256 hash of the JSON string is generated for integrity checks.

  • Serialization: Data is converted to a Buffer for encryption.

Step 2: ML-KEM Encryption

  • Key Encapsulation:

    • The recipient’s ML-KEM public key (stored in account.encryptionPublicKey) generates a shared secret.

    • ML-KEM’s encapsulate() function creates:

      • Ciphertext: Encapsulated shared secret (sent to the server).

      • Shared Secret: Used for symmetric encryption.

  • Symmetric Encryption:

    • The shared secret encrypts the backup data using XSalsa20 (or AES-256) for efficiency.

    • Result: A hybrid ciphertext (encryptedPayload) containing both ML-KEM ciphertext and symmetrically encrypted data.

Step 3: Signature Generation (SLH-DSA)

  • Hashing the Payload:

    • The encrypted payload is hashed using Keccak-256 to create a fixed-size digest.

  • SLH-DSA Signing:

    • The hash is signed using the user’s SLH-DSA private key (derived from their mnemonic).

    • Produces a 49,856-byte signature for tamper-proof verification.

Step 4: Cloud Storage

  • Payload Structure:

    {
      "signature": "0x...", // 49,856-byte SLH-DSA signature
      "payload": "0x..."    // ML-KEM ciphertext + symmetrically encrypted data
    }
  • Server-Side Handling:

    • Backups are stored under the user’s public key (account.publicKey).

    • The server uses SHA-256 to hash the public key for secure storage.

Backup Verification:

The server performs security checks before allowing access:

Check 1: SLH-DSA Signature Validation

  • Message Reconstruction:

    • The server generates valid messages based on timestamps (e.g., pubkey-GET-BACKUPS-2023-09-01).

const legitMessages = [  
  `${pubkey}-GET-BACKUPS-${ymdnow}`,  
  `${pubkey}-GET-BACKUPS-${ymdlb}`, // Lower bound timestamp  
  `${pubkey}-GET-BACKUPS-${ymdub}`, // Upper bound timestamp  
];  
  • Signature Verification:

    • The server uses the user’s SLH-DSA public key to verify the signature against all possible messages.

    • Ensures the request is recent and untampered.

    const valid = slh.slh_dsa_shake_256f.verify(  
      pubkeyBytes,  
      messageBuffer,  
      signature  
    );  

Check 2: Ownership Proof

  • Ownership is verified through SLH-DSA signature. Since ML-KEM and SLH-DSA keys are derived from the same mnemonic, validating SLH-DSA signatures proves control over the ML-KEM private key.

// Verify SLH-DSA signature to prove ownership of ML-KEM key
let provenOwnership = false;
for (const message of legitMessages) {
  const valid = slh.slh_dsa_shake_256f.verify(
    pubkeyBytes, // SLH-DSA public key (derived from same mnemonic as ML-KEM key)
    Buffer.from(message, "utf8"),
    byteStringToBytes(signature)
  );
  if (valid) {
    provenOwnership = true;
    break;
  }
}
if (!provenOwnership) {
  throw new HttpError(HttpStatus.BadRequest, ERROR_MESSAGE.INVALID_SIGNATURE);
}

Backup Retrieval & Decryption

Step 1: Fetching the Backup

  • Users request backups via their public key and a timestamped signature.

    const backup = await getBackup(userId);

Step 2: ML-KEM Decryption

  • Key Decapsulation:

    • The user’s ML-KEM private key decrypts the ciphertext to recover the shared secret.

    const sharedSecret = ml_kem.decapsulate(ciphertext, privateKey);
  • Symmetric Decryption:

    • The shared secret decrypts the symmetrically encrypted data.

Step 3: Data Reconstruction

  • The decrypted Buffer is parsed back into JSON.

  • Accounts are reinitialized in the wallet.

    const decryptedBackup: BackupData = JSON.parse(decryptedData.toString());

PreviousQuranium Chain Transaction SigningNextBackup and Recovery

Last updated 26 days ago