Skip to main content
Privora uses Program Derived Addresses (PDAs) to control who can request MPC decryption of encrypted values. This page explains the authorization system.

Overview

When a program produces encrypted results, it can create authorization PDAs that grant specific users the right to request decryption.

Authorization Types

DecryptionAuth

Standard authorization for a user to decrypt a specific encrypted value:
use privora_sdk_program::auth::pda::DecryptionAuth;

// Find the PDA for a user to decrypt some data
let (pda, bump) = DecryptionAuth::find_pda(&data_hash, &user_pubkey);
PDA Seeds:
["decrypt_auth", data_hash, user_pubkey]

MatchAuth

Authorization based on order matching (for orderbook-style applications):
use privora_sdk_program::auth::pda::MatchAuth;

// Authorize user for matched order data
let (pda, bump) = MatchAuth::find_pda(
    buy_order_id,
    sell_order_id,
    "price",      // field being authorized
    &user_pubkey,
);
PDA Seeds:
["match_auth", buy_order_id, sell_order_id, field, user_pubkey]

DecryptedResult

PDA where decrypted results are stored after MPC decryption:
use privora_sdk_program::auth::pda::DecryptedResult;

// Find where decrypted result will be stored
let (result_pda, bump) = DecryptedResult::find_pda(&data_hash, &user_pubkey);

Creating Authorization PDAs

Via CPI

Programs create authorization PDAs through CPI to the FHE Auth program:
use privora_sdk_program::auth::cpi::create_decryption_auth;

pub fn authorize_user(
    fhe_auth_program: AccountInfo,
    auth_pda: AccountInfo,
    data_hash: [u8; 32],
    user: Pubkey,
    payer: AccountInfo,
    system_program: AccountInfo,
) -> ProgramResult {
    create_decryption_auth(
        &fhe_auth_program,
        &auth_pda,
        &data_hash,
        &user,
        &payer,
        &system_program,
    )?;

    msg!("User {} authorized to decrypt", user);
    Ok(())
}

Match-Based Authorization

For orderbook matching, authorize both parties:
// In match_orders instruction
pub fn match_orders(
    buy_order: &Order,
    sell_order: &Order,
    // ... other accounts
) -> ProgramResult {
    // Perform FHE matching...
    let fill_price = sell_order.price_ref;
    let fill_qty = buy_qty.min(&sell_qty)?.store()?;

    // Authorize buy user for fill details
    create_match_authorization(
        buy_order.order_id,
        sell_order.order_id,
        "price",
        &buy_order.owner,
    )?;

    create_match_authorization(
        buy_order.order_id,
        sell_order.order_id,
        "quantity",
        &buy_order.owner,
    )?;

    // Authorize sell user for fill details
    create_match_authorization(
        buy_order.order_id,
        sell_order.order_id,
        "price",
        &sell_order.owner,
    )?;

    create_match_authorization(
        buy_order.order_id,
        sell_order.order_id,
        "quantity",
        &sell_order.owner,
    )?;

    Ok(())
}

Decryption Flow

Requesting Decryption

use privora_sdk_program::auth::cpi::request_decryption;

// Request decryption of authorized data
let plaintext = request_decryption(
    &data_hash,
    &user_pubkey,
    &auth_pda,
)?;

FHE Auth Program

The FHE Auth program manages authorization PDAs:
pub const FHE_AUTH_PROGRAM_ID: Pubkey =
    solana_pubkey::pubkey!("FheAuth111111111111111111111111111111111111");

Instructions

InstructionDescription
AuthorizeCreate authorization PDA for user
AuthorizeWithSeedsCreate PDA with custom seeds
RequestDecryptionRequest MPC decryption
RevokeRevoke authorization (close PDA)

Authorization Patterns

Pattern 1: Owner Authorization

Authorize the owner of encrypted data:
// When storing encrypted balance
let balance_ref = encrypted_balance.store()?;

// Authorize owner to decrypt their own balance
create_decryption_auth(
    &balance_ref.hash(),
    &owner_pubkey,
)?;

Pattern 2: Conditional Authorization

Authorize based on program logic:
// Only authorize if trade executed
if trade_executed {
    // Authorize buyer to see fill price
    create_decryption_auth(
        &fill_price_ref.hash(),
        &buyer_pubkey,
    )?;

    // Authorize seller to see fill price
    create_decryption_auth(
        &fill_price_ref.hash(),
        &seller_pubkey,
    )?;
}

Pattern 3: Time-Limited Authorization

Combine with on-chain time checks:
// Check if authorization period is valid
let current_time = Clock::get()?.unix_timestamp;
if current_time < authorization_expiry {
    create_decryption_auth(&data_hash, &user_pubkey)?;
}

Security Considerations

Authorization is Permanent

Once created, authorization PDAs grant decryption rights:
Authorization PDAs cannot be revoked after MPC decryption occurs. Design your authorization logic carefully.

Minimize Authorization Scope

Only authorize what’s necessary:
// Good: Specific authorization
create_decryption_auth(&specific_hash, &user)?;

// Avoid: Over-broad authorization patterns

Verify Authorization Before Creating

Check conditions before authorizing:
// Verify user should be authorized
if order.owner != user_pubkey {
    return Err(ProgramError::InvalidArgument);
}

create_decryption_auth(&order.price_ref.hash(), &user_pubkey)?;

Client-Side Decryption

Rust Client

use privora_sdk_client::prelude::*;

// After authorization PDA is created
let user_crypto = UserCrypto::from_keypair(&keypair)?;

// Request decryption
let encrypted_result = privora.request_decryption(&data_hash).await?;

// Decrypt with user's X25519 key
let plaintext = user_crypto.decrypt_from_mpc(
    &encrypted_result.ciphertext,
    &encrypted_result.nonce,
    &encrypted_result.ephemeral_pubkey,
)?;

TypeScript Client

// Request decryption
const encryptedResult = await privora.requestDecryption(dataHash);

// Decrypt with user's key
const plaintext = userCrypto.decryptFromMPC(
  encryptedResult.ciphertext,
  encryptedResult.nonce,
  encryptedResult.ephemeralPubkey
);

Best Practices

Principle of Least Privilege

Only authorize users who need access to specific data

Authorization Logging

Log authorization events for auditability

Dual Encryption for Own Data

Use dual encryption so users can view their own data without MPC

Test Authorization Logic

Thoroughly test who gets authorized under what conditions

Next Steps