Guide · Parte 4 di 6
Costruire una PoE sigillata
Una PoE semplice dimostra che un certo contenuto è esistito. Una PoE sigillata dimostra la stessa cosa mantenendo segreto il contenuto stesso: cifri i byte per una o più chiavi di destinatario, conservi solo il testo cifrato e ancori il record on-chain (sulla blockchain). Chiunque può vedere che il record esiste e verificarne la struttura; solo chi possiede una chiave privata corrispondente può decifrare il payload. Vedi Sealed PoE per il formato della busta e Sealed until claimed per il modello di minaccia.
Indirizzare i destinatari
Un destinatario è identificato da una stringa in stile age. Ne esistono due tipi, distinti
dal prefisso:
age1…— una chiave classica X25519 (32 byte).age1pqc…— una chiave ibrida X-Wing (ML-KEM-768 + X25519, 1216 byte).
X-Wing (mlkem768x25519) è il KEM predefinito: resta sicuro contro un futuro avversario
quantistico, e ogni identità dispone sempre di un indirizzo age1pqc….
Un destinatario ti consegna la propria stringa fuori banda. La decodifichi nella chiave
pubblica grezza di cui ha bisogno l'helper di sigillatura con parseAgeRecipient:
import { parseAgeRecipient } from '@cardanowall/sdk-ts';
const them = parseAgeRecipient('age1pqc…'); // { kem: 'mlkem768x25519', publicKey: Uint8Array }Se hai tu stesso un seed a 32 byte, recipientsFromSeed ti restituisce entrambi i tuoi
indirizzi: condividine uno perché altri possano sigillare per te, e includi la tua chiave
nella lista dei destinatari per mantenere l'accesso in lettura a ciò che invii:
import { recipientsFromSeed } from '@cardanowall/sdk-ts';
const me = recipientsFromSeed(mySeed); // { age: 'age1…', age1pqc: 'age1pqc…' }Sigillare e pubblicare
La sigillatura passa per un gateway, che costruisce e trasmette la transazione Cardano e conserva il testo cifrato al posto tuo. Punta il client al gateway che usi; l'SDK è indipendente dal gateway.
publishSealed accetta le chiavi pubbliche grezze dei destinatari, quindi raccogli la
publicKey da ciascun indirizzo decodificato. Tutti i destinatari devono condividere lo
stesso KEM: tieni insieme le chiavi age1pqc…. Prima blocca un prezzo con quote, poi
pubblica:
import { Label309Client, parseAgeRecipient } from '@cardanowall/sdk-ts';
const client = new Label309Client({
baseUrl: 'https://your-gateway.example',
apiKey: process.env.CW_API_KEY,
});
const content = new TextEncoder().encode('the secret payload');
const recipients = ['age1pqc…recipient', me.age1pqc].map((r) => parseAgeRecipient(r).publicKey);
const quote = await client.poe.quote({
recordBytes: 512,
recipientCount: recipients.length,
fileBytesTotal: content.length,
});
const result = await client.poe.publishSealed({
content,
recipients,
quoteId: quote.quote_id,
// kem defaults to 'mlkem768x25519' (X-Wing); pass 'x25519' only for age1… keys.
});
console.log(result.tx_hash);L'helper cifra il contenuto, carica il testo cifrato, lega il suo URI ar:// e l'hash del
testo in chiaro in un record Label 309 e lo invia. Il tuo seed e il testo in chiaro non
lasciano mai la tua macchina in chiaro.
Con Python
cardanowall-sdk è un gemello byte per byte: stesso KEM predefinito, stessa busta:
import asyncio
import os
from cardanowall import Label309Client, parse_age_recipient
async def main():
content = b"the secret payload"
recipients = [parse_age_recipient("age1pqc…recipient").public_key]
async with Label309Client(
base_url="https://your-gateway.example",
api_key=os.environ["CW_API_KEY"],
) as client:
quote = await client.poe.quote(
record_bytes=512, recipient_count=len(recipients), file_bytes_total=len(content)
)
result = await client.poe.publish_sealed(
content=content, recipients=recipients, quote_id=quote["quote_id"]
)
print(result["tx_hash"])
asyncio.run(main())Con Rust
Il crate cardanowall sigilla tramite lo stesso client gateway. parse_age_recipient
decodifica ogni indirizzo nella chiave grezza, e kem: None mantiene il valore predefinito
X-Wing:
use cardanowall::client::{
Label309Client, Label309ClientConfig, PublishSealedInput, QuoteInput,
};
use cardanowall::recipient::parse_age_recipient;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Label309Client::new(Label309ClientConfig {
base_url: Some("https://your-gateway.example".into()),
api_key: std::env::var("CW_API_KEY").ok(),
})?;
let content = b"the secret payload".to_vec();
let recipients = vec![parse_age_recipient("age1pqc…recipient")?.public_key];
let quote = client.poe().quote(&QuoteInput {
record_bytes: 512,
recipient_count: recipients.len() as u64,
file_bytes_total: content.len() as u64,
})?;
let result = client.poe().publish_sealed(&PublishSealedInput {
content,
recipients,
quote_id: quote.quote_id,
hash_alg: None,
kem: None, // defaults to mlkem768x25519 (X-Wing); set Some for x25519
signer: None,
idempotency_key: None,
})?;
println!("{:?}", result.tx_hash);
Ok(())
}La CLI pubblica record di solo hash e record Merkle; la sigillatura è oggi un flusso disponibile solo via SDK.
Una volta che il record è definitivamente registrato, ogni destinatario lo scopre, decifra il payload con la propria chiave privata e ricalcola l'hash del testo in chiaro per chiudere il cerchio: è la parte del destinatario in Verificare un record.
Sigilla anche per te stesso
publishSealed non ti aggiunge mai in silenzio alla lista dei destinatari. Se non includi una
delle tue chiavi, pubblichi un record che non potrai mai più rileggere. Includi me.age1pqc (o
me.age) tra i destinatari ogni volta che vuoi mantenere l'accesso a ciò che hai inviato.