@oxc-wallet/protocols
v1.2.0
Published
Note: ⚠️ Protocol derived keypairs differ.
Readme
Protocols
Note: ⚠️ Protocol derived keypairs differ.
Loophole Protocol
Key Derivation
- Issuer sends
identifierto backend - Backend signs a provenance chain
- Backend signature hash is returned to issuer
- Issuer signs a provenance chain
- Issuer signature hash is used as
identifier'sprivate key
Optional: To protect user identity, Issuer could hash internal user identifier with adjacent salt.
protocol = 'loophole'
h1 = userEmail OR keccak256(concat(userEmail, userSalt))
h2 = keccak256(sign(concat(protocol, backendAddress, issuerAddress, h1), backendSK))
sk = keccak256(sign(concat(protocol, backendAddress, issuerAddress, h2), issuerSK))Usage
import { Peer } from "@oxc-wallet/protocols";
import { createWalletClient, http } from "viem";
const issuer = new Peer("0x....");
const remote = await Peer.remote("https://wallet.oxc.sh", "loophole");
const account = await issuer.derive("[email protected]", remote);
const wallet = createWalletClient({
account,
chain,
transport: http(),
});
const signature = await wallet.signMessage({ message: "Hello World" });Oxford Protocol
Key Derivation
- Issuer sends
identifierto backend - Backend signs a provenance chain
- Backend signature hash is used as
identifier'sprivate key - Identifier address is returned to issuer
- Issuer creates a JSON-RPC account with signing delegated to backend
protocol = 'oxford'
id = userEmail
sk = keccak256(sign(concat(protocol, backendAddress, issuerAddress, id), backendSK))Usage
import { Peer, IOXCAccount } from "@oxc-wallet/protocols";
const issuer = new Peer("0x....");
const remote = await Peer.remote("https://wallet.oxc.sh", "oxford");
const account = await issuer.derive("[email protected]", remote) as IOXCAccount;
console.log(account.address); // 0x...Encryption / Decryption
Encrypt and decrypt data using the derived account's encryption keys:
// Encrypt a message
const encrypted = await account.encrypt("secret message");
console.log(encrypted); // 0x...
// Decrypt the message
const decrypted = await account.decrypt(encrypted);
console.log(decrypted); // "secret message"Message Signing
Sign messages using the derived account:
// Sign a string message (EIP-191 personal sign)
const signature = await account.signMessage("Hello World");
// Sign raw bytes (hex-encoded)
const rawSignature = await account.signRawMessage("0xdeadbeef");Signatures can be verified using viem:
import { verifyMessage } from "viem";
const isValid = await verifyMessage({
address: account.address,
message: "Hello World",
signature,
});Transaction Signing
Sign transactions using the derived account:
const signedTx = await account.signTransaction({
to: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
value: 1000000000000000000n, // 1 ETH
chainId: 1,
nonce: 0,
gas: 21000n,
maxFeePerGas: 20000000000n,
maxPriorityFeePerGas: 1000000000n,
});Direct Peer Methods
All account methods are also available directly on the Peer class:
const issuer = new Peer("0x....");
const remote = await Peer.remote("https://wallet.oxc.sh", "oxford");
const identifier = "[email protected]";
// Encrypt/decrypt
const encrypted = await issuer.encrypt(identifier, "message", remote);
const decrypted = await issuer.decrypt(identifier, encrypted, remote);
// Sign message
const signature = await issuer.signMessage(identifier, "Hello", remote);
// Sign raw message
const rawSig = await issuer.signRawMessage(identifier, "0xdeadbeef", remote);
// Sign transaction
const signedTx = await issuer.signTransaction(identifier, transaction, remote);API Reference
Peer Class
Constructor
new Peer(secret: Hex)Creates a new Peer instance with the given private key.
Properties
details- Returns{ peer: Hex, address: Hex, pubkey: Hex }
Static Methods
Peer.remote(url: string, protocol: "oxford" | "loophole")- Creates a remote peer stub
Instance Methods
| Method | Description | Protocol Support |
|--------|-------------|------------------|
| derive(identifier, stub) | Derive an account for the identifier | oxford, loophole |
| encrypt(identifier, message, stub) | Encrypt a message | oxford |
| decrypt(identifier, ciphertext, stub) | Decrypt a message | oxford |
| signMessage(identifier, message, stub) | Sign a string message | oxford |
| signRawMessage(identifier, hex, stub) | Sign raw hex data | oxford |
| signTransaction(identifier, tx, stub) | Sign a transaction | oxford |
| handle(parcel, op) | Handle incoming requests (server-side) | oxford, loophole |
IOXCAccount Type
The account returned by derive() with the oxford protocol:
| Method | Description |
|--------|-------------|
| encrypt(message: string) | Encrypt a message |
| decrypt(ciphertext: Hex) | Decrypt a message |
| signMessage(message: string) | Sign a string message |
| signRawMessage(message: Hex) | Sign raw hex data |
| signTransaction(tx: TransactionSerializable) | Sign a transaction |
Transport Layer
- Parcels are
x25519-xsalsa20-poly1305public key encrypted boxes. - Sender is verified by checking
secp256k1signature. - Parcels get stale after 10 seconds (timestamp included in payload).
License
Unlicensed
