Skip to content

Cryptographic functions

Cryptographic functions are defined in crypto.codon. They can be imported like the following code:

from chain.crypto import sha256

Or just import the crypto module:

from chain import crypto

Then, call internal functions such as crypto.sha256 using the appropriate format.

sha256

Hash function using the sha256 algorithm:

def sha256(data: bytes) -> Checksum256:

Used for checking if the hash256 value is correct. If not, an exception will be raised:

def assert_sha256(data: bytes, hash: Checksum256):

sha1

Hash function using the sha1 algorithm:

def sha1(data: bytes) -> Checksum160:

Used for checking if the sha1 hash value is correct. If not, an exception will be raised:

def assert_sha1(data: bytes, hash: Checksum160):

sha512

Hash function using the sha512 algorithm:

def sha512(data: bytes) -> Checksum512:

Used for checking if the hash512 value is correct. If not, an exception will be raised:

def assert_sha512(data: bytes, hash: Checksum512):

ripemd160

Hash function using the ripemd160 algorithm:

def ripemd160(data: bytes) -> Checksum160:

Used for checking if the ripemd160 algorithm hash value is correct. If not, an exception will be raised:

def assert_ripemd160(data: bytes, hash: Checksum160):

recover_key

Used to recover the public key from digest and signature:

def recover_key(digest: Checksum256, sig: Signature) -> PublicKey:

Checks if the signature is correct. If not, an exception will be raised:

def assert_recover_key(digest: Checksum256, sig: Signature, pub: PublicKey):

Example:

# crypto_example.codon
from chain.contract import Contract
from chain.crypto import sha256, assert_sha256, sha512, assert_sha512, sha1, assert_sha1, ripemd160, assert_ripemd160
from chain.crypto import recover_key, assert_recover_key
from chain.crypto import Signature, Checksum256, PublicKey

@contract(main=True)
class MyContract(Contract):

    def __init__(self):
        super().__init__()

    @action('testcrypto')
    def test_crypto(self):
        assert_sha256(b"hello", sha256(b"hello"))
        assert_sha1(b"hello", sha1(b"hello"))
        assert_sha512(b"hello", sha512(b"hello"))
        assert_ripemd160(b"hello", ripemd160(b"hello"))

    @action('testrecover')
    def test_recover(self, msg: bytes, digest: Checksum256, sig: Signature, k1: PublicKey):
        _digest = sha256(msg)
        assert _digest == digest
        _pubkey = recover_key(digest, sig)
        assert _pubkey == k1, "_pubkey == k1"
        assert_recover_key(digest, sig, k1)
        print('done!')

Testing code:

def test_crypto():
    t = init_test('crypto_example')
    args = {}
    ret = t.push_action('hello', 'testcrypto', args, {'hello': 'active'})
    t.produce_block()
    logger.info("++++++++++%s\n", ret['elapsed'])

def test_recover():
    t = init_test('crypto_example')

    msg = b'hello,world'
    # key pair
    public_key = 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV'
    private_key = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3'

    h = hashlib.sha256()
    h.update(msg)
    digest = h.hexdigest()
    logger.info('++++digest: %s', digest)

    #sign with private key
    sig = eosapi.sign_digest(digest, private_key)
    logger.info('++++signature: %s', sig)
    args = {
        "msg": msg.hex(),
        "digest": digest,
        "sig": sig,
        "k1": public_key,
    }
    ret = t.push_action('hello', 'testrecover', args, {'hello': 'active'})
    t.produce_block()
    logger.info("++++++++++%s\n", ret['elapsed'])

Compilation:

python-contract build crypto_example.codon

Testing:

ipyeos -m pytest -s -x test.py -k test_crypto
ipyeos -m pytest -s -x test.py -k test_recover

In this example code, the usage of commonly used hash functions as well as the usage of recover_key and assert_recover_key are demonstrated separately. The usage of hash functions is relatively simple; here is an explanation of the test code for recover_key: recover_key takes two parameters, namely digest and signature. The digest is the result of running the sha256 algorithm on a binary data. In the above code, the hash calculation was performed on hello,world using the sha256 algorithm.

h = hashlib.sha256()
h.update(b'hello,world')
digest = h.hexdigest()

The computed result is passed as a parameter to the action.

Here is an explanation of testrecover:

@action('testrecover')
def test_recover(self, msg: bytes, digest: Checksum256, sig: Signature, k1: PublicKey):
    _digest = sha256(msg)
    assert _digest == digest #判断digest是否对msg进行hash256算法的hash结果
    _pubkey = recover_key(digest, sig)
    assert _pubkey == k1, "_pubkey == k1" #判断public key是否正确

    assert_recover_key(digest, sig, k1) #作用相当于上面两行代码
    print('done!')

In the sent transaction, the user's signature on the transaction is also required to indicate that the user has authorized the transaction. Then, in the smart contract, the require_auth function can be called to determine whether the transaction has been authorized by a specific user.

In actual smart contract applications, the above method can also be used to determine whether a certain section of binary data in the smart contract is signed using a specific private key. The process is as follows:

  • The contract stores the user's public key.
  • the user signs the data with their private key.
  • The user sends the data, along with the corresponding signature, to the smart contract.
  • The smart contract can call RecoverKey to reconstruct the public key from the user's data and the signature on the data.
  • The smart contract reads the user's public key stored on the chain and compares it with the public key reconstructed by calling RecoverKey. If they are the same, it can be confirmed that the data is signed by the corresponding user.

Comments