packages feed

haskell-opentimestamps-0.5.5.0: src/OpenTimestamps/Op.hs

{-# LANGUAGE GADTs #-}

{- | Operations that can be applied in timestamp proofs.

This module defines all supported operations that can be used to
transform data in timestamp proofs, including cryptographic hash
functions, data transformations, and byte manipulation operations.
-}
module OpenTimestamps.Op
  ( Op (..)
  , execute
  , getOp
  , opToTag
  , putOp
  ) where

import Crypto.Hash
  ( Digest
  , Keccak_256 (Keccak_256)
  , RIPEMD160 (RIPEMD160)
  , SHA1 (SHA1)
  , SHA256 (SHA256)
  , hashWith
  )
import Data.Binary.Get (Get)
import Data.Binary.Put (Put, putWord8)
import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
import qualified Data.ByteString.Base16 as B16
import Data.Word (Word8)
import OpenTimestamps.Config as Config
import OpenTimestamps.VarInt (getVarBytes, putVarBytes)

{- | All the types of operations supported in timestamp operations.

Hash operations apply cryptographic hash functions to the input.
Transform operations modify the data format or byte order.
Append/Prepend operations concatenate additional bytes.
-}
data Op where
  Sha1 :: Op
  Sha256 :: Op
  Ripemd160 :: Op
  Keccak256 :: Op
  Hexlify :: Op
  Reverse :: Op
  Append :: BS.ByteString -> Op
  Prepend :: BS.ByteString -> Op
  deriving (Eq, Show, Ord)

-- | Execute an operation on the given data.
execute :: Op -> BS.ByteString -> BS.ByteString
execute op input = case op of
  Sha1 -> digestToBS (hashWith SHA1 input)
  Sha256 -> digestToBS (hashWith SHA256 input)
  Ripemd160 -> digestToBS (hashWith RIPEMD160 input)
  Keccak256 -> digestToBS (hashWith Keccak_256 input)
  Hexlify -> B16.encode input
  Reverse -> BA.reverse input
  Append bs -> input <> bs
  Prepend bs -> bs <> input

-- | Convert a cryptographic digest to a ByteString.
digestToBS :: Digest a -> BS.ByteString
digestToBS = BA.convert

-- | Deserialize an operation from its tag byte.
getOp :: Word8 -> Get Op
getOp tag = case tag of
  0x02 -> pure Sha1
  0x08 -> pure Sha256
  0x03 -> pure Ripemd160
  0x67 -> pure Keccak256
  0xf3 -> pure Hexlify
  0xf2 -> pure Reverse
  0xf0 -> Append <$> getVarBytes Config.opMaxVarBytesLength 0
  0xf1 -> Prepend <$> getVarBytes Config.opMaxVarBytesLength 0
  x -> fail ("BadOpTag: " ++ show x)

-- | Convert an operation to its tag byte.
opToTag :: Op -> Word8
opToTag Sha1 = 0x02
opToTag Sha256 = 0x08
opToTag Ripemd160 = 0x03
opToTag Keccak256 = 0x67
opToTag Hexlify = 0xf3
opToTag Reverse = 0xf2
opToTag (Append _) = 0xf0
opToTag (Prepend _) = 0xf1

-- | Serialize an operation to binary format.
putOp :: Op -> Put
putOp op = case op of
  Sha1 -> putWord8 0x02
  Sha256 -> putWord8 0x08
  Ripemd160 -> putWord8 0x03
  Hexlify -> putWord8 0xf3
  Keccak256 -> putWord8 0x67
  Reverse -> putWord8 0xf2
  Append bs -> putWord8 0xf0 >> putVarBytes bs
  Prepend bs -> putWord8 0xf1 >> putVarBytes bs