Skip to content

secp256k1 - [mainnet]

This module implements ECDSA signatures based on the prime-order secp256k1 ellptic curve (i.e., cofactor is 1).

use 0x1::error;
use 0x1::option;

Constants

The size of a secp256k1-based ECDSA signature, in bytes.

const SIGNATURE_NUM_BYTES: u64 = 64;

An error occurred while deserializing, for example due to wrong input size.

const E_DESERIALIZE: u64 = 1;

The size of a secp256k1-based ECDSA public key, in bytes.

const RAW_PUBLIC_KEY_NUM_BYTES: u64 = 64;

Recovery ID needs to be either 0, 1, 2 or 3. If you are recovering from an (r, s, v) Ethereum signature, take its v value and, set the recovery_id as follows: if v == 27, set to 0, if v == 28, set to 1, if v == 37, set to 0, if v == 38, set to 1.

const E_BAD_RECOVERY_ID: u64 = 2;

Structs

ECDSARawPublicKey

A 64-byte ECDSA public key.

struct ECDSARawPublicKey has copy, drop, store
Fields
bytes: vector<u8>

ECDSASignature

A 64-byte ECDSA signature.

struct ECDSASignature has copy, drop, store
Fields
bytes: vector<u8>

Functions

ecdsa_signature_from_bytes

Constructs an ECDSASignature struct from the given 64 bytes.

public fun ecdsa_signature_from_bytes(bytes: vector<u8>): secp256k1::ECDSASignature
Implementation
public fun ecdsa_signature_from_bytes(bytes: vector<u8>): ECDSASignature {
assert!(bytes.length() == SIGNATURE_NUM_BYTES, std::error::invalid_argument(E_DESERIALIZE));
ECDSASignature { bytes }
}

ecdsa_raw_public_key_from_64_bytes

Constructs an ECDSARawPublicKey struct, given a 64-byte raw representation.

public fun ecdsa_raw_public_key_from_64_bytes(bytes: vector<u8>): secp256k1::ECDSARawPublicKey
Implementation
public fun ecdsa_raw_public_key_from_64_bytes(bytes: vector<u8>): ECDSARawPublicKey {
assert!(bytes.length() == RAW_PUBLIC_KEY_NUM_BYTES, std::error::invalid_argument(E_DESERIALIZE));
ECDSARawPublicKey { bytes }
}

ecdsa_raw_public_key_to_bytes

Serializes an ECDSARawPublicKey struct to 64-bytes.

public fun ecdsa_raw_public_key_to_bytes(pk: &secp256k1::ECDSARawPublicKey): vector<u8>
Implementation
public fun ecdsa_raw_public_key_to_bytes(pk: &ECDSARawPublicKey): vector<u8> {
pk.bytes
}

ecdsa_signature_to_bytes

Serializes an ECDSASignature struct to 64-bytes.

public fun ecdsa_signature_to_bytes(sig: &secp256k1::ECDSASignature): vector<u8>
Implementation
public fun ecdsa_signature_to_bytes(sig: &ECDSASignature): vector<u8> {
sig.bytes
}

ecdsa_recover

Recovers the signer’s raw (64-byte) public key from a secp256k1 ECDSA signature given the (2-bit) recovery_id and the signed message (32 byte digest).

This recovery algorithm can only be used to check validity of a signature if the signer’s public key (or its hash) is known beforehand. When the algorithm returns a public key pk, this means that the signature in signature verified on message under that pk. But, again, that is only meaningful if pk is the “right” one (e.g., in Ethereum, the “right” pk is the one whose hash matches the account’s address).

If you do not understand this nuance, please learn more about ECDSA and pubkey recovery (see https://alinush.github.io/ecdsa#pubkey-recovery), or you risk writing completely-insecure code.

Note: This function does not apply any additional hashing on the message; it simply passes in the message as raw bytes to the ECDSA recovery function. (The max allowed size ~32 bytes.)

  • Nonetheless, most applications will first hash the message to be signed. So, typically, message here tends to be a hash rather than an actual message. Therefore, the developer should be aware of what hash function was used for this.
  • In particular, if using this function to verify an Ethereum signature, you will likely have to input a keccak256 hash of the message as the message parameter.
public fun ecdsa_recover(message: vector<u8>, recovery_id: u8, signature: &secp256k1::ECDSASignature): option::Option<secp256k1::ECDSARawPublicKey>
Implementation
public fun ecdsa_recover(
message: vector<u8>,
recovery_id: u8,
signature: &ECDSASignature,
): Option<ECDSARawPublicKey> {
// If recovery ID is not 0 or 1 or 2 or 3, help the caller out by aborting with `E_BAD_RECOVERY_ID`
if(recovery_id != 0 && recovery_id != 1 && recovery_id != 2 && recovery_id != 3) {
abort std::error::invalid_argument(E_BAD_RECOVERY_ID);
};
let (pk, success) = ecdsa_recover_internal(message, recovery_id, signature.bytes);
if (success) {
std::option::some(ecdsa_raw_public_key_from_64_bytes(pk))
} else {
std::option::none<ECDSARawPublicKey>()
}
}

ecdsa_recover_internal

Returns (public_key, true) if signature verifies on message under the recovered public_key and returns ([], false) otherwise.

fun ecdsa_recover_internal(message: vector<u8>, recovery_id: u8, signature: vector<u8>): (vector<u8>, bool)
Implementation
native fun ecdsa_recover_internal(
message: vector<u8>,
recovery_id: u8,
signature: vector<u8>
): (vector<u8>, bool);

Specification

ecdsa_signature_from_bytes

public fun ecdsa_signature_from_bytes(bytes: vector<u8>): secp256k1::ECDSASignature
aborts_if len(bytes) != SIGNATURE_NUM_BYTES;
ensures result == ECDSASignature { bytes };

ecdsa_raw_public_key_from_64_bytes

public fun ecdsa_raw_public_key_from_64_bytes(bytes: vector<u8>): secp256k1::ECDSARawPublicKey
aborts_if len(bytes) != RAW_PUBLIC_KEY_NUM_BYTES;
ensures result == ECDSARawPublicKey { bytes };

ecdsa_raw_public_key_to_bytes

public fun ecdsa_raw_public_key_to_bytes(pk: &secp256k1::ECDSARawPublicKey): vector<u8>
aborts_if false;
ensures result == pk.bytes;

ecdsa_signature_to_bytes

public fun ecdsa_signature_to_bytes(sig: &secp256k1::ECDSASignature): vector<u8>
aborts_if false;
ensures result == sig.bytes;

ecdsa_recover

public fun ecdsa_recover(message: vector<u8>, recovery_id: u8, signature: &secp256k1::ECDSASignature): option::Option<secp256k1::ECDSARawPublicKey>
aborts_if recovery_id > 3;
aborts_if ecdsa_recover_internal_abort_condition(message, recovery_id, signature.bytes);
let pk = spec_ecdsa_recover_internal_result_1(message, recovery_id, signature.bytes);
let success = spec_ecdsa_recover_internal_result_2(message, recovery_id, signature.bytes);
ensures success ==> result == std::option::spec_some(ecdsa_raw_public_key_from_64_bytes(pk));
ensures !success ==> result == std::option::spec_none<ECDSARawPublicKey>();

ecdsa_recover_internal

fun ecdsa_recover_internal(message: vector<u8>, recovery_id: u8, signature: vector<u8>): (vector<u8>, bool)
pragma opaque;
aborts_if ecdsa_recover_internal_abort_condition(message, recovery_id, signature);
ensures result_1 == spec_ecdsa_recover_internal_result_1(message, recovery_id, signature);
ensures result_2 == spec_ecdsa_recover_internal_result_2(message, recovery_id, signature);
ensures len(result_1) == if (result_2) { RAW_PUBLIC_KEY_NUM_BYTES } else { 0 };
fun ecdsa_recover_internal_abort_condition(message: vector<u8>, recovery_id: u8, signature: vector<u8>): bool;
fun spec_ecdsa_recover_internal_result_1(message: vector<u8>, recovery_id: u8, signature: vector<u8>): vector<u8>;
fun spec_ecdsa_recover_internal_result_2(message: vector<u8>, recovery_id: u8, signature: vector<u8>): bool;