module PlutusCore.Crypto.BLS12_381.Error
where

import Data.Bits (testBit)
import Data.ByteString qualified as BS

data BLS12_381_Error
  = HashToCurveDstTooBig -- DSTs can be at most 255 bytes long.
  | BadCompressedData -- A compressed point failed structural (length/flag) validation.
  deriving stock (Int -> BLS12_381_Error -> ShowS
[BLS12_381_Error] -> ShowS
BLS12_381_Error -> String
(Int -> BLS12_381_Error -> ShowS)
-> (BLS12_381_Error -> String)
-> ([BLS12_381_Error] -> ShowS)
-> Show BLS12_381_Error
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> BLS12_381_Error -> ShowS
showsPrec :: Int -> BLS12_381_Error -> ShowS
$cshow :: BLS12_381_Error -> String
show :: BLS12_381_Error -> String
$cshowList :: [BLS12_381_Error] -> ShowS
showList :: [BLS12_381_Error] -> ShowS
Show)

{-| Structural validation of a compressed BLS12-381 point, shared by the C-free
G1 and G2 `uncompress` stubs (@expectedLen@ is 48 for G1, 96 for G2). It checks
exactly what can be checked without field arithmetic: the length, and the three
metadata bits of the leading byte -- the compression bit must be set, and if the
infinity bit is set the encoding must be the canonical @0xc0 00..00@. It
deliberately does NOT verify that the bytes encode a point on the curve or in the
subgroup (that needs blst), so it is weaker than the real `blsUncompress`; but it
rejects the malformed inputs the real decoder rejects on structure alone, rather
than accepting anything. -}
checkCompressed :: Int -> BS.ByteString -> Either BLS12_381_Error BS.ByteString
checkCompressed :: Int -> ByteString -> Either BLS12_381_Error ByteString
checkCompressed Int
expectedLen ByteString
bs
  | ByteString -> Int
BS.length ByteString
bs Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
expectedLen = BLS12_381_Error -> Either BLS12_381_Error ByteString
forall a b. a -> Either a b
Left BLS12_381_Error
BadCompressedData
  | Bool -> Bool
not (Word8 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word8
b0 Int
7)          = BLS12_381_Error -> Either BLS12_381_Error ByteString
forall a b. a -> Either a b
Left BLS12_381_Error
BadCompressedData -- must be the compressed form
  | Word8 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word8
b0 Int
6                = -- point at infinity: must be exactly 0xc0 00..00
      if Word8
b0 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0xc0 Bool -> Bool -> Bool
&& (Word8 -> Bool) -> ByteString -> Bool
BS.all (Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0) (Int -> ByteString -> ByteString
BS.drop Int
1 ByteString
bs)
        then ByteString -> Either BLS12_381_Error ByteString
forall a b. b -> Either a b
Right ByteString
bs
        else BLS12_381_Error -> Either BLS12_381_Error ByteString
forall a b. a -> Either a b
Left BLS12_381_Error
BadCompressedData
  | Bool
otherwise                   = ByteString -> Either BLS12_381_Error ByteString
forall a b. b -> Either a b
Right ByteString
bs
  where
    b0 :: Word8
b0 = HasCallStack => ByteString -> Word8
ByteString -> Word8
BS.head ByteString
bs