Cryptography

Cryptography in Sui:

Cross-chain Signature Verification

Dec 13, 2022

Joy Wang

2 min read

Move supports derivation of a public key from a signature, making it unnecessary to send a public key with the signature and message digest. This capability comes from the Elliptic Curve Digital Signature Algorithm (ECDSA), one of the cryptographic primitives supported in Move for signature verification.

In the Ed25519 signature scheme, signature verification requires a user to submit the signature itself, the message digest, and the public key. However, ECDSA offers a unique feature where the public key itself does not need to be submitted, but can be derived directly from the signature.

Deriving the public key, as opposed to sending it, preserves bandwidth, which may be desirable on high volume or low bandwidth networks. For instance, Ethereum follows that approach to save a few bytes from transaction's size. Additionally, in a cross-blockchain setting, bridging smart contracts on one chain needs to verify transaction signatures submitted to a different chain. Submitting less data better preserves both inter-chain compatibility and succinctness. Employing public key derivation in this manner results in sending less data, which in turn results in lower gas cost.

Public Key Recovery

Move supports several cryptographic primitives. Here we demonstrate one of the most important features for ECDSA: how to recover a public key from a Secp256k1 signature on-chain.

Recovering a public key involves deriving it from a signature through a process that’s fairly easy to implement. This process makes use of Move’s ecrecover function.

In the following example, we show how to verify an Ethereum signature in Move using public key recovery.

  1. Normalize an Ethereum-style signature such that the recovery ID is 0 or 1. This initial step sets use of the true recovery ID instead of the one calculated by Etheruem’s idiomatic chain ID, based on EIP-155. In this step, we show how to normalize an Ethereum-style signature on-chain. We encourage users to perform this step off-chain when it makes sense.

// Normalize the last byte of the signature to be 0 or 1. let v = vector::borrow_mut(&mut signature, 64); if (*v == 27) { *v = 0; } else if (*v == 28) { *v = 1; } else if (*v > 35) { *v = (*v - 1) % 2; };

 

  1. Call Move’s ecrecover function with the normalized signature and the hash of the signature’s message. The result of this function is a compressed ECDSA public key.

let pubkey = ecdsa::ecrecover(&signature, &hashed_msg);

 

  1. Calculate the Ethereum address by decompressing the public key, hash it using Keccak-256, and take its last 20 bytes.

let uncompressed = ecdsa::decompress_pubkey(&pubkey); // Take the last 64 bytes of the uncompressed pubkey. let uncompressed_64 = vector::empty<u8>(); let i = 1; while (i < 65) { let value = vector::borrow(&uncompressed, i); vector::push_back(&mut uncompressed_64, *value); i = i + 1; }; // Take the last 20 bytes of the hash of the 64-bytes uncompressed pubkey. let hashed = ecdsa::keccak256(&uncompressed_64); let addr = vector::empty<u8>(); let i = 12; while (i < 32) { let value = vector::borrow(&hashed, i); vector::push_back(&mut addr, *value); i = i + 1; };

Now you can validate the signature by checking whether the derived address is indeed the sender!

You can find the full example on Github.

Flexible Cryptographic Primitives

To unlock the full potential of building cryptography-sensitive smart contracts with Move, we want to provide the greatest range of tools for developers. By offering the ECDSA public key recovery primitive, we are empowering developers to build cross-chain applications securely and with ease. At the same time, we allow for backwards compatibility with existing wallet and light client implementations that are already familiar with the ecrecover functionality.

Move supports derivation of a public key from a signature, making it unnecessary to send a public key with the signature and message digest. This capability comes from the Elliptic Curve Digital Signature Algorithm (ECDSA), one of the cryptographic primitives supported in Move for signature verification.

In the Ed25519 signature scheme, signature verification requires a user to submit the signature itself, the message digest, and the public key. However, ECDSA offers a unique feature where the public key itself does not need to be submitted, but can be derived directly from the signature.

Deriving the public key, as opposed to sending it, preserves bandwidth, which may be desirable on high volume or low bandwidth networks. For instance, Ethereum follows that approach to save a few bytes from transaction's size. Additionally, in a cross-blockchain setting, bridging smart contracts on one chain needs to verify transaction signatures submitted to a different chain. Submitting less data better preserves both inter-chain compatibility and succinctness. Employing public key derivation in this manner results in sending less data, which in turn results in lower gas cost.

Public Key Recovery

Move supports several cryptographic primitives. Here we demonstrate one of the most important features for ECDSA: how to recover a public key from a Secp256k1 signature on-chain.

Recovering a public key involves deriving it from a signature through a process that’s fairly easy to implement. This process makes use of Move’s ecrecover function.

In the following example, we show how to verify an Ethereum signature in Move using public key recovery.

  1. Normalize an Ethereum-style signature such that the recovery ID is 0 or 1. This initial step sets use of the true recovery ID instead of the one calculated by Etheruem’s idiomatic chain ID, based on EIP-155. In this step, we show how to normalize an Ethereum-style signature on-chain. We encourage users to perform this step off-chain when it makes sense.

// Normalize the last byte of the signature to be 0 or 1. let v = vector::borrow_mut(&mut signature, 64); if (*v == 27) { *v = 0; } else if (*v == 28) { *v = 1; } else if (*v > 35) { *v = (*v - 1) % 2; };

 

  1. Call Move’s ecrecover function with the normalized signature and the hash of the signature’s message. The result of this function is a compressed ECDSA public key.

let pubkey = ecdsa::ecrecover(&signature, &hashed_msg);

 

  1. Calculate the Ethereum address by decompressing the public key, hash it using Keccak-256, and take its last 20 bytes.

let uncompressed = ecdsa::decompress_pubkey(&pubkey); // Take the last 64 bytes of the uncompressed pubkey. let uncompressed_64 = vector::empty<u8>(); let i = 1; while (i < 65) { let value = vector::borrow(&uncompressed, i); vector::push_back(&mut uncompressed_64, *value); i = i + 1; }; // Take the last 20 bytes of the hash of the 64-bytes uncompressed pubkey. let hashed = ecdsa::keccak256(&uncompressed_64); let addr = vector::empty<u8>(); let i = 12; while (i < 32) { let value = vector::borrow(&hashed, i); vector::push_back(&mut addr, *value); i = i + 1; };

Now you can validate the signature by checking whether the derived address is indeed the sender!

You can find the full example on Github.

Flexible Cryptographic Primitives

To unlock the full potential of building cryptography-sensitive smart contracts with Move, we want to provide the greatest range of tools for developers. By offering the ECDSA public key recovery primitive, we are empowering developers to build cross-chain applications securely and with ease. At the same time, we allow for backwards compatibility with existing wallet and light client implementations that are already familiar with the ecrecover functionality.

Read Next