@@ -16,8 +16,10 @@ module System.Nix.Internal.Hash where
1616import qualified Crypto.Hash.MD5 as MD5
1717import qualified Crypto.Hash.SHA1 as SHA1
1818import qualified Crypto.Hash.SHA256 as SHA256
19+ import qualified Crypto.Hash.SHA512 as SHA512
1920import qualified Data.ByteString as BS
2021import qualified Data.ByteString.Base16 as Base16
22+ import qualified Data.ByteString.Base64 as Base64
2123import Data.Bits (xor )
2224import qualified Data.ByteString.Lazy as BSL
2325import qualified Data.Hashable as DataHashable
@@ -37,6 +39,7 @@ data HashAlgorithm
3739 = MD5
3840 | SHA1
3941 | SHA256
42+ | SHA512
4043 | Truncated Nat HashAlgorithm
4144 -- ^ The hash algorithm obtained by truncating the result of the
4245 -- input 'HashAlgorithm' to the given number of bytes. See
@@ -80,11 +83,9 @@ instance NamedAlgo 'SHA256 where
8083 algoName = " sha256"
8184 hashSize = 32
8285
83- {-
8486instance NamedAlgo 'SHA512 where
8587 algoName = " sha512"
8688 hashSize = 64
87- -}
8889
8990-- | A digest whose 'NamedAlgo' is not known at compile time.
9091data SomeNamedDigest = forall a . NamedAlgo a => SomeDigest (Digest a )
@@ -94,24 +95,31 @@ instance Show SomeNamedDigest where
9495 SomeDigest (digest :: Digest hashType ) -> T. unpack $ " SomeDigest " <> algoName @ hashType <> " :" <> encodeBase32 digest
9596
9697mkNamedDigest :: Text -> Text -> Either String SomeNamedDigest
97- mkNamedDigest name hash = case name of
98- " md5 " -> SomeDigest <$> decode @ 'MD5
99- " sha1 " -> SomeDigest <$> decode @ 'SHA1
100- " sha256 " -> SomeDigest <$> decode @ 'SHA256
101- _ -> Left $ " Unknown hash name: " ++ T. unpack name
98+ mkNamedDigest name sriHash =
99+ let (sriName, hash) = T. breakOnEnd " - " sriHash in
100+ if sriName == " " || sriName == (name <> " - " )
101+ then mkDigest name hash
102+ else Left $ T. unpack $ " Sri hash method " <> sriName <> " does not match the required hash type " <> name
102103 where
103- size = T. length hash
104- decode :: forall a . (NamedAlgo a , ValidAlgo a ) => Either String (Digest a )
105- decode
104+ mkDigest name hash = case name of
105+ " md5" -> SomeDigest <$> decode @ 'MD5 hash
106+ " sha1" -> SomeDigest <$> decode @ 'SHA1 hash
107+ " sha256" -> SomeDigest <$> decode @ 'SHA256 hash
108+ " sha512" -> SomeDigest <$> decode @ 'SHA512 hash
109+ _ -> Left $ " Unknown hash name: " ++ T. unpack name
110+ decode :: forall a . (NamedAlgo a , ValidAlgo a ) => Text -> Either String (Digest a )
111+ decode hash
106112 | size == base16Len = decodeBase16 hash
107113 | size == base32Len = decodeBase32 hash
108- -- | size == base64Len = decodeBase64 s -- TODO
109- | otherwise = Left $ T. unpack hash ++ " is not a valid " ++ T. unpack name ++ " hash."
114+ | size == base64Len = decodeBase64 hash
115+ | otherwise = Left $ T. unpack sriHash ++ " is not a valid " ++ T. unpack name ++ " hash. Its length ( " ++ show size ++ " ) does not match any of " ++ show [base16Len, base32Len, base64Len]
110116 where
117+ size = T. length hash
111118 hsize = hashSize @ a
112119 base16Len = hsize * 2
113120 base32Len = ((hsize * 8 - 1 ) `div` 5 ) + 1 ;
114- -- base64Len = ((4 * hsize / 3) + 3) & ~3;
121+ base64Len = ((4 * hsize `div` 3 ) + 3 ) `div` 4 * 4 ;
122+
115123
116124-- | Hash an entire (strict) 'BS.ByteString' as a single call.
117125--
@@ -150,6 +158,16 @@ decodeBase16 t = case Base16.decode (T.encodeUtf8 t) of
150158 (x, " " ) -> Right $ Digest x
151159 _ -> Left $ " Unable to decode base16 string " ++ T. unpack t
152160
161+ -- | Encode a 'Digest' in hex.
162+ encodeBase64 :: Digest a -> T. Text
163+ encodeBase64 (Digest bs) = T. decodeUtf8 (Base64. encode bs)
164+
165+ -- | Decode a 'Digest' in hex
166+ decodeBase64 :: T. Text -> Either String (Digest a )
167+ decodeBase64 t = case Base64. decode (T. encodeUtf8 t) of
168+ Right x -> Right $ Digest x
169+ Left e -> Left $ " Unable to decode base64 string " ++ T. unpack t ++ " : " ++ e
170+
153171-- | Uses "Crypto.Hash.MD5" from cryptohash-md5.
154172instance ValidAlgo 'MD5 where
155173 type AlgoCtx 'MD5 = MD5. Ctx
@@ -171,6 +189,13 @@ instance ValidAlgo 'SHA256 where
171189 update = SHA256. update
172190 finalize = Digest . SHA256. finalize
173191
192+ -- | Uses "Crypto.Hash.SHA512" from cryptohash-sha512.
193+ instance ValidAlgo 'SHA512 where
194+ type AlgoCtx 'SHA512 = SHA512. Ctx
195+ initialize = SHA512. init
196+ update = SHA512. update
197+ finalize = Digest . SHA512. finalize
198+
174199-- | Reuses the underlying 'ValidAlgo' instance, but does a
175200-- 'truncateDigest' at the end.
176201instance (ValidAlgo a , KnownNat n ) => ValidAlgo ('Truncated n a ) where
0 commit comments