ERC-7573 Decryption Oracle: Smart Contract for Generation/Verification/Decryption of Encrypted Keys


⚠️ Before you continue: Please read the Disclaimer.
By using this site, software, or contracts, you acknowledge that you have read and accepted it.


Introduction

The finmath keys decryption-oracle project provides open source implementations of the ERC-7573 decryption oracle (for secure stateless delivery-vs-payment).

Interfaces

The batch-enabled interfaces are included with this project and published as @finmath.net/dvp@0.8.0. The vendored interfaces have the same declarations, ABI, and NatSpec as that package. Their declarations are ordered generate, verify, decrypt for readability.

Published Legacy Deployments (2.5.7)

These deployments predate batch key generation. They are incompatible with both the tagged 3.0.0 ABI and the 4.0.1 ABI in this source tree. No public deployment of the 4.0.1 ABI has been published yet.

Ethereum Mainnet

0xa116A2BDbef2BA379eD6eCED40504D4f28c755fc

Polygon Mainnet

0xB387746f1048645F142cAC13e762A3931f3114Ba

Sepolia (Ethereum Testnet)

0xda273EFE2F491903AB3DAf8Bee1A79A8F64e33E0

Amoy (Polygon Testnet)

0x86A6A4707526e230B66FE6E15A321b16000C076f

Use these only with the 2.5.7 interfaces.

Using the Decryption Oracle Contract

The interaction with the decryption oracle is asynchronous following a request*/fulfill* pattern.

Send your request to an instance (address) of the decryption oracle contract using one of the following three functions…

interface IKeyDecryptionOracle {
    struct EncryptedKey {
        bytes32 keyId;
        bytes encryptedKey;
    }

    function requestGenerateEncryptedHashedKeys(uint256 id, IKeyDecryptionOracleCallback callback, address receiverContract, bytes calldata transaction, bytes32[] calldata keyIds) external payable returns (uint256 requestId);
    function requestVerifyEncryptedKeys(uint256 id, EncryptedKey[] calldata keys, IKeyDecryptionOracleCallback callback) external payable returns (uint256 requestId);
    function requestDecrypt(uint256 id, bytes calldata encryptedKey, IKeyDecryptionOracleCallback callback, bytes calldata transaction) external payable returns (uint256 requestId);
}

To obtain the result implement the IKeyDecryptionOracleCallback and pass the address of an instance that should receive the results as callback. The results will then be passed to the corresponding method:

interface IKeyDecryptionOracleCallback {
    struct EncryptedHashedKey {
        bytes32 keyId;
        bytes encryptedKey;
        bytes hashedKey;
    }

    function onEncryptedHashedKeysGenerated(uint256 requestId, EncryptedHashedKey[] calldata keys, address receiverContract, bytes calldata transaction) external;
    function onEncryptedKeysVerificationCompleted(uint256 requestId, bool verified, EncryptedHashedKey[] calldata keys, address receiverContract, bytes calldata transaction) external;
    function onKeyReleased(uint256 requestId, bytes calldata key) external;
    function onKeyDenied(uint256 requestId) external;
}

keyIds must be non-empty and contain distinct semantic identifiers. A one-element array is the single-key form. The fulfillment must return exactly those identifiers (in any order), and the proxy delivers the complete result in one callback after validating the batch, receiverContract, and transaction. Verification is also atomic and role-tagged. Its callback returns the complete requested set and an explicit verified result; consumers must not infer rejection from empty data. The reference proxy accepts at most 32 generation or verification keys and charges feeGenerate() or feeVerify() per key.

The Java oracle remains protocol-neutral. Its encrypted release-key document contains only the release secret, eligible receiver, and transaction. Semantic keyId values, consumer id, requester, replay protection, and outcome meaning belong to the proxy and consuming contract.

Atomic verification covers the exact set named by that verification request. If an application requires multiple outcome roles, its consumer must submit and validate all of them; verification of a subset from an earlier generation batch remains a valid generic single-key request. References may be constructed with the oracle public key. Therefore, applications relying on the generation flow must authenticate its callback, pin the returned (keyId, encryptedKey, hashedKey) material, and compare it during verification; reference verification alone is not generation proof. Generic verification establishes the release-key hash, receiver, and transaction; it does not establish generator provenance or the semantic meaning of keyId. The 0.8.0 interface's use of “authenticate” refers to the outer role-tagged tuple together with proxy/consumer request state, not extra fields inside the encrypted XML document.

Every request returns an oracle-assigned requestId, which is emitted in the request event, used for fulfillment, and passed to the callback. The consumer-supplied id remains context in the request event. Consumers must correlate pending callbacks by (oracle, requestId) and validate the expected operation kind.

Migrating from 3.0.0 to 4.0.1

Version 3.0.0 introduced batch generation through requestGenerateEncryptedHashedKeys and onEncryptedHashedKeysGenerated, but retained scalar verification and passed the caller-supplied id to callbacks. Version 4.0.1 replaces scalar verification with requestVerifyEncryptedKeys and onEncryptedKeysVerificationCompleted; all callbacks now receive the oracle-assigned requestId instead of the caller's id.

These changes break the 3.0.0 ABI, but the encrypted release-key format is unchanged. Redeploy the proxy and all callback consumers, and upgrade the off-chain service at the same time. Do not combine a 4.0.1 contract or consumer with a tagged 3.0.0 service. Custom proxies used with the reference service must also implement isRequestPending(requestId).

For production, use KeyDecryptionOracle_DefaultPermissioned (or construct the base contract with requestsPermissionless == false) and allowlist only trusted consumer contracts. Plaintext keys are visible in fulfillment transaction calldata; callback validation cannot undo disclosure from an unauthorized request. The permissionless wrapper and DeployForDemo are for local demos only.

IMPORTANT: A compatible deployment may require a small fee to cover the gas cost and protect against over-use (feeGenerate, feeVerify, feeDecrypt return the current fee in POL or ETH wei; generation and verification fees are per key). For example, call decrypt via

oracle.requestDecrypt{value: KeyDecryptionOracle(payable(address(oracle))).feeDecrypt()}(
  id, encryptedKey, callback, transaction
);

Literature

For a technical/mathematical description of related concepts see https://ssrn.com/abstract=4628811

Standards (Protocols)

IKeyDecryptionOracle and IKeyDecryptionOracleCallback

For the exact interface definition see the solidity page.

ERC-7573 Secure Delivery-versus-Payment

See ERC 7573.

License

The code is distributed under the Apache License version 2.0, unless otherwise explicitly stated.