{-# LANGUAGE TypeApplications #-}

module Tahoe.CHK.Crypto (
    sha1,
    sha256,
    sha256d,
    storageIndexLength,
    toBytes,
    taggedHash,
    taggedHash',
    taggedPairHash,
    taggedPairHash',
    blockHash,
    blockHash',
    storageIndexHash,
    ciphertextTag,
    ciphertextSegmentHash,
    ciphertextSegmentHash',
    uriExtensionHash,
    convergenceEncryptionTag,
    convergenceEncryptionHashLazy,
    convergenceSecretLength,
) where

import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL

import Crypto.Hash (
    Digest,
    HashAlgorithm,
    hash,
    hashDigestSize,
    hashlazy,
 )
import Data.ByteArray (convert)

import Crypto.Cipher.AES (AES128)
import Crypto.Hash.Algorithms (
    SHA1,
    SHA256 (SHA256),
 )

import Tahoe.CHK.Cipher (Key)
import Tahoe.CHK.SHA256d (Digest' (..), SHA256d, toBytes)
import Tahoe.CHK.Types (Parameters (Parameters), StorageIndex)
import Tahoe.CHK.URIExtension (
    URIExtension,
    showBytes,
    uriExtensionToBytes,
 )
import Tahoe.Netstring (
    netstring,
 )

sha1 :: B.ByteString -> B.ByteString
sha1 :: ByteString -> ByteString
sha1 ByteString
xs = Digest SHA1 -> ByteString
forall a. ByteArrayAccess a => a -> ByteString
toBytes (ByteString -> Digest SHA1
forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
hash ByteString
xs :: Digest SHA1)

sha256 :: B.ByteString -> B.ByteString
sha256 :: ByteString -> ByteString
sha256 ByteString
xs = Digest SHA256 -> ByteString
forall a. ByteArrayAccess a => a -> ByteString
toBytes (ByteString -> Digest SHA256
forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
hash ByteString
xs :: Digest SHA256)

sha256d :: B.ByteString -> B.ByteString
sha256d :: ByteString -> ByteString
sha256d = Digest SHA256d -> ByteString
forall a. ByteArrayAccess a => a -> ByteString
toBytes (Digest SHA256d -> ByteString)
-> (ByteString -> Digest SHA256d) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteString -> Digest SHA256d
forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
hash :: B.ByteString -> Digest SHA256d)

taggedHash :: Int -> B.ByteString -> B.ByteString -> B.ByteString
taggedHash :: Int -> ByteString -> ByteString -> ByteString
taggedHash Int
size ByteString
tag ByteString
bytes = Int -> ByteString -> ByteString
B.take Int
size (ByteString -> ByteString)
-> (Digest' SHA256d -> ByteString) -> Digest' SHA256d -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Digest' SHA256d -> ByteString
forall a. ByteArrayAccess a => a -> ByteString
toBytes (Digest' SHA256d -> ByteString) -> Digest' SHA256d -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString -> Digest' SHA256d
forall hash.
HashAlgorithm hash =>
ByteString -> ByteString -> Digest' hash
taggedHash' @SHA256d ByteString
tag ByteString
bytes

{- | Compute the "tagged hash" of a byte string: the hash of the concatenation
 of the netstring encoding of a tag and the given bytes.
-}
taggedHash' :: HashAlgorithm hash => B.ByteString -> B.ByteString -> Digest' hash
taggedHash' :: ByteString -> ByteString -> Digest' hash
taggedHash' ByteString
tag ByteString
bytes = Digest hash -> Digest' hash
forall a. Digest a -> Digest' a
Digest' (Digest hash -> Digest' hash)
-> ([ByteString] -> Digest hash) -> [ByteString] -> Digest' hash
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Digest hash
forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
hash (ByteString -> Digest hash)
-> ([ByteString] -> ByteString) -> [ByteString] -> Digest hash
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> ByteString
B.concat ([ByteString] -> Digest' hash) -> [ByteString] -> Digest' hash
forall a b. (a -> b) -> a -> b
$ [ByteString -> ByteString
netstring ByteString
tag, ByteString
bytes]

taggedPairHash :: Int -> B.ByteString -> B.ByteString -> B.ByteString -> B.ByteString
taggedPairHash :: Int -> ByteString -> ByteString -> ByteString -> ByteString
taggedPairHash Int
size ByteString
tag ByteString
left ByteString
right = Int -> ByteString -> ByteString
B.take Int
size (ByteString -> ByteString)
-> (Digest' SHA256d -> ByteString) -> Digest' SHA256d -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Digest' SHA256d -> ByteString
forall a. ByteArrayAccess a => a -> ByteString
toBytes (Digest' SHA256d -> ByteString) -> Digest' SHA256d -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString -> ByteString -> Digest' SHA256d
forall hash.
HashAlgorithm hash =>
ByteString -> ByteString -> ByteString -> Digest' hash
taggedPairHash' @SHA256d ByteString
tag ByteString
left ByteString
right

{- | Compute the "tagged pair hash" of two byte strings: the hash of the
 concatenation of the netstring encoding of a tag and each of two other byte
 strings.
-}
taggedPairHash' :: HashAlgorithm hash => B.ByteString -> B.ByteString -> B.ByteString -> Digest' hash
taggedPairHash' :: ByteString -> ByteString -> ByteString -> Digest' hash
taggedPairHash' ByteString
tag ByteString
left ByteString
right = Digest hash -> Digest' hash
forall a. Digest a -> Digest' a
Digest' (Digest hash -> Digest' hash)
-> ([ByteString] -> Digest hash) -> [ByteString] -> Digest' hash
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Digest hash
forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
hash (ByteString -> Digest hash)
-> ([ByteString] -> ByteString) -> [ByteString] -> Digest hash
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> ByteString
B.concat ([ByteString] -> Digest' hash) -> [ByteString] -> Digest' hash
forall a b. (a -> b) -> a -> b
$ [ByteString -> ByteString
netstring ByteString
tag, ByteString -> ByteString
netstring ByteString
left, ByteString -> ByteString
netstring ByteString
right]

blockTag :: B.ByteString
blockTag :: ByteString
blockTag = ByteString
"allmydata_encoded_subshare_v1"

-- allmydata.util.hashutil.block_hash
blockHash :: B.ByteString -> B.ByteString
blockHash :: ByteString -> ByteString
blockHash = Int -> ByteString -> ByteString -> ByteString
taggedHash (SHA256 -> Int
forall a. HashAlgorithm a => a -> Int
hashDigestSize SHA256
SHA256) ByteString
blockTag

{- | Compute the hash of a share block.  This is the same function as
 allmydata.util.hashutil.block_hash.
-}
blockHash' :: HashAlgorithm hash => B.ByteString -> Digest' hash
blockHash' :: ByteString -> Digest' hash
blockHash' = ByteString -> ByteString -> Digest' hash
forall hash.
HashAlgorithm hash =>
ByteString -> ByteString -> Digest' hash
taggedHash' ByteString
blockTag

storageIndexTag :: B.ByteString
storageIndexTag :: ByteString
storageIndexTag = ByteString
"allmydata_immutable_key_to_storage_index_v1"

-- Compute the storage index for a given encryption key
-- allmydata.util.hashutil.storage_index_hash
storageIndexHash :: Key AES128 -> StorageIndex
storageIndexHash :: Key AES128 -> ByteString
storageIndexHash = Int -> ByteString -> ByteString -> ByteString
taggedHash Int
storageIndexLength ByteString
storageIndexTag (ByteString -> ByteString)
-> (Key AES128 -> ByteString) -> Key AES128 -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Key AES128 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
convert

ciphertextTag :: B.ByteString
ciphertextTag :: ByteString
ciphertextTag = ByteString
"allmydata_crypttext_v1"

ciphertextSegmentTag :: B.ByteString
ciphertextSegmentTag :: ByteString
ciphertextSegmentTag = ByteString
"allmydata_crypttext_segment_v1"

ciphertextSegmentHash :: B.ByteString -> B.ByteString
ciphertextSegmentHash :: ByteString -> ByteString
ciphertextSegmentHash = Digest' SHA256d -> ByteString
forall a. ByteArrayAccess a => a -> ByteString
toBytes (Digest' SHA256d -> ByteString)
-> (ByteString -> Digest' SHA256d) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashAlgorithm SHA256d => ByteString -> Digest' SHA256d
forall hash. HashAlgorithm hash => ByteString -> Digest' hash
ciphertextSegmentHash' @SHA256d

-- | Compute the hash of a segment of ciphertext.
ciphertextSegmentHash' :: HashAlgorithm hash => B.ByteString -> Digest' hash
ciphertextSegmentHash' :: ByteString -> Digest' hash
ciphertextSegmentHash' = ByteString -> ByteString -> Digest' hash
forall hash.
HashAlgorithm hash =>
ByteString -> ByteString -> Digest' hash
taggedHash' ByteString
ciphertextSegmentTag

uriExtensionTag :: B.ByteString
uriExtensionTag :: ByteString
uriExtensionTag = ByteString
"allmydata_uri_extension_v1"

uriExtensionHash :: URIExtension -> B.ByteString
uriExtensionHash :: URIExtension -> ByteString
uriExtensionHash = Int -> ByteString -> ByteString -> ByteString
taggedHash (SHA256 -> Int
forall a. HashAlgorithm a => a -> Int
hashDigestSize SHA256
SHA256) ByteString
uriExtensionTag (ByteString -> ByteString)
-> (URIExtension -> ByteString) -> URIExtension -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. URIExtension -> ByteString
uriExtensionToBytes

convergenceEncryptionTagPrefix :: B.ByteString
convergenceEncryptionTagPrefix :: ByteString
convergenceEncryptionTagPrefix = ByteString
"allmydata_immutable_content_to_key_with_added_secret_v1+"

convergenceEncryptionTag :: B.ByteString -> Parameters -> B.ByteString
convergenceEncryptionTag :: ByteString -> Parameters -> ByteString
convergenceEncryptionTag ByteString
secret (Parameters SegmentSize
segmentSize Total
total Int
_ Total
required) =
    ByteString
tag
  where
    tag :: ByteString
tag = [ByteString] -> ByteString
B.concat [ByteString
convergenceEncryptionTagPrefix, ByteString -> ByteString
netstring ByteString
secret, ByteString -> ByteString
netstring ByteString
paramTag]
    paramTag :: ByteString
paramTag = ByteString -> [ByteString] -> ByteString
B.intercalate ByteString
"," ([ByteString] -> ByteString)
-> ([SegmentSize] -> [ByteString]) -> [SegmentSize] -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SegmentSize -> ByteString) -> [SegmentSize] -> [ByteString]
forall a b. (a -> b) -> [a] -> [b]
map SegmentSize -> ByteString
forall s. Show s => s -> ByteString
showBytes ([SegmentSize] -> ByteString) -> [SegmentSize] -> ByteString
forall a b. (a -> b) -> a -> b
$ [SegmentSize
requiredI, SegmentSize
totalI, SegmentSize
segmentSizeI]
    requiredI :: SegmentSize
requiredI = Total -> SegmentSize
forall a. Integral a => a -> SegmentSize
toInteger Total
required
    totalI :: SegmentSize
totalI = Total -> SegmentSize
forall a. Integral a => a -> SegmentSize
toInteger Total
total
    segmentSizeI :: SegmentSize
segmentSizeI = SegmentSize -> SegmentSize
forall a. Integral a => a -> SegmentSize
toInteger SegmentSize
segmentSize

-- Compute the strict convergence encryption hash on a lazy data parameter.
convergenceEncryptionHashLazy :: B.ByteString -> Parameters -> BL.ByteString -> B.ByteString
convergenceEncryptionHashLazy :: ByteString -> Parameters -> ByteString -> ByteString
convergenceEncryptionHashLazy ByteString
secret Parameters
params ByteString
bytes =
    -- It was somewhat helpful during development/debugging to make this
    -- function return this instead:
    --
    --     BL.toStrict toHash
    --
    Int -> ByteString -> ByteString
B.take Int
convergenceSecretLength ByteString
theSHA256d
  where
    theSHA256d :: ByteString
theSHA256d = Digest SHA256d -> ByteString
forall a. ByteArrayAccess a => a -> ByteString
toBytes (ByteString -> Digest SHA256d
forall a. HashAlgorithm a => ByteString -> Digest a
hashlazy ByteString
toHash :: Digest SHA256d)

    toHash :: BL.ByteString
    toHash :: ByteString
toHash = [ByteString] -> ByteString
BL.concat [ByteString
tag, ByteString
bytes]

    tag :: ByteString
tag = ByteString -> ByteString
BL.fromStrict (ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
netstring (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> Parameters -> ByteString
convergenceEncryptionTag ByteString
secret Parameters
params

type ByteLength = Int

convergenceSecretLength :: ByteLength
convergenceSecretLength :: Int
convergenceSecretLength = Int
16

storageIndexLength :: ByteLength
storageIndexLength :: Int
storageIndexLength = Int
16