ppad-sha512-0.2.2: lib/Crypto/Hash/SHA512/Internal.hs
{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UnliftedNewtypes #-}
{-# LANGUAGE ViewPatterns #-}
-- |
-- Module: Crypto.Hash.SHA512.Internal
-- Copyright: (c) 2024 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- SHA-512 internals.
module Crypto.Hash.SHA512.Internal (
-- * Types
Block(B, ..)
, Registers(R, ..)
, MAC(..)
-- * Parsing
, parse
, parse_pad1
, parse_pad2
-- * Serializing
, cat
, cat_into
-- * Hash function internals
, update
, iv
-- * HMAC utilities
, pad_registers
, pad_registers_with_length
, xor
, parse_key
-- * HMAC-DRBG utilities
, parse_vsb
, parse_pad1_vsb
, parse_pad2_vsb
-- * Pointer-based IO utilities
, poke_registers
) where
import qualified Data.Bits as B
import qualified Data.ByteString as BS
import qualified Data.ByteString.Internal as BI
import qualified Data.ByteString.Unsafe as BU
import Data.Word (Word8, Word64)
import qualified GHC.IO (IO(..))
import GHC.Ptr (Ptr(..))
import GHC.Exts (Int#)
import qualified GHC.Exts as Exts
import qualified GHC.Word (Word64(..), Word8(..))
-- types ----------------------------------------------------------------------
-- | A message authentication code.
--
-- Note that you should compare MACs for equality using the 'Eq'
-- instance, which performs the comparison in constant time, instead
-- of unwrapping and comparing the underlying 'ByteStrings'.
--
-- >>> let foo@(MAC bs0) = hmac key "hi"
-- >>> let bar@(MAC bs1) = hmac key "there"
-- >>> foo == bar -- do this
-- False
-- >>> bs0 == bs1 -- don't do this
-- False
newtype MAC = MAC BS.ByteString
deriving newtype Show
instance Eq MAC where
-- | A constant-time equality check for message authentication codes.
--
-- Runs in variable-time only for invalid inputs.
(MAC a@(BI.PS _ _ la)) == (MAC b@(BI.PS _ _ lb))
| la /= lb = False
| otherwise = BS.foldl' (B..|.) 0 (BS.packZipWith B.xor a b) == 0
-- | SHA512 block.
newtype Block = Block
(# Exts.Word64#, Exts.Word64#, Exts.Word64#, Exts.Word64#
, Exts.Word64#, Exts.Word64#, Exts.Word64#, Exts.Word64#
, Exts.Word64#, Exts.Word64#, Exts.Word64#, Exts.Word64#
, Exts.Word64#, Exts.Word64#, Exts.Word64#, Exts.Word64#
#)
pattern B
:: Exts.Word64# -> Exts.Word64# -> Exts.Word64# -> Exts.Word64#
-> Exts.Word64# -> Exts.Word64# -> Exts.Word64# -> Exts.Word64#
-> Exts.Word64# -> Exts.Word64# -> Exts.Word64# -> Exts.Word64#
-> Exts.Word64# -> Exts.Word64# -> Exts.Word64# -> Exts.Word64#
-> Block
pattern B w00 w01 w02 w03 w04 w05 w06 w07 w08 w09 w10 w11 w12 w13 w14 w15 =
Block
(# w00, w01, w02, w03, w04, w05, w06, w07
, w08, w09, w10, w11, w12, w13, w14, w15
#)
{-# COMPLETE B #-}
-- | SHA512 state.
newtype Registers = Registers
(# Exts.Word64#, Exts.Word64#, Exts.Word64#, Exts.Word64#
, Exts.Word64#, Exts.Word64#, Exts.Word64#, Exts.Word64#
#)
pattern R
:: Exts.Word64# -> Exts.Word64# -> Exts.Word64# -> Exts.Word64#
-> Exts.Word64# -> Exts.Word64# -> Exts.Word64# -> Exts.Word64#
-> Registers
pattern R w00 w01 w02 w03 w04 w05 w06 w07 = Registers
(# w00, w01, w02, w03
, w04, w05, w06, w07
#)
{-# COMPLETE R #-}
-- parsing (nonfinal input) ---------------------------------------------------
-- | Given a bytestring and offset, parse a full block.
--
-- The length of the input is not checked.
parse :: BS.ByteString -> Int -> Block
parse bs m = B
(word64be bs m)
(word64be bs (m + 08))
(word64be bs (m + 16))
(word64be bs (m + 24))
(word64be bs (m + 32))
(word64be bs (m + 40))
(word64be bs (m + 48))
(word64be bs (m + 56))
(word64be bs (m + 64))
(word64be bs (m + 72))
(word64be bs (m + 80))
(word64be bs (m + 88))
(word64be bs (m + 96))
(word64be bs (m + 104))
(word64be bs (m + 112))
(word64be bs (m + 120))
{-# INLINE parse #-}
-- | Parse the 64-bit word encoded at the given offset.
--
-- The length of the input is not checked.
word64be :: BS.ByteString -> Int -> Exts.Word64#
word64be bs m =
let !(GHC.Word.W8# r0) = BU.unsafeIndex bs m
!(GHC.Word.W8# r1) = BU.unsafeIndex bs (m + 1)
!(GHC.Word.W8# r2) = BU.unsafeIndex bs (m + 2)
!(GHC.Word.W8# r3) = BU.unsafeIndex bs (m + 3)
!(GHC.Word.W8# r4) = BU.unsafeIndex bs (m + 4)
!(GHC.Word.W8# r5) = BU.unsafeIndex bs (m + 5)
!(GHC.Word.W8# r6) = BU.unsafeIndex bs (m + 6)
!(GHC.Word.W8# r7) = BU.unsafeIndex bs (m + 7)
!w0 = Exts.word8ToWord# r0
!w1 = Exts.word8ToWord# r1
!w2 = Exts.word8ToWord# r2
!w3 = Exts.word8ToWord# r3
!w4 = Exts.word8ToWord# r4
!w5 = Exts.word8ToWord# r5
!w6 = Exts.word8ToWord# r6
!w7 = Exts.word8ToWord# r7
!s0 = Exts.uncheckedShiftL# w0 56#
!s1 = Exts.uncheckedShiftL# w1 48#
!s2 = Exts.uncheckedShiftL# w2 40#
!s3 = Exts.uncheckedShiftL# w3 32#
!s4 = Exts.uncheckedShiftL# w4 24#
!s5 = Exts.uncheckedShiftL# w5 16#
!s6 = Exts.uncheckedShiftL# w6 8#
in Exts.wordToWord64#
(s0 `Exts.or#` s1 `Exts.or#` s2 `Exts.or#` s3 `Exts.or#`
s4 `Exts.or#` s5 `Exts.or#` s6 `Exts.or#` w7)
{-# INLINE word64be #-}
-- parsing (final input) ------------------------------------------------------
-- | Parse the final chunk of an input message, assuming it is less than
-- 112 bytes in length (unchecked!).
--
-- Returns one block consisting of the chunk and padding.
parse_pad1
:: BS.ByteString -- ^ final input chunk (< 112 bytes)
-> Word64 -- ^ length of all input
-> Block -- ^ resulting block
parse_pad1 bs l =
let !bits = l * 8
!(GHC.Word.W64# llo) = bits
in B (w64_at bs 000) (w64_at bs 008) (w64_at bs 016) (w64_at bs 024)
(w64_at bs 032) (w64_at bs 040) (w64_at bs 048) (w64_at bs 056)
(w64_at bs 064) (w64_at bs 072) (w64_at bs 080) (w64_at bs 088)
(w64_at bs 096) (w64_at bs 104) (Exts.wordToWord64# 0##) llo
{-# INLINABLE parse_pad1 #-}
-- | Parse the final chunk of an input message, assuming it is at least 112
-- bytes in length (unchecked!).
--
-- Returns two blocks consisting of the chunk and padding.
parse_pad2
:: BS.ByteString -- ^ final input chunk (>= 112 bytes)
-> Word64 -- ^ length of all input
-> (# Block, Block #) -- ^ resulting blocks
parse_pad2 bs l =
let !bits = l * 8
!z = Exts.wordToWord64# 0##
!(GHC.Word.W64# llo) = bits
!block0 = B
(w64_at bs 000) (w64_at bs 008) (w64_at bs 016) (w64_at bs 024)
(w64_at bs 032) (w64_at bs 040) (w64_at bs 048) (w64_at bs 056)
(w64_at bs 064) (w64_at bs 072) (w64_at bs 080) (w64_at bs 088)
(w64_at bs 096) (w64_at bs 104) (w64_at bs 112) (w64_at bs 120)
!block1 = B z z z z z z z z z z z z z z z llo
in (# block0, block1 #)
{-# INLINABLE parse_pad2 #-}
-- | Return the byte at offset 'i', or a padding separator or zero byte
-- beyond the input bounds, as an unboxed word.
w8_as_w64_at
:: BS.ByteString -- ^ input chunk
-> Int -- ^ offset
-> Exts.Word#
w8_as_w64_at bs@(BI.PS _ _ l) i = case compare i l of
LT -> let !(GHC.Word.W8# w) = BU.unsafeIndex bs i
in Exts.word8ToWord# w
EQ -> 0x80##
_ -> 0x00##
{-# INLINE w8_as_w64_at #-}
-- | Return the 64-bit word encoded by eight consecutive bytes at the
-- provided offset.
w64_at
:: BS.ByteString
-> Int
-> Exts.Word64#
w64_at bs i =
let !w0 = w8_as_w64_at bs i `Exts.uncheckedShiftL#` 56#
!w1 = w8_as_w64_at bs (i + 1) `Exts.uncheckedShiftL#` 48#
!w2 = w8_as_w64_at bs (i + 2) `Exts.uncheckedShiftL#` 40#
!w3 = w8_as_w64_at bs (i + 3) `Exts.uncheckedShiftL#` 32#
!w4 = w8_as_w64_at bs (i + 4) `Exts.uncheckedShiftL#` 24#
!w5 = w8_as_w64_at bs (i + 5) `Exts.uncheckedShiftL#` 16#
!w6 = w8_as_w64_at bs (i + 6) `Exts.uncheckedShiftL#` 08#
!w7 = w8_as_w64_at bs (i + 7)
in Exts.wordToWord64#
(w0 `Exts.or#` w1 `Exts.or#` w2 `Exts.or#` w3 `Exts.or#`
w4 `Exts.or#` w5 `Exts.or#` w6 `Exts.or#` w7)
{-# INLINE w64_at #-}
-- update ---------------------------------------------------------------------
-- | Update register state, given new input block.
update :: Registers -> Block -> Registers
update
(R h0 h1 h2 h3 h4 h5 h6 h7)
(B b00 b01 b02 b03 b04 b05 b06 b07 b08 b09 b10 b11 b12 b13 b14 b15)
=
let -- message schedule
!w00 = b00; !w01 = b01; !w02 = b02; !w03 = b03
!w04 = b04; !w05 = b05; !w06 = b06; !w07 = b07
!w08 = b08; !w09 = b09; !w10 = b10; !w11 = b11
!w12 = b12; !w13 = b13; !w14 = b14; !w15 = b15
!w16 = ssig1# w14 `p` w09 `p` ssig0# w01 `p` w00
!w17 = ssig1# w15 `p` w10 `p` ssig0# w02 `p` w01
!w18 = ssig1# w16 `p` w11 `p` ssig0# w03 `p` w02
!w19 = ssig1# w17 `p` w12 `p` ssig0# w04 `p` w03
!w20 = ssig1# w18 `p` w13 `p` ssig0# w05 `p` w04
!w21 = ssig1# w19 `p` w14 `p` ssig0# w06 `p` w05
!w22 = ssig1# w20 `p` w15 `p` ssig0# w07 `p` w06
!w23 = ssig1# w21 `p` w16 `p` ssig0# w08 `p` w07
!w24 = ssig1# w22 `p` w17 `p` ssig0# w09 `p` w08
!w25 = ssig1# w23 `p` w18 `p` ssig0# w10 `p` w09
!w26 = ssig1# w24 `p` w19 `p` ssig0# w11 `p` w10
!w27 = ssig1# w25 `p` w20 `p` ssig0# w12 `p` w11
!w28 = ssig1# w26 `p` w21 `p` ssig0# w13 `p` w12
!w29 = ssig1# w27 `p` w22 `p` ssig0# w14 `p` w13
!w30 = ssig1# w28 `p` w23 `p` ssig0# w15 `p` w14
!w31 = ssig1# w29 `p` w24 `p` ssig0# w16 `p` w15
!w32 = ssig1# w30 `p` w25 `p` ssig0# w17 `p` w16
!w33 = ssig1# w31 `p` w26 `p` ssig0# w18 `p` w17
!w34 = ssig1# w32 `p` w27 `p` ssig0# w19 `p` w18
!w35 = ssig1# w33 `p` w28 `p` ssig0# w20 `p` w19
!w36 = ssig1# w34 `p` w29 `p` ssig0# w21 `p` w20
!w37 = ssig1# w35 `p` w30 `p` ssig0# w22 `p` w21
!w38 = ssig1# w36 `p` w31 `p` ssig0# w23 `p` w22
!w39 = ssig1# w37 `p` w32 `p` ssig0# w24 `p` w23
!w40 = ssig1# w38 `p` w33 `p` ssig0# w25 `p` w24
!w41 = ssig1# w39 `p` w34 `p` ssig0# w26 `p` w25
!w42 = ssig1# w40 `p` w35 `p` ssig0# w27 `p` w26
!w43 = ssig1# w41 `p` w36 `p` ssig0# w28 `p` w27
!w44 = ssig1# w42 `p` w37 `p` ssig0# w29 `p` w28
!w45 = ssig1# w43 `p` w38 `p` ssig0# w30 `p` w29
!w46 = ssig1# w44 `p` w39 `p` ssig0# w31 `p` w30
!w47 = ssig1# w45 `p` w40 `p` ssig0# w32 `p` w31
!w48 = ssig1# w46 `p` w41 `p` ssig0# w33 `p` w32
!w49 = ssig1# w47 `p` w42 `p` ssig0# w34 `p` w33
!w50 = ssig1# w48 `p` w43 `p` ssig0# w35 `p` w34
!w51 = ssig1# w49 `p` w44 `p` ssig0# w36 `p` w35
!w52 = ssig1# w50 `p` w45 `p` ssig0# w37 `p` w36
!w53 = ssig1# w51 `p` w46 `p` ssig0# w38 `p` w37
!w54 = ssig1# w52 `p` w47 `p` ssig0# w39 `p` w38
!w55 = ssig1# w53 `p` w48 `p` ssig0# w40 `p` w39
!w56 = ssig1# w54 `p` w49 `p` ssig0# w41 `p` w40
!w57 = ssig1# w55 `p` w50 `p` ssig0# w42 `p` w41
!w58 = ssig1# w56 `p` w51 `p` ssig0# w43 `p` w42
!w59 = ssig1# w57 `p` w52 `p` ssig0# w44 `p` w43
!w60 = ssig1# w58 `p` w53 `p` ssig0# w45 `p` w44
!w61 = ssig1# w59 `p` w54 `p` ssig0# w46 `p` w45
!w62 = ssig1# w60 `p` w55 `p` ssig0# w47 `p` w46
!w63 = ssig1# w61 `p` w56 `p` ssig0# w48 `p` w47
!w64 = ssig1# w62 `p` w57 `p` ssig0# w49 `p` w48
!w65 = ssig1# w63 `p` w58 `p` ssig0# w50 `p` w49
!w66 = ssig1# w64 `p` w59 `p` ssig0# w51 `p` w50
!w67 = ssig1# w65 `p` w60 `p` ssig0# w52 `p` w51
!w68 = ssig1# w66 `p` w61 `p` ssig0# w53 `p` w52
!w69 = ssig1# w67 `p` w62 `p` ssig0# w54 `p` w53
!w70 = ssig1# w68 `p` w63 `p` ssig0# w55 `p` w54
!w71 = ssig1# w69 `p` w64 `p` ssig0# w56 `p` w55
!w72 = ssig1# w70 `p` w65 `p` ssig0# w57 `p` w56
!w73 = ssig1# w71 `p` w66 `p` ssig0# w58 `p` w57
!w74 = ssig1# w72 `p` w67 `p` ssig0# w59 `p` w58
!w75 = ssig1# w73 `p` w68 `p` ssig0# w60 `p` w59
!w76 = ssig1# w74 `p` w69 `p` ssig0# w61 `p` w60
!w77 = ssig1# w75 `p` w70 `p` ssig0# w62 `p` w61
!w78 = ssig1# w76 `p` w71 `p` ssig0# w63 `p` w62
!w79 = ssig1# w77 `p` w72 `p` ssig0# w64 `p` w63
-- rounds (constants are cube roots of first 80 primes)
!(R s00a s00b s00c s00d s00e s00f s00g s00h) =
step# h0 h1 h2 h3 h4 h5 h6 h7 (k 0x428a2f98d728ae22##) w00
!(R s01a s01b s01c s01d s01e s01f s01g s01h) =
step# s00a s00b s00c s00d s00e s00f s00g s00h
(k 0x7137449123ef65cd##) w01
!(R s02a s02b s02c s02d s02e s02f s02g s02h) =
step# s01a s01b s01c s01d s01e s01f s01g s01h
(k 0xb5c0fbcfec4d3b2f##) w02
!(R s03a s03b s03c s03d s03e s03f s03g s03h) =
step# s02a s02b s02c s02d s02e s02f s02g s02h
(k 0xe9b5dba58189dbbc##) w03
!(R s04a s04b s04c s04d s04e s04f s04g s04h) =
step# s03a s03b s03c s03d s03e s03f s03g s03h
(k 0x3956c25bf348b538##) w04
!(R s05a s05b s05c s05d s05e s05f s05g s05h) =
step# s04a s04b s04c s04d s04e s04f s04g s04h
(k 0x59f111f1b605d019##) w05
!(R s06a s06b s06c s06d s06e s06f s06g s06h) =
step# s05a s05b s05c s05d s05e s05f s05g s05h
(k 0x923f82a4af194f9b##) w06
!(R s07a s07b s07c s07d s07e s07f s07g s07h) =
step# s06a s06b s06c s06d s06e s06f s06g s06h
(k 0xab1c5ed5da6d8118##) w07
!(R s08a s08b s08c s08d s08e s08f s08g s08h) =
step# s07a s07b s07c s07d s07e s07f s07g s07h
(k 0xd807aa98a3030242##) w08
!(R s09a s09b s09c s09d s09e s09f s09g s09h) =
step# s08a s08b s08c s08d s08e s08f s08g s08h
(k 0x12835b0145706fbe##) w09
!(R s10a s10b s10c s10d s10e s10f s10g s10h) =
step# s09a s09b s09c s09d s09e s09f s09g s09h
(k 0x243185be4ee4b28c##) w10
!(R s11a s11b s11c s11d s11e s11f s11g s11h) =
step# s10a s10b s10c s10d s10e s10f s10g s10h
(k 0x550c7dc3d5ffb4e2##) w11
!(R s12a s12b s12c s12d s12e s12f s12g s12h) =
step# s11a s11b s11c s11d s11e s11f s11g s11h
(k 0x72be5d74f27b896f##) w12
!(R s13a s13b s13c s13d s13e s13f s13g s13h) =
step# s12a s12b s12c s12d s12e s12f s12g s12h
(k 0x80deb1fe3b1696b1##) w13
!(R s14a s14b s14c s14d s14e s14f s14g s14h) =
step# s13a s13b s13c s13d s13e s13f s13g s13h
(k 0x9bdc06a725c71235##) w14
!(R s15a s15b s15c s15d s15e s15f s15g s15h) =
step# s14a s14b s14c s14d s14e s14f s14g s14h
(k 0xc19bf174cf692694##) w15
!(R s16a s16b s16c s16d s16e s16f s16g s16h) =
step# s15a s15b s15c s15d s15e s15f s15g s15h
(k 0xe49b69c19ef14ad2##) w16
!(R s17a s17b s17c s17d s17e s17f s17g s17h) =
step# s16a s16b s16c s16d s16e s16f s16g s16h
(k 0xefbe4786384f25e3##) w17
!(R s18a s18b s18c s18d s18e s18f s18g s18h) =
step# s17a s17b s17c s17d s17e s17f s17g s17h
(k 0x0fc19dc68b8cd5b5##) w18
!(R s19a s19b s19c s19d s19e s19f s19g s19h) =
step# s18a s18b s18c s18d s18e s18f s18g s18h
(k 0x240ca1cc77ac9c65##) w19
!(R s20a s20b s20c s20d s20e s20f s20g s20h) =
step# s19a s19b s19c s19d s19e s19f s19g s19h
(k 0x2de92c6f592b0275##) w20
!(R s21a s21b s21c s21d s21e s21f s21g s21h) =
step# s20a s20b s20c s20d s20e s20f s20g s20h
(k 0x4a7484aa6ea6e483##) w21
!(R s22a s22b s22c s22d s22e s22f s22g s22h) =
step# s21a s21b s21c s21d s21e s21f s21g s21h
(k 0x5cb0a9dcbd41fbd4##) w22
!(R s23a s23b s23c s23d s23e s23f s23g s23h) =
step# s22a s22b s22c s22d s22e s22f s22g s22h
(k 0x76f988da831153b5##) w23
!(R s24a s24b s24c s24d s24e s24f s24g s24h) =
step# s23a s23b s23c s23d s23e s23f s23g s23h
(k 0x983e5152ee66dfab##) w24
!(R s25a s25b s25c s25d s25e s25f s25g s25h) =
step# s24a s24b s24c s24d s24e s24f s24g s24h
(k 0xa831c66d2db43210##) w25
!(R s26a s26b s26c s26d s26e s26f s26g s26h) =
step# s25a s25b s25c s25d s25e s25f s25g s25h
(k 0xb00327c898fb213f##) w26
!(R s27a s27b s27c s27d s27e s27f s27g s27h) =
step# s26a s26b s26c s26d s26e s26f s26g s26h
(k 0xbf597fc7beef0ee4##) w27
!(R s28a s28b s28c s28d s28e s28f s28g s28h) =
step# s27a s27b s27c s27d s27e s27f s27g s27h
(k 0xc6e00bf33da88fc2##) w28
!(R s29a s29b s29c s29d s29e s29f s29g s29h) =
step# s28a s28b s28c s28d s28e s28f s28g s28h
(k 0xd5a79147930aa725##) w29
!(R s30a s30b s30c s30d s30e s30f s30g s30h) =
step# s29a s29b s29c s29d s29e s29f s29g s29h
(k 0x06ca6351e003826f##) w30
!(R s31a s31b s31c s31d s31e s31f s31g s31h) =
step# s30a s30b s30c s30d s30e s30f s30g s30h
(k 0x142929670a0e6e70##) w31
!(R s32a s32b s32c s32d s32e s32f s32g s32h) =
step# s31a s31b s31c s31d s31e s31f s31g s31h
(k 0x27b70a8546d22ffc##) w32
!(R s33a s33b s33c s33d s33e s33f s33g s33h) =
step# s32a s32b s32c s32d s32e s32f s32g s32h
(k 0x2e1b21385c26c926##) w33
!(R s34a s34b s34c s34d s34e s34f s34g s34h) =
step# s33a s33b s33c s33d s33e s33f s33g s33h
(k 0x4d2c6dfc5ac42aed##) w34
!(R s35a s35b s35c s35d s35e s35f s35g s35h) =
step# s34a s34b s34c s34d s34e s34f s34g s34h
(k 0x53380d139d95b3df##) w35
!(R s36a s36b s36c s36d s36e s36f s36g s36h) =
step# s35a s35b s35c s35d s35e s35f s35g s35h
(k 0x650a73548baf63de##) w36
!(R s37a s37b s37c s37d s37e s37f s37g s37h) =
step# s36a s36b s36c s36d s36e s36f s36g s36h
(k 0x766a0abb3c77b2a8##) w37
!(R s38a s38b s38c s38d s38e s38f s38g s38h) =
step# s37a s37b s37c s37d s37e s37f s37g s37h
(k 0x81c2c92e47edaee6##) w38
!(R s39a s39b s39c s39d s39e s39f s39g s39h) =
step# s38a s38b s38c s38d s38e s38f s38g s38h
(k 0x92722c851482353b##) w39
!(R s40a s40b s40c s40d s40e s40f s40g s40h) =
step# s39a s39b s39c s39d s39e s39f s39g s39h
(k 0xa2bfe8a14cf10364##) w40
!(R s41a s41b s41c s41d s41e s41f s41g s41h) =
step# s40a s40b s40c s40d s40e s40f s40g s40h
(k 0xa81a664bbc423001##) w41
!(R s42a s42b s42c s42d s42e s42f s42g s42h) =
step# s41a s41b s41c s41d s41e s41f s41g s41h
(k 0xc24b8b70d0f89791##) w42
!(R s43a s43b s43c s43d s43e s43f s43g s43h) =
step# s42a s42b s42c s42d s42e s42f s42g s42h
(k 0xc76c51a30654be30##) w43
!(R s44a s44b s44c s44d s44e s44f s44g s44h) =
step# s43a s43b s43c s43d s43e s43f s43g s43h
(k 0xd192e819d6ef5218##) w44
!(R s45a s45b s45c s45d s45e s45f s45g s45h) =
step# s44a s44b s44c s44d s44e s44f s44g s44h
(k 0xd69906245565a910##) w45
!(R s46a s46b s46c s46d s46e s46f s46g s46h) =
step# s45a s45b s45c s45d s45e s45f s45g s45h
(k 0xf40e35855771202a##) w46
!(R s47a s47b s47c s47d s47e s47f s47g s47h) =
step# s46a s46b s46c s46d s46e s46f s46g s46h
(k 0x106aa07032bbd1b8##) w47
!(R s48a s48b s48c s48d s48e s48f s48g s48h) =
step# s47a s47b s47c s47d s47e s47f s47g s47h
(k 0x19a4c116b8d2d0c8##) w48
!(R s49a s49b s49c s49d s49e s49f s49g s49h) =
step# s48a s48b s48c s48d s48e s48f s48g s48h
(k 0x1e376c085141ab53##) w49
!(R s50a s50b s50c s50d s50e s50f s50g s50h) =
step# s49a s49b s49c s49d s49e s49f s49g s49h
(k 0x2748774cdf8eeb99##) w50
!(R s51a s51b s51c s51d s51e s51f s51g s51h) =
step# s50a s50b s50c s50d s50e s50f s50g s50h
(k 0x34b0bcb5e19b48a8##) w51
!(R s52a s52b s52c s52d s52e s52f s52g s52h) =
step# s51a s51b s51c s51d s51e s51f s51g s51h
(k 0x391c0cb3c5c95a63##) w52
!(R s53a s53b s53c s53d s53e s53f s53g s53h) =
step# s52a s52b s52c s52d s52e s52f s52g s52h
(k 0x4ed8aa4ae3418acb##) w53
!(R s54a s54b s54c s54d s54e s54f s54g s54h) =
step# s53a s53b s53c s53d s53e s53f s53g s53h
(k 0x5b9cca4f7763e373##) w54
!(R s55a s55b s55c s55d s55e s55f s55g s55h) =
step# s54a s54b s54c s54d s54e s54f s54g s54h
(k 0x682e6ff3d6b2b8a3##) w55
!(R s56a s56b s56c s56d s56e s56f s56g s56h) =
step# s55a s55b s55c s55d s55e s55f s55g s55h
(k 0x748f82ee5defb2fc##) w56
!(R s57a s57b s57c s57d s57e s57f s57g s57h) =
step# s56a s56b s56c s56d s56e s56f s56g s56h
(k 0x78a5636f43172f60##) w57
!(R s58a s58b s58c s58d s58e s58f s58g s58h) =
step# s57a s57b s57c s57d s57e s57f s57g s57h
(k 0x84c87814a1f0ab72##) w58
!(R s59a s59b s59c s59d s59e s59f s59g s59h) =
step# s58a s58b s58c s58d s58e s58f s58g s58h
(k 0x8cc702081a6439ec##) w59
!(R s60a s60b s60c s60d s60e s60f s60g s60h) =
step# s59a s59b s59c s59d s59e s59f s59g s59h
(k 0x90befffa23631e28##) w60
!(R s61a s61b s61c s61d s61e s61f s61g s61h) =
step# s60a s60b s60c s60d s60e s60f s60g s60h
(k 0xa4506cebde82bde9##) w61
!(R s62a s62b s62c s62d s62e s62f s62g s62h) =
step# s61a s61b s61c s61d s61e s61f s61g s61h
(k 0xbef9a3f7b2c67915##) w62
!(R s63a s63b s63c s63d s63e s63f s63g s63h) =
step# s62a s62b s62c s62d s62e s62f s62g s62h
(k 0xc67178f2e372532b##) w63
!(R s64a s64b s64c s64d s64e s64f s64g s64h) =
step# s63a s63b s63c s63d s63e s63f s63g s63h
(k 0xca273eceea26619c##) w64
!(R s65a s65b s65c s65d s65e s65f s65g s65h) =
step# s64a s64b s64c s64d s64e s64f s64g s64h
(k 0xd186b8c721c0c207##) w65
!(R s66a s66b s66c s66d s66e s66f s66g s66h) =
step# s65a s65b s65c s65d s65e s65f s65g s65h
(k 0xeada7dd6cde0eb1e##) w66
!(R s67a s67b s67c s67d s67e s67f s67g s67h) =
step# s66a s66b s66c s66d s66e s66f s66g s66h
(k 0xf57d4f7fee6ed178##) w67
!(R s68a s68b s68c s68d s68e s68f s68g s68h) =
step# s67a s67b s67c s67d s67e s67f s67g s67h
(k 0x06f067aa72176fba##) w68
!(R s69a s69b s69c s69d s69e s69f s69g s69h) =
step# s68a s68b s68c s68d s68e s68f s68g s68h
(k 0x0a637dc5a2c898a6##) w69
!(R s70a s70b s70c s70d s70e s70f s70g s70h) =
step# s69a s69b s69c s69d s69e s69f s69g s69h
(k 0x113f9804bef90dae##) w70
!(R s71a s71b s71c s71d s71e s71f s71g s71h) =
step# s70a s70b s70c s70d s70e s70f s70g s70h
(k 0x1b710b35131c471b##) w71
!(R s72a s72b s72c s72d s72e s72f s72g s72h) =
step# s71a s71b s71c s71d s71e s71f s71g s71h
(k 0x28db77f523047d84##) w72
!(R s73a s73b s73c s73d s73e s73f s73g s73h) =
step# s72a s72b s72c s72d s72e s72f s72g s72h
(k 0x32caab7b40c72493##) w73
!(R s74a s74b s74c s74d s74e s74f s74g s74h) =
step# s73a s73b s73c s73d s73e s73f s73g s73h
(k 0x3c9ebe0a15c9bebc##) w74
!(R s75a s75b s75c s75d s75e s75f s75g s75h) =
step# s74a s74b s74c s74d s74e s74f s74g s74h
(k 0x431d67c49c100d4c##) w75
!(R s76a s76b s76c s76d s76e s76f s76g s76h) =
step# s75a s75b s75c s75d s75e s75f s75g s75h
(k 0x4cc5d4becb3e42b6##) w76
!(R s77a s77b s77c s77d s77e s77f s77g s77h) =
step# s76a s76b s76c s76d s76e s76f s76g s76h
(k 0x597f299cfc657e2a##) w77
!(R s78a s78b s78c s78d s78e s78f s78g s78h) =
step# s77a s77b s77c s77d s77e s77f s77g s77h
(k 0x5fcb6fab3ad6faec##) w78
!(R s79a s79b s79c s79d s79e s79f s79g s79h) =
step# s78a s78b s78c s78d s78e s78f s78g s78h
(k 0x6c44198c4a475817##) w79
in R (h0 `p` s79a) (h1 `p` s79b) (h2 `p` s79c) (h3 `p` s79d)
(h4 `p` s79e) (h5 `p` s79f) (h6 `p` s79g) (h7 `p` s79h)
where
p = Exts.plusWord64#
{-# INLINE p #-}
k :: Exts.Word# -> Exts.Word64#
k = Exts.wordToWord64#
{-# INLINE k #-}
-- rotate right
rotr# :: Exts.Word64# -> Int# -> Exts.Word64#
rotr# x n =
Exts.uncheckedShiftRL64# x n `Exts.or64#`
Exts.uncheckedShiftL64# x (64# Exts.-# n)
{-# INLINE rotr# #-}
-- logical right shift
shr# :: Exts.Word64# -> Int# -> Exts.Word64#
shr# = Exts.uncheckedShiftRL64#
{-# INLINE shr# #-}
-- ch(x, y, z) = (x & y) ^ (~x & z)
ch# :: Exts.Word64# -> Exts.Word64# -> Exts.Word64# -> Exts.Word64#
ch# x y z =
(x `Exts.and64#` y) `Exts.xor64#`
(Exts.not64# x `Exts.and64#` z)
{-# INLINE ch# #-}
-- maj(x, y, z) = (x & (y | z)) | (y & z)
maj# :: Exts.Word64# -> Exts.Word64# -> Exts.Word64# -> Exts.Word64#
maj# x y z =
(x `Exts.and64#` (y `Exts.or64#` z)) `Exts.or64#`
(y `Exts.and64#` z)
{-# INLINE maj# #-}
-- big sigma 0: rotr28 ^ rotr34 ^ rotr39
bsig0# :: Exts.Word64# -> Exts.Word64#
bsig0# x =
rotr# x 28# `Exts.xor64#` rotr# x 34# `Exts.xor64#` rotr# x 39#
{-# INLINE bsig0# #-}
-- big sigma 1: rotr14 ^ rotr18 ^ rotr41
bsig1# :: Exts.Word64# -> Exts.Word64#
bsig1# x =
rotr# x 14# `Exts.xor64#` rotr# x 18# `Exts.xor64#` rotr# x 41#
{-# INLINE bsig1# #-}
-- small sigma 0: rotr1 ^ rotr8 ^ shr7
ssig0# :: Exts.Word64# -> Exts.Word64#
ssig0# x =
rotr# x 1# `Exts.xor64#` rotr# x 8# `Exts.xor64#` shr# x 7#
{-# INLINE ssig0# #-}
-- small sigma 1: rotr19 ^ rotr61 ^ shr6
ssig1# :: Exts.Word64# -> Exts.Word64#
ssig1# x =
rotr# x 19# `Exts.xor64#` rotr# x 61# `Exts.xor64#` shr# x 6#
{-# INLINE ssig1# #-}
-- round step
step#
:: Exts.Word64# -> Exts.Word64# -> Exts.Word64# -> Exts.Word64#
-> Exts.Word64# -> Exts.Word64# -> Exts.Word64# -> Exts.Word64#
-> Exts.Word64# -> Exts.Word64#
-> Registers
step# a b c d e f g h k w =
let !t1 = h
`Exts.plusWord64#` bsig1# e
`Exts.plusWord64#` ch# e f g
`Exts.plusWord64#` k
`Exts.plusWord64#` w
!t2 = bsig0# a `Exts.plusWord64#` maj# a b c
in R (t1 `Exts.plusWord64#` t2) a b c (d `Exts.plusWord64#` t1) e f g
{-# INLINE step# #-}
-- initial register state; first 64 bits of the fractional parts of the
-- square roots of the first eight primes
iv :: () -> Registers
iv _ = R
(Exts.wordToWord64# 0x6a09e667f3bcc908##)
(Exts.wordToWord64# 0xbb67ae8584caa73b##)
(Exts.wordToWord64# 0x3c6ef372fe94f82b##)
(Exts.wordToWord64# 0xa54ff53a5f1d36f1##)
(Exts.wordToWord64# 0x510e527fade682d1##)
(Exts.wordToWord64# 0x9b05688c2b3e6c1f##)
(Exts.wordToWord64# 0x1f83d9abfb41bd6b##)
(Exts.wordToWord64# 0x5be0cd19137e2179##)
-- serializing ----------------------------------------------------------------
-- | Concat SHA512 state into a ByteString.
cat :: Registers -> BS.ByteString
cat rs = BI.unsafeCreate 64 (cat_into rs)
{-# INLINABLE cat #-}
-- | Serialize SHA512 state to a pointer (big-endian).
cat_into :: Registers -> Ptr Word8 -> IO ()
cat_into (R h0 h1 h2 h3 h4 h5 h6 h7) (Ptr addr) = GHC.IO.IO $ \s0 ->
case poke64be addr 00# h0 s0 of { s1 ->
case poke64be addr 08# h1 s1 of { s2 ->
case poke64be addr 16# h2 s2 of { s3 ->
case poke64be addr 24# h3 s3 of { s4 ->
case poke64be addr 32# h4 s4 of { s5 ->
case poke64be addr 40# h5 s5 of { s6 ->
case poke64be addr 48# h6 s6 of { s7 ->
case poke64be addr 56# h7 s7 of { s8 ->
(# s8, () #)
}}}}}}}}
{-# INLINE cat_into #-}
poke64be
:: Exts.Addr#
-> Int#
-> Exts.Word64#
-> Exts.State# Exts.RealWorld
-> Exts.State# Exts.RealWorld
poke64be a off w s0 =
case Exts.writeWord8OffAddr# a off (byte# w 56#) s0 of { s1 ->
case Exts.writeWord8OffAddr# a (off Exts.+# 1#) (byte# w 48#) s1 of { s2 ->
case Exts.writeWord8OffAddr# a (off Exts.+# 2#) (byte# w 40#) s2 of { s3 ->
case Exts.writeWord8OffAddr# a (off Exts.+# 3#) (byte# w 32#) s3 of { s4 ->
case Exts.writeWord8OffAddr# a (off Exts.+# 4#) (byte# w 24#) s4 of { s5 ->
case Exts.writeWord8OffAddr# a (off Exts.+# 5#) (byte# w 16#) s5 of { s6 ->
case Exts.writeWord8OffAddr# a (off Exts.+# 6#) (byte# w 8#) s6 of { s7 ->
Exts.writeWord8OffAddr# a (off Exts.+# 7#) (byte# w 0#) s7
}}}}}}}
{-# INLINE poke64be #-}
byte# :: Exts.Word64# -> Int# -> Exts.Word8#
byte# w n = Exts.wordToWord8#
(Exts.word64ToWord# (Exts.uncheckedShiftRL64# w n))
{-# INLINE byte# #-}
-- | Write register state to a pointer (native endian Word64s).
poke_registers :: Ptr Word64 -> Registers -> IO ()
poke_registers (Ptr addr) (R w0 w1 w2 w3 w4 w5 w6 w7) = GHC.IO.IO $ \s0 ->
case Exts.writeWord64OffAddr# addr 0# w0 s0 of { s1 ->
case Exts.writeWord64OffAddr# addr 1# w1 s1 of { s2 ->
case Exts.writeWord64OffAddr# addr 2# w2 s2 of { s3 ->
case Exts.writeWord64OffAddr# addr 3# w3 s3 of { s4 ->
case Exts.writeWord64OffAddr# addr 4# w4 s4 of { s5 ->
case Exts.writeWord64OffAddr# addr 5# w5 s5 of { s6 ->
case Exts.writeWord64OffAddr# addr 6# w6 s6 of { s7 ->
case Exts.writeWord64OffAddr# addr 7# w7 s7 of { s8 ->
(# s8, () #) }}}}}}}}
{-# INLINE poke_registers #-}
-- hmac utilities -------------------------------------------------------------
-- pad registers to block
pad_registers :: Registers -> Block
pad_registers (R w0 w1 w2 w3 w4 w5 w6 w7) = B
w0 w1 w2 w3 w4 w5 w6 w7
(Exts.wordToWord64# 0##) (Exts.wordToWord64# 0##) (Exts.wordToWord64# 0##)
(Exts.wordToWord64# 0##) (Exts.wordToWord64# 0##) (Exts.wordToWord64# 0##)
(Exts.wordToWord64# 0##) (Exts.wordToWord64# 0##)
{-# INLINE pad_registers #-}
-- pad registers to block, using padding separator and augmented length
-- (assumes existence of a leading block)
-- length = (128 + 64) * 8 = 1536 = 0x600
pad_registers_with_length :: Registers -> Block
pad_registers_with_length (R h0 h1 h2 h3 h4 h5 h6 h7) = B
h0 h1 h2 h3 h4 h5 h6 h7 -- inner hash
(Exts.wordToWord64# 0x8000000000000000##) -- padding separator
(Exts.wordToWord64# 0x0000000000000000##)
(Exts.wordToWord64# 0x0000000000000000##)
(Exts.wordToWord64# 0x0000000000000000##)
(Exts.wordToWord64# 0x0000000000000000##)
(Exts.wordToWord64# 0x0000000000000000##)
(Exts.wordToWord64# 0x0000000000000000##) -- high 64 bits of length
(Exts.wordToWord64# 0x0000000000000600##) -- low 64 bits of length
{-# INLINABLE pad_registers_with_length #-}
xor :: Block -> Exts.Word64# -> Block
xor (B w00 w01 w02 w03 w04 w05 w06 w07 w08 w09 w10 w11 w12 w13 w14 w15) b = B
(Exts.xor64# w00 b)
(Exts.xor64# w01 b)
(Exts.xor64# w02 b)
(Exts.xor64# w03 b)
(Exts.xor64# w04 b)
(Exts.xor64# w05 b)
(Exts.xor64# w06 b)
(Exts.xor64# w07 b)
(Exts.xor64# w08 b)
(Exts.xor64# w09 b)
(Exts.xor64# w10 b)
(Exts.xor64# w11 b)
(Exts.xor64# w12 b)
(Exts.xor64# w13 b)
(Exts.xor64# w14 b)
(Exts.xor64# w15 b)
{-# INLINE xor #-}
parse_key :: BS.ByteString -> Block
parse_key bs = B
(w64_zero bs 000) (w64_zero bs 008) (w64_zero bs 016) (w64_zero bs 024)
(w64_zero bs 032) (w64_zero bs 040) (w64_zero bs 048) (w64_zero bs 056)
(w64_zero bs 064) (w64_zero bs 072) (w64_zero bs 080) (w64_zero bs 088)
(w64_zero bs 096) (w64_zero bs 104) (w64_zero bs 112) (w64_zero bs 120)
{-# INLINE parse_key #-}
-- read big-endian Word64#, zero-padding beyond input length
w64_zero :: BS.ByteString -> Int -> Exts.Word64#
w64_zero bs i =
let !w0 = w8_zero bs i `Exts.uncheckedShiftL#` 56#
!w1 = w8_zero bs (i + 1) `Exts.uncheckedShiftL#` 48#
!w2 = w8_zero bs (i + 2) `Exts.uncheckedShiftL#` 40#
!w3 = w8_zero bs (i + 3) `Exts.uncheckedShiftL#` 32#
!w4 = w8_zero bs (i + 4) `Exts.uncheckedShiftL#` 24#
!w5 = w8_zero bs (i + 5) `Exts.uncheckedShiftL#` 16#
!w6 = w8_zero bs (i + 6) `Exts.uncheckedShiftL#` 08#
!w7 = w8_zero bs (i + 7)
in Exts.wordToWord64#
(w0 `Exts.or#` w1 `Exts.or#` w2 `Exts.or#` w3 `Exts.or#`
w4 `Exts.or#` w5 `Exts.or#` w6 `Exts.or#` w7)
{-# INLINE w64_zero #-}
-- read byte as Word#, returning zero beyond input length
w8_zero :: BS.ByteString -> Int -> Exts.Word#
w8_zero bs@(BI.PS _ _ l) i
| i < l = let !(GHC.Word.W8# w) = BU.unsafeIndex bs i
in Exts.word8ToWord# w
| otherwise = 0##
{-# INLINE w8_zero #-}
-- hmac-drbg utilities --------------------------------------------------------
-- | Parse first complete block from v || sep || dat[0:63].
--
-- Requires len(dat) >= 63.
parse_vsb :: Registers -> Word8 -> BS.ByteString -> Block
parse_vsb (R v0 v1 v2 v3 v4 v5 v6 v7) (GHC.Word.W8# sep) dat =
let !(GHC.Word.W8# b0) = BU.unsafeIndex dat 0
!(GHC.Word.W8# b1) = BU.unsafeIndex dat 1
!(GHC.Word.W8# b2) = BU.unsafeIndex dat 2
!(GHC.Word.W8# b3) = BU.unsafeIndex dat 3
!(GHC.Word.W8# b4) = BU.unsafeIndex dat 4
!(GHC.Word.W8# b5) = BU.unsafeIndex dat 5
!(GHC.Word.W8# b6) = BU.unsafeIndex dat 6
!w08 =
Exts.uncheckedShiftL# (Exts.word8ToWord# sep) 56#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# b0) 48#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# b1) 40#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# b2) 32#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# b3) 24#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# b4) 16#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# b5) 8#
`Exts.or#`
Exts.word8ToWord# b6
in B v0 v1 v2 v3 v4 v5 v6 v7
(Exts.wordToWord64# w08)
(word64be dat 07) (word64be dat 15) (word64be dat 23)
(word64be dat 31) (word64be dat 39) (word64be dat 47) (word64be dat 55)
{-# INLINE parse_vsb #-}
-- | Parse single padding block from v || sep || dat.
--
-- Requires (65 + len(dat)) < 112.
parse_pad1_vsb :: Registers -> Word8 -> BS.ByteString -> Word64 -> Block
parse_pad1_vsb (R v0 v1 v2 v3 v4 v5 v6 v7) sep dat total =
let !bits = total * 8
!(GHC.Word.W64# llo) = bits
in B v0 v1 v2 v3 v4 v5 v6 v7
(w64_sdp sep dat 064) (w64_sdp sep dat 072)
(w64_sdp sep dat 080) (w64_sdp sep dat 088)
(w64_sdp sep dat 096) (w64_sdp sep dat 104)
(Exts.wordToWord64# 0##) llo
{-# INLINABLE parse_pad1_vsb #-}
-- | Parse two padding blocks from v || sep || dat.
--
-- Requires 112 <= (65 + len(dat)) < 128.
parse_pad2_vsb
:: Registers -> Word8 -> BS.ByteString -> Word64 -> (# Block, Block #)
parse_pad2_vsb (R v0 v1 v2 v3 v4 v5 v6 v7) sep dat total =
let !bits = total * 8
!z = Exts.wordToWord64# 0##
!(GHC.Word.W64# llo) = bits
!b0 = B v0 v1 v2 v3 v4 v5 v6 v7
(w64_sdp sep dat 064) (w64_sdp sep dat 072)
(w64_sdp sep dat 080) (w64_sdp sep dat 088)
(w64_sdp sep dat 096) (w64_sdp sep dat 104)
(w64_sdp sep dat 112) (w64_sdp sep dat 120)
!b1 = B z z z z z z z z z z z z z z z llo
in (# b0, b1 #)
{-# INLINABLE parse_pad2_vsb #-}
-- Read Word64 at offset i (>= 64) from (sep || dat || 0x80 || zeros).
w64_sdp :: Word8 -> BS.ByteString -> Int -> Exts.Word64#
w64_sdp sep dat i =
let !(GHC.Word.W8# a) = byte_sdp sep dat i
!(GHC.Word.W8# b) = byte_sdp sep dat (i + 1)
!(GHC.Word.W8# c) = byte_sdp sep dat (i + 2)
!(GHC.Word.W8# d) = byte_sdp sep dat (i + 3)
!(GHC.Word.W8# e) = byte_sdp sep dat (i + 4)
!(GHC.Word.W8# f) = byte_sdp sep dat (i + 5)
!(GHC.Word.W8# g) = byte_sdp sep dat (i + 6)
!(GHC.Word.W8# h) = byte_sdp sep dat (i + 7)
in Exts.wordToWord64#
(Exts.uncheckedShiftL# (Exts.word8ToWord# a) 56#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# b) 48#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# c) 40#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# d) 32#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# e) 24#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# f) 16#
`Exts.or#`
Exts.uncheckedShiftL# (Exts.word8ToWord# g) 8#
`Exts.or#`
Exts.word8ToWord# h)
{-# INLINE w64_sdp #-}
-- Read byte at offset i (>= 64) from (sep || dat || 0x80 || zeros).
byte_sdp :: Word8 -> BS.ByteString -> Int -> Word8
byte_sdp sep dat@(BI.PS _ _ l) i
| i == 64 = sep
| i < 65 + l = BU.unsafeIndex dat (i - 65)
| i == 65 + l = 0x80
| otherwise = 0x00
{-# INLINE byte_sdp #-}