Cryptographic functions

Brief overview of cryptographic functions in Clarity and their importance in smart contract development.

Cryptographic functions are essential in blockchain smart contracts for ensuring data integrity, creating secure hashes, and verifying signatures. Clarity provides several built-in cryptographic functions that are crucial for these purposes.

Why these functions matter

Clarity's cryptographic functions are designed with blockchain-specific considerations in mind:

  1. Security: These functions provide cryptographic primitives essential for building secure smart contracts.
  2. Data Integrity: They allow for the creation and verification of unique identifiers for data, ensuring data hasn't been tampered with.
  3. Authentication: Cryptographic functions enable signature verification, crucial for authenticating transactions and messages.
  4. Determinism: The behavior of these functions is consistent across all nodes, ensuring blockchain consensus.
  5. Interoperability: Some functions (like keccak256) provide compatibility with other blockchain systems.

Core Cryptographic Functions

1. sha256

What: Computes the SHA256 hash of the input. Why: SHA256 is widely used for creating unique, fixed-size representations of data, crucial for data integrity checks. When: Use when you need to create a unique identifier for data or verify data integrity. How:

(sha256 value)

Best Practices:

  • Use for creating unique identifiers for data
  • Combine with other data before hashing to prevent rainbow table attacks

Example Use Case: Creating a unique identifier for a document or transaction.

(define-public (submit-document-hash (document-hash (buff 32)))
  (let ((user-submission-id (sha256 (concat document-hash tx-sender))))
    (map-set document-submissions user-submission-id true)
    (ok user-submission-id)))

2. keccak256

What: Computes the Keccak256 hash of the input. Why: Keccak256 is another cryptographic hash function, often used in Ethereum-compatible systems. When: Use when interoperability with Ethereum systems is needed or when an alternative to SHA256 is required. How:

(keccak256 value)

Best Practices:

  • Use when SHA256 is not suitable for your specific use case
  • Be aware of the differences in output compared to SHA256

Example Use Case: Creating a hash for Ethereum address compatibility.

(define-read-only (get-eth-address (public-key (buff 33)))
  (let ((hashed-key (keccak256 (unwrap-panic (secp256k1-recover? message-hash signature public-key)))))
    (buff-to-hex-string (unwrap-panic (slice? hashed-key u12 u32)))))

3. secp256k1-recover?

What: Recovers the public key from a signed message and signature. Why: Essential for verifying signatures without needing the public key in advance. When: Use when implementing signature verification schemes, especially for transactions or messages. How:

(secp256k1-recover? message-hash signature)

Best Practices:

  • Always check the return value as it's an optional type
  • Use in combination with secp256k1-verify for complete signature verification

Example Use Case: Verifying a signed message in a decentralized voting system.

(define-public (submit-vote (vote uint) (signature (buff 65)) (message-hash (buff 32)))
  (let ((signer (unwrap! (secp256k1-recover? message-hash signature) (err u1))))
    (asserts! (is-eq (principal-of? signer) (some tx-sender)) (err u2))
    (map-set votes tx-sender vote)
    (ok true)))

## Practical Example: Document Verification System

Let's implement a basic document verification system that demonstrates the use of Clarity's cryptographic functions. This system will allow users to submit document hashes, sign them, and later verify the authenticity of the document and the signer.

```clarity
;; Define a map to store document hashes and their signers
(define-map documents
  { doc-hash: (buff 32) }
  { signer: principal, eth-compatible-hash: (buff 32) })

;; Function to submit a document hash
(define-public (submit-document (doc-hash (buff 32)))
  (let
    (
      (submission-id (sha256 (concat doc-hash tx-sender)))
      (eth-hash (keccak256 doc-hash))
    )
    (map-set documents { doc-hash: submission-id }
                       { signer: tx-sender, eth-compatible-hash: eth-hash })
    (ok submission-id)))

;; Function to sign a document hash
(define-public (sign-document (doc-hash (buff 32)) (signature (buff 65)))
  (let
    ((submission-id (sha256 (concat doc-hash tx-sender))))
    (match (map-get? documents { doc-hash: submission-id })
      doc-info
        (let
          ((signer (unwrap! (secp256k1-recover? doc-hash signature) (err u1))))
          (asserts! (is-eq (principal-of? signer) (some tx-sender)) (err u2))
          (ok true))
      (err u3))))

;; Function to verify a document and its signer
(define-read-only (verify-document (doc-hash (buff 32)) (signature (buff 65)))
  (let
    ((submission-id (sha256 (concat doc-hash tx-sender))))
    (match (map-get? documents { doc-hash: submission-id })
      doc-info
        (let
          ((signer (unwrap! (secp256k1-recover? doc-hash signature) (err u1))))
          (if (is-eq (some (get signer doc-info)) (principal-of? signer))
              (ok true)
              (err u2)))
      (err u3))))

;; Function to get Ethereum-compatible hash
(define-read-only (get-eth-hash (doc-hash (buff 32)))
  (match (map-get? documents { doc-hash: doc-hash })
    doc-info (ok (get eth-compatible-hash doc-info))
    (err u1)))

Conclusion

Cryptographic functions in Clarity provide powerful tools for ensuring data integrity and implementing secure signature schemes. By understanding when and how to use these functions, developers can create robust smart contracts that maintain the security and trustlessness essential to blockchain applications. Always consider the specific security requirements of your application when choosing and implementing these cryptographic functions.