Skip to content

Cryptography

Various cryptographic global functions. Crypto-oriented extension functions for Cell, Builder, and Slice types are listed on their reference page: Cells, Builders and Slices.

checkSignature

fun checkSignature(hash: Int, signature: Slice, publicKey: Int): Bool;

Checks the Ed25519 signature of the 256-bit unsigned Int hash using a publicKey, represented by a 256-bit unsigned Int. The signature must contain at least 512 bits of data, but only the first 512 bits are used.

Returns true if the signature is valid, false otherwise.

Usage example:

message ExtMsg {
signature: Slice;
data: Cell;
}
contract Showcase {
// Persistent state variables
pub: Int as uint256; // public key as a 256-bit unsigned Int
// Constructor function init(), where all variables are initialized
init(pub: Int) {
self.pub = pub; // storing the public key upon contract initialization
}
// External message receiver, which accepts message ExtMsg
external(msg: ExtMsg) {
let hash: Int = beginCell().storeRef(msg.data).endCell().hash();
let check: Bool = checkSignature(hash, msg.signature, self.pub);
// ---- ------------- --------
// ↑ ↑ ↑
// | | publicKey stored in our contract
// | signature obtained from the received message
// hash calculated using the data from the received message
// ... follow-up logic ...
}
}

checkDataSignature

fun checkDataSignature(data: Slice, signature: Slice, publicKey: Int): Bool;

Checks the Ed25519 signature of the data using a publicKey, similar to checkSignature(). If the bit length of data is not divisible by 8, this function throws an error with exit code 9: Cell underflow. Verification itself is done indirectly on a SHA-256 hash of the data.

Returns true if the signature is valid, false otherwise.

Usage example:

let data: Slice = someData;
let signature: Slice = someSignature;
let publicKey: Int = 42;
let check: Bool = checkDataSignature(data, signature, publicKey);

SignedBundle

Available since Tact 1.6.6

struct SignedBundle {
/// A 512-bit Ed25519 signature of the `signedData`.
signature: Slice as bytes64;
/// The remaining non-serialized data of the enclosing struct or message struct,
/// which was used to obtain the 512-bit Ed25519 `signature`.
signedData: Slice as remaining;
}

A struct that contains a 512-bit Ed25519 signature and the data it signs.

See the usage example for the SignedBundle.verifySignature() function.

SignedBundle.verifySignature

Available since Tact 1.6.6

extends fun verifySignature(self: SignedBundle, publicKey: Int): Bool;

Extension function for the SignedBundle struct.

Checks whether self.signedData was signed by the 512-bit Ed25519 signature self.signature, using the given publicKey. Returns true if the signature is valid, false otherwise.

Usage example:

contract Example(publicKey: Int as uint256) {
external(msg: MessageWithSignedData) {
// Checks that the signature of the SignedBundle from the incoming external
// message wasn't forged and made by the owner of this self.publicKey with
// its respective private key managed elsewhere.
throwUnless(35, msg.bundle.verifySignature(self.publicKey));
// ...rest of the checks and code...
}
}
message MessageWithSignedData {
// The `bundle.signature` contains the 512-bit Ed25519 signature
// of the remaining data fields of this message struct,
// while `bundle.signedData` references those data fields.
// In this case, the fields are `walletId` and `seqno`.
bundle: SignedBundle;
// These fields are common to external messages to user wallets.
walletId: Int as int32;
seqno: Int as uint32;
}

sha256

Gas-expensive

fun sha256(data: Slice): Int;
fun sha256(data: String): Int;

Computes and returns the SHA-256 hash as a 256256-bit unsigned Int from the passed Slice or String data, which should have a number of bits divisible by 88.

In case data is a Slice, it must have no more than a single reference per cell, because only the first reference of each nested cell will be taken into account.

This function tries to resolve constant string values at compile-time whenever possible.

Attempts to specify a Slice or String with a number of bits not divisible by 88 throw an exception with exit code 9: Cell underflow.

Usage examples:

sha256(beginCell().asSlice());
sha256("Hello, world!"); // will be resolved at compile-time
sha256(someVariableElsewhere); // will try to resolve at compile-time,
// and fall back to run-time evaluation

keccak256

Gas-expensive Available since Tact 1.6.6

fun keccak256(data: Slice): Int;

Computes and returns the Ethereum-compatible Keccak-256 hash as a 256-bit unsigned Int from the passed Slice data.

The data slice should have a number of bits divisible by 8 and no more than a single reference per cell, because only the first reference of each nested cell will be taken into account.

Attempts to specify a Slice with a number of bits not divisible by 8 throw an exception with exit code 9: Cell underflow.

Usage examples:

contract Examples() {
receive(rawMsg: Slice) {
// Hash incoming message body Slice
let hash: Int = keccak256(rawMsg);
// Process data that spans multiple cells
let b: Builder = beginCell()
.storeUint(123456789, 32)
.storeRef(beginCell().storeString("Extra data in a ref").endCell());
let largeDataHash: Int = keccak256(b.asSlice());
// Match Ethereum's hash format
let ethAddress: String = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
let ethAddressHash: Int = keccak256(ethAddress.asSlice());
}
}