CKB Crypto Service
ckb-crypto-service
is an encryption service built on CKB’s multi-process architecture and IPC (inter-process communication) mechanism. It provides a unified, secure, and easy-to-use cryptographic interface for smart contracts.
Unlike traditional approaches where cryptographic libraries are embedded directly into contracts—leading to compatibility issues, dependency conflicts, and inconsistent implementations—this service decouples cryptographic logic from contracts. It supports multiple programming languages such as Rust, C, and JavaScript, significantly improving development efficiency and security. It is the preferred infrastructure for implementing cryptographic functionality in CKB smart contracts.
Supported Algorithms
- Blake2b
- SHA-256 (SHA-2)
- RIPEMD-160
- Secp256k1
- Schnorr
- Ed25519
How It Works
ckb-crypto-service
communicates across processes using the CKB Spawn mechanism and wraps IPC interfaces via ckb-script-ipc. See related IPC documentation.
The service is started using spawn
, with a pair of file descriptors passed in. After startup, it listens for requests from the parent process using ckb_script_ipc_common::spawn::run_server
. On receiving a request, it executes the corresponding cryptographic function and sends the result back.
Rust Integration
A Rust wrapper is provided via ckb-crypto-interface
for convenient usage.
Initialization
let (read_pipe, write_pipe) = spawn_cell_server(
&args[0..32],
ckb_std::ckb_types::core::ScriptHashType::Data2,
&[CString::new("").unwrap().as_ref()],
).unwrap();
let crypto_cli = CkbCryptoClient::new(read_pipe, write_pipe);
(CkbCryptoClient
is auto-generated by ckb-script-ipc
.)
API Methods
pub trait CkbCrypto {
fn hasher_new(hash_type: HasherType) -> HasherCtx;
fn hasher_update(ctx: HasherCtx, data: Vec<u8>) -> Result<(), CryptoError>;
fn hasher_finalize(ctx: HasherCtx) -> Result<Vec<u8>, CryptoError>;
fn secp256k1_recovery(
prehash: Vec<u8>,
signature: Vec<u8>,
recovery_id: u8,
) -> Result<Vec<u8>, CryptoError>;
fn secp256k1_verify(
public_key: Vec<u8>,
prehash: Vec<u8>,
signature: Vec<u8>,
) -> Result<(), CryptoError>;
fn schnorr_verify(
public_key: Vec<u8>,
prehash: Vec<u8>,
signature: Vec<u8>,
) -> Result<(), CryptoError>;
fn ed25519_verify(
public_key: Vec<u8>,
prehash: Vec<u8>,
signature: Vec<u8>,
) -> Result<(), CryptoError>;
}
These methods can be called using the initialized crypto_cli
instance.
Hashing Example
Supported hash functions:
- CkbBlake2b
- Blake2b
- Sha256
- Ripemd160
Usage:
- Create a context using
hasher_new
- Feed data using
hasher_update
- Retrieve the result using
hasher_finalize
Example:
let ctx = crypto_cli.hasher_new(HasherType::CkbBlake2b);
crypto_cli
.hasher_update(ctx.clone(), crypto_info.witness.clone())
.expect("update ckb blake2b");
let hash = crypto_cli
.hasher_finalize(ctx)
.expect("ckb blake2b finalize");
Signature Verification
Available methods:
secp256k1_recovery
: recover public key from signaturesecp256k1_verify
: verify signature using known public key (more efficient)schnorr_verify
ed25519_verify
Notes:
prehash
must be 32 bytes (already hashed input)signature
is variable-lengthrecovery_id
is required for recovering public key insecp256k1_recovery
JavaScript Integration
JavaScript currently lacks an auto-generation tool like Rust’s ckb_script_ipc::service
, so IPC packets must be constructed and parsed manually. See TypeScript example.
Basic usage:
function runFunction(channel: Channel, payload: Object) {
let payloadHex = new bindings.TextEncoder().encode(JSON.stringify(payload));
let res = channel.call(new RequestPacket(payloadHex));
if (res.errorCode() != 0) {
throw Error(`IPC Error: ${res.errorCode()}`);
}
let resPayload = new bindings.TextDecoder().decode(res.payload());
return Object.values(JSON.parse(resPayload))[0];
}
// Example: run Blake2b hash
function ckbBlake2b(channel: Channel, data: number[]) {
let hasher_ctx = runFunction(channel, {
HasherNew: { hash_type: "CkbBlake2b" },
});
runFunction(channel, { HasherUpdate: { ctx: hasher_ctx, data: data } });
let hash = new Uint8Array(
resultOk(runFunction(channel, { HasherFinalize: { ctx: hasher_ctx } }))
);
return hash;
}
Create the IPC channel:
function startService(): Channel {
const args = HighLevel.loadScript().args;
const codeHash = args.slice(35, 35 + 32);
const [readPipe, writePipe] = spawnCellServer(
codeHash,
bindings.SCRIPT_HASH_TYPE_DATA2,
[]
);
return new Channel(readPipe, writePipe);
}
Deployment
None