{-# 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