Encrypt files before upload using AES-256-CTR or ECIES (secp256k1), and decrypt them transparently on download. Wire-compatible with the 0G TypeScript SDK and the Go storage client.
The Python SDK supports encrypting files before they’re uploaded to the
0G network, so storage nodes never see your plaintext. Two schemes are
available, both wire-compatible with the official TypeScript SDK and the
Go storage client:
Version
Scheme
Key material
Use case
v1
AES-256-CTR with a caller-supplied 32-byte key
Symmetric key
You manage the key out-of-band (e.g. KMS, password-derived)
v2
ECIES on secp256k1 + AES-256-CTR
Recipient’s public key for encrypt, private key for decrypt
Encrypt for an arbitrary recipient without pre-sharing a secret
A small encryption header (17 bytes for v1, 50 bytes for v2) is prepended
to the ciphertext and stored alongside it. The download path detects the
header automatically and decrypts when you supply the matching key.
If you download without symmetric_key, the raw ciphertext (including the
17-byte header) lands on disk and you can decrypt later via the
primitives — see Low-level primitives below.
ECIES lets anyone with the recipient’s secp256k1 public key encrypt a
file that only the holder of the matching private key can decrypt. The
SDK derives a fresh ephemeral keypair per encryption and embeds the
ephemeral public key in the header, so no key needs to be pre-shared.
Indexer.peek_header(root_hash) fetches just enough bytes from the first
segment to identify whether a stored file is encrypted and which scheme
it uses. Useful for rendering a “this file is encrypted, supply a key”
prompt before committing to a full download.
header, err = indexer.peek_header(root_hash)if err is not None: raise errif header is None: print("Plaintext file — download directly")elif header.version == 1: print("AES-256 — supply symmetric_key= to download")elif header.version == 2: print("ECIES — supply private_key= to download")
try_decrypt is non-throwing — it returns the raw bytes unchanged if
decryption fails for any reason (file not encrypted, wrong key type,
off-curve ephemeral pubkey, etc.) so callers can fall back to the
ciphertext:
from core.decryption import try_decryptwith open("./downloaded.bin", "rb") as f: raw = f.read()result = try_decrypt(raw, symmetric_key=key) # v1# orresult = try_decrypt(raw, private_key=PRIVATE_KEY) # v2if result.decrypted: plaintext = result.byteselse: print("Not encrypted, or wrong key — returned raw bytes")
The Python SDK’s encryption module is a direct port of
src.ts/common/encryption.ts from @0gfoundation/0g-ts-sdk and uses the
same TS reference vectors for its test suite.
AES-CTR has no authentication tag — it does not detect tampering. If
you need authenticated encryption on top, encrypt with the SDK then
sign the resulting ciphertext separately, or wrap the plaintext with
an HMAC before uploading.