All cryptographic operations currently use the secp256k1 elliptic curve, matching Ethereum and XRP Ledger.
- Curve order N:
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - Private keys: 256-bit integers in [1, N-1]
- Public keys: curve points (X, Y), each 32 bytes, uncompressed format (64 bytes total)
Used for EVM-compatible wallets and TEE identity signing.
- Hash message with Keccak256 (32 bytes)
- Sign hash with secp256k1 ECDSA
- Output:
[R || S || V]= 65 bytes (R: 32, S: 32, V: 1)
Recovery: crypto.SigToPub(hash, signature) recovers the signer's public key.
Used for XRP Ledger wallets.
- Hash message with SHA512, take first 32 bytes (SHA512-Half)
- Sign hash with secp256k1 ECDSA
- Output:
[R || S || V]= 65 bytes
Used for generating provable randomness. See VRF documentation for full details.
- Hash nonce to curve point:
h = HashToCurve(nonce) - Compute gamma:
gamma = sk * h - Generate random k, compute witness points
- Compute challenge:
c = HashToZn(Pack(G, h, pk, gamma, u, v)) - Compute response:
s = k - sk*c (mod N) - Output:
Proof{Gamma, C, S, U, CGamma, V, ZInv}(~939 bytes JSON)
Verification checks four equations involving the witness points and can be performed on-chain.
Elliptic Curve Integrated Encryption Scheme, used for encrypting key splits during backup and for encrypting a full wallet private key during direct TEE-to-TEE backup.
- Parameters:
ECIES_AES128_SHA256with secp256k1 - Overhead: 113 bytes per encryption (ephemeral public key + MAC)
- Encryption:
utils.Encrypt(plaintext, recipientPubKey)(wrapsecies.Encrypt(rand, ..., nil, nil)) - Decryption:
utils.Decrypt(ciphertext, privKey)(wrapseciesPrivKey.Decrypt(ciphertext, nil, nil))
The utils.Encrypt / utils.Decrypt helpers in pkg/utils/crypto.go convert the secp256k1 ECDSA key to its ECIES form (rejecting non-S256 curves) before delegating to go-ethereum's ecies. Decryption is supported for XRP and EVM wallet types only. VRF wallets do not support decryption.
Every signature the TEE produces is taken over a canonical domain-separated preimage rather than a bare hash, so a signature is bound to both its payload type and the target network and cannot be replayed in another context. (The only exceptions are the external bit-exact formats — XRP Ledger transaction signatures and VRF proofs — which must not be wrapped.) The construction matches the on-chain SignedPayload library:
signing.Payload{prefix, chainID, dataHash}.Hash() = keccak256(abi.encode(
bytes32 prefix, // domain-separation tag for the payload type
uint256 chainID, // binds the signature to one EVM network
bytes32 dataHash // keccak256(abi.encode(payload fields))
))
The TEE then signs this value with the standard eth-signed-message wrap (accounts.TextHash); on-chain verifiers recover against the matching SignedPayload.ethSignedHash. signing.Payload is provided by go-flare-common's pkg/signing; chainID comes from the node's configured CHAIN_ID and is required (signing fails if it is unset).
Domain tag (bytes32(string)) |
Payload | dataHash = keccak256(abi.encode(...)) of |
|---|---|---|
TEE_MACHINE_REGISTER |
Machine registration / attestation | TeeMachineData (extension ID, owner, code hash, platform, public key, governance hash) |
TEE_KEY_EXISTENCE |
Key existence proof | the KeyExistence struct (TEE ID, wallet/key ID, public key, nonce, config) |
TEE_MACHINE_PATH_LIST |
Governance machine-path list | (extensionID, nonce, paths) |
FDC2 |
FDC2 attestation proof | (headerHash, requestBodyHash, responseBodyHash) — see FDC Proving |
TEE_KEY_DIRECT_BACKUP |
Direct (TEE-to-TEE) backup envelope | keccak256(payload) — see Backup & Restore |
TEE_WALLET_BACKUP |
TEE-identity signature over a data-provider wallet backup | WalletBackup.HashForSigning() |
PMW_WALLET_BACKUP |
Wallet-key signature over a data-provider wallet backup | WalletBackup.HashForSigning() |
PMW_KEY_SPLIT |
Wallet-key signature over a single encrypted key split | KeySplitData.HashForSigning() |
TEE_VOTE_HASH |
End-phase vote hash / rewarding data | voteHash — see Action Processing |
TEE_ACTION_RESULT |
Action result posted to the proxy | ActionResult.Hash() |
The governance signer set itself is committed as GovernanceHash = keccak256(abi.encode(address[] signers, uint256 threshold)), included in the registered TeeMachineData.
The TEE_WALLET_BACKUP and PMW_WALLET_BACKUP tags share the same dataHash but differ so a TEE-identity signature and a wallet-key signature over identical backup content stay non-interchangeable.
Splits a secret into shares such that any threshold shares can reconstruct the secret, but fewer reveal nothing.
- Construct random polynomial of degree
threshold - 1with the secret as constant term - Evaluate polynomial at points
x = 1, 2, ..., numShares - Each share is a point
(x, y)on the polynomial
All arithmetic is modulo the secp256k1 curve order N.
Given threshold shares, reconstruct the secret (y-intercept) using Lagrange interpolation:
secret = sum_i ( y_i * product_j!=i ( x_j / (x_j - x_i) ) ) mod N
Duplicate X values (same share submitted twice) are detected and rejected.
The wallet private key is split into n additive shares:
shares[0], shares[1], ..., shares[n-2] = random
shares[n-1] = privateKey - sum(shares[0..n-2]) mod N
Reconstruction: privateKey = sum(all shares) mod N
In backup, n = 2: one share for admins, one for providers. Each share is further split via Shamir sharing.
All three backup signatures use the domain-separated signing.Payload{tag, chainID, innerHash}.Hash() construction described above (the legacy "\x19Flare PMW backup:\n32" prefix has been removed).
Each KeySplitData is JSON-marshaled and Keccak256-hashed, then the wallet's private key signs signing.Payload{PMW_KEY_SPLIT, chainID, hash}.Hash(). Signing with the wallet key (not the recipient's) means recipients cannot forge modified splits.
The WalletBackup content (metadata + both encrypted share sets) is JSON-marshaled and hashed, then the wallet key signs signing.Payload{PMW_WALLET_BACKUP, chainID, hash}.Hash().
The same backup content hash is signed by the TEE's identity key over signing.Payload{TEE_WALLET_BACKUP, chainID, hash}.Hash().
| Function | Usage |
|---|---|
| Keccak256 | EVM signing, action result signing, backup hashing, vote hashes |
| SHA512-Half | XRP transaction signing |
| HashToZn | VRF challenge generation (Keccak256 mod N) |
| HashToCurve | VRF nonce-to-point mapping (iterative Keccak256) |