Guides · Partie 4 sur 6
Construire une PoE scellée
Une PoE simple prouve _qu’_un contenu donné a existé. Une PoE scellée prouve la même chose tout en gardant le contenu lui-même secret : vous chiffrez les octets pour une ou plusieurs clés de destinataire, ne stockez que le texte chiffré et ancrez l’enregistrement sur la chaîne. Quiconque peut constater que l’enregistrement existe et en vérifier la structure ; seul un détenteur d’une clé privée correspondante peut déchiffrer la charge utile. Voir Sealed PoE pour le format de l’enveloppe et Sealed until claimed pour le modèle de menace.
Adresser vos destinataires
Un destinataire est identifié par une chaîne de style age. Il en existe deux
types, que le préfixe permet de distinguer :
age1…— une clé classique X25519 (32 octets).age1pqc…— une clé hybride X-Wing (ML-KEM-768 + X25519, 1216 octets).
X-Wing (mlkem768x25519) est le KEM par défaut : il reste sûr face à un
futur adversaire quantique, et toute identité dispose toujours d’une adresse
age1pqc….
Un destinataire vous transmet sa chaîne par un canal externe. Vous la décodez
pour retrouver la clé publique brute dont l’assistant de scellement a besoin à
l’aide de parseAgeRecipient :
import { parseAgeRecipient } from '@cardanowall/sdk-ts';
const them = parseAgeRecipient('age1pqc…'); // { kem: 'mlkem768x25519', publicKey: Uint8Array }Si vous détenez vous-même un seed de 32 octets, recipientsFromSeed vous fournit
vos deux adresses propres : partagez-en une pour que d’autres puissent vous
sceller des enregistrements, et incluez votre propre clé dans la liste des
destinataires afin de conserver l’accès en lecture à ce que vous envoyez :
import { recipientsFromSeed } from '@cardanowall/sdk-ts';
const me = recipientsFromSeed(mySeed); // { age: 'age1…', age1pqc: 'age1pqc…' }Sceller et publier
Le scellement passe par une passerelle, qui construit et diffuse la transaction Cardano et stocke le texte chiffré à votre place. Pointez le client vers la passerelle que vous utilisez ; le SDK est indépendant de la passerelle.
publishSealed reçoit des clés publiques de destinataire brutes ; recueillez
donc le publicKey de chaque adresse décodée. Tous les destinataires doivent
partager un même KEM : gardez les clés age1pqc… ensemble. Verrouillez d’abord
un prix avec quote, puis publiez :
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’assistant chiffre le contenu, téléverse le texte chiffré, lie son URI ar://
et l’empreinte du texte en clair dans un enregistrement Label 309 et le soumet.
Votre seed et le texte en clair ne quittent jamais votre machine en clair.
Avec Python
cardanowall-sdk est un jumeau octet pour octet : même KEM par défaut, même
enveloppe :
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())Avec Rust
Le crate cardanowall scelle via le même client de passerelle.
parse_age_recipient décode chaque adresse en clé brute, et kem: None
conserve la valeur par défaut 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 publie des enregistrements à empreinte seule et des enregistrements Merkle ; le scellement est aujourd’hui un flux propre au SDK.
Une fois l’enregistrement consolidé, chaque destinataire le découvre, déchiffre la charge utile avec sa clé privée et recalcule l’empreinte du texte en clair pour boucler la boucle : c’est la moitié destinataire de Vérifier un enregistrement.
Scellez aussi pour vous-même
publishSealed ne vous ajoute jamais en silence à la liste des destinataires. Si vous n’incluez
pas l’une de vos propres clés, vous publiez un enregistrement que vous ne pourrez jamais relire.
Incluez me.age1pqc (ou me.age) parmi les destinataires chaque fois que vous voulez conserver
l’accès à ce que vous avez envoyé.