Search code examples
haskellcryptography

Examples of SizedByteArray in Haskell


I've been trying to use the hsblst library and I having problems with Data.ByteArray.Sized. For example, to use keygen from Crypto.BLST with signature

keygen :: (ByteArrayAccess ba, 32 <= n, KnownNat n) => SizedByteArray n ba -> SecretKey

How do I make a SizedByteArray n ba object? In this link there is an example where

sk1 = keygen $ AS.unsafeSizedByteArray @32 @ByteString "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"

where AS is an alias for Data.ByteArray.Sized. This example is not working for me. When I try to do the same those at_marks do not work.

Then I tried this code

import Data.ByteArray.Sized qualified as AS
import Data.Text.Encoding
import qualified Data.ByteString as B
import qualified Data.Text as T
import Data.Proxy (Proxy(..))
import Crypto.BLST

sk ="79cc407e6917b5673a1f6966c23c9e15\
     \d257e5ab46cfe7b9b2f64200f2b2843e"
skp = T.pack sk
skpb = encodeUtf8 skp

the name skpb is of type ByteString. So, according to the definition of sizedByteArray (shown below) I should get a Maybe SizedByteArray right?

-- | create a 'SizedByteArray' from the given 'ByteArrayAccess' if the
-- size is the same as the target size.
--
sizedByteArray :: forall n ba . (KnownNat n, ByteArrayAccess ba)
               => ba
               -> Maybe (SizedByteArray n ba)
sizedByteArray ba
    | length ba == n = Just $ SizedByteArray ba
    | otherwise      = Nothing
  where
    n = fromInteger $ natVal (Proxy @n)

After trying this I get these errors:

ghci> AS.sizedByteArray skpb

<interactive>:7:1: error: [GHC-39999]
    • No instance for ‘GHC.TypeNats.KnownNat n0’
        arising from a use of ‘it’
    • In the first argument of ‘print’, namely ‘it’
      In a stmt of an interactive GHCi command: print it

and

ghci> keygen $ AS.sizedByteArray skpb

<interactive>:8:1: error: [GHC-64725]
    • Cannot satisfy: 32 <= n0
    • In the first argument of ‘($)’, namely ‘keygen’
      In the expression: keygen $ AS.sizedByteArray skpb
      In an equation for ‘it’: it = keygen $ AS.sizedByteArray skpb

What am I doing wrong?


Solution

  • The definition:

    sk1 :: SecretKey
    sk1 = keygen $ AS.unsafeSizedByteArray @32 @B.ByteString
                   "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
    

    will work fine, but you need to enable DataKinds to use 32 as a type, OverloadedStrings to use a string literal as a ByteString, and TypeApplications to allow those @-marks, which are applications to type-level arguments. So, the following should compile:

    {-# LANGUAGE DataKinds #-}
    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE TypeApplications #-}
    
    import Crypto.BLST
    import qualified Data.ByteArray.Sized as AS
    import qualified Data.ByteString as B
    
    sk1 :: SecretKey
    sk1 = keygen $ AS.unsafeSizedByteArray @32 @B.ByteString
                   "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
    

    More generally, with those extensions enabled, you should be able to construct SizedByteArray values using any of the following methods:

    -- type applications
    sba2 = AS.unsafeSizedByteArray @64 @B.ByteString
           "0123456789012345678901234567890123456789012345678901234567890123"
    
    -- top-level type signature
    sba1 :: AS.SizedByteArray 64 B.ByteString
    sba1 = AS.unsafeSizedByteArray
           "0123456789012345678901234567890123456789012345678901234567890123"
    
    -- in-line type signature
    sba3 = AS.unsafeSizedByteArray
           "0123456789012345678901234567890123456789012345678901234567890123"
           :: AS.SizedByteArray 64 B.ByteString
    

    Note that these sizes aren't checked at compile time, so the following will compile:

    sbabadlen = AS.unsafeSizedByteArray @100 @B.ByteString "too short"
    

    but throw a runtime exception when it's evaluated. The "safe" version will also compile:

    sbabadlen = AS.sizedByteArray @100 @B.ByteString "too short"
    

    and will evaluate to Nothing instead of Just a SizedByteArray.

    Oh, and I should add: there is no simple way of constructing a SizedByteArray without specifying its length explicitly in the source code, so that's why your attempt to have the compiler determine the length based on the actual contents failed with a missing KnownNat instance.