Skip to content

Commit f0d0d12

Browse files
committed
Add support for base64 and sha512
cryptohash-sha512 is not yet updated to work with ghc 8.10. Some jailbreaking is required.
1 parent a139b6f commit f0d0d12

File tree

3 files changed

+41
-14
lines changed

3 files changed

+41
-14
lines changed

hnix-store-core/hnix-store-core.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ library
3333
build-depends: base >=4.10 && <5
3434
, attoparsec
3535
, base16-bytestring
36+
, base64-bytestring
3637
, bytestring
3738
, binary
3839
, bytestring
3940
, containers
4041
, cryptohash-md5
4142
, cryptohash-sha1
4243
, cryptohash-sha256
44+
, cryptohash-sha512
4345
, directory
4446
, filepath
4547
, hashable

hnix-store-core/src/System/Nix/Internal/Hash.hs

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ module System.Nix.Internal.Hash where
1616
import qualified Crypto.Hash.MD5 as MD5
1717
import qualified Crypto.Hash.SHA1 as SHA1
1818
import qualified Crypto.Hash.SHA256 as SHA256
19+
import qualified Crypto.Hash.SHA512 as SHA512
1920
import qualified Data.ByteString as BS
2021
import qualified Data.ByteString.Base16 as Base16
22+
import qualified Data.ByteString.Base64 as Base64
2123
import Data.Bits (xor)
2224
import qualified Data.ByteString.Lazy as BSL
2325
import 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-
{-
8486
instance NamedAlgo 'SHA512 where
8587
algoName = "sha512"
8688
hashSize = 64
87-
-}
8889

8990
-- | A digest whose 'NamedAlgo' is not known at compile time.
9091
data 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

9697
mkNamedDigest :: 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.
154172
instance 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.
176201
instance (ValidAlgo a, KnownNat n) => ValidAlgo ('Truncated n a) where

hnix-store-remote/src/System/Nix/Store/Remote/Parsers.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ parseTypedDigest :: Parser (Either String SomeNamedDigest)
4949
parseTypedDigest = mkNamedDigest <$> parseHashType <*> parseHash
5050

5151
parseHashType :: Parser Data.Text.Text
52-
parseHashType = decodeUtf8 <$> ("sha256" <|> "sha1" <|> "md5") <* ":"
52+
parseHashType = decodeUtf8 <$> ("sha256" <|> "sha512" <|> "sha1" <|> "md5") <* (":" <|> "-")
5353

5454
parseHash :: Parser Data.Text.Text
5555
parseHash = decodeUtf8 <$> takeWhile1 (/= ':')

0 commit comments

Comments
 (0)