<commit_before>module TeleHash.Crypto1a
  (
    cset_1a
  --  init
  , crypt_keygen_1a
  , crypt_loadkey_1a
  ) where

-- | Implement Cypher Suite 1a for TeleHash, as per
-- https://github.com/telehash/telehash.org/blob/master/cs/1a.md

{-

    SHA1 - well trusted and performs well on small devices
    ECC secp160r1 - small key sizes, balance of strong crypto (~1024bit) and still supportable with low cpu
    HMAC-SHA1 - common implementations available for embedded environments
    AES-128-CTR - low impact cipher, many implementations including hardware ones

-}


import Crypto.PubKey.ECC.ECDSA
import Crypto.PubKey.ECC.Generate
import Crypto.Random
import Crypto.Types.PubKey.ECC
import Data.ByteString.Base64
import Data.List
import Data.Maybe
import Data.Word
import Crypto.Number.Serialize
import TeleHash.Utils
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Data.ByteString as B
import qualified Data.ByteString.Base16 as B16
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.UTF8 as BU
import qualified Data.Digest.Pure.SHA as SHA

curve = getCurveByName SEC_p160r1

-- ---------------------------------------------------------------------

cset_1a = CS
  {
  csLoadkey = crypt_loadkey_1a
  }
-- ---------------------------------------------------------------------

-- | Generate base64 encoded public and matching private keys
crypt_keygen_1a :: CPRG t => t -> ((B.ByteString, B.ByteString), t)
crypt_keygen_1a g = ((pub_b64,priv_b64),g')
  where
    ((pub,priv) ,g') = generate g curve
    (PublicKey _ pubk) = pub
    (PrivateKey _ privk) = priv

    pub_b64 = encode $ pointTow8s pubk

    (Just p20) = i2ospOf 20 privk
    priv_b64 = encode p20

{-
int crypt_keygen_1a(packet_t p)
{
  char b64[ECC_BYTES*4];
  uint8_t id_private[ECC_BYTES], id_public[ECC_BYTES*2];

  // create line ephemeral key
  ecc_make_key(id_public, id_private);

  base64enc(b64,id_public,ECC_BYTES*2);
  packet_set_str(p,"1a",b64);

  base64enc(b64,id_private,ECC_BYTES);
  packet_set_str(p,"1a_secret",b64);

  return 0;
}

-}

-- ---------------------------------------------------------------------

crypt_loadkey_1a :: Maybe HashContainer -> String -> Maybe String
   -> TeleHash (Maybe HashContainer)
crypt_loadkey_1a mhc pub mpriv = do
  -- base64 decode it
  let mbs = decode $ B8.pack pub
  case mbs of
    Left _err -> do
      logT $ "invalid public key b64decode failed:" ++ pub
      return Nothing
    Right bs -> do
      if B8.length bs /= 40
        then do logT $ "invalid public key wrong len:" ++ pub
                return Nothing
        else do
          -- convert the ByteString into a pair of Integer
          let (b1,b2) = B.splitAt 20 bs
              i1 = os2ip b1
              i2 = os2ip b2
          -- create the public key
              pubkey = PublicKey curve (Point i1 i2)
              hc' = case mpriv of
                Nothing -> mhc
                Just priv -> hcp
                  where
                    mbsp = decode $ B8.pack priv
                    hcp = case mbsp of
                      Left _err -> mhc
                      Right bsp -> Just $ hcn {hcPrivate = Just $ Private1a privkey}
                        where
                          i = os2ip bsp
                          privkey = PrivateKey curve i
                          newPartsPub = ("1a",pub)
                          newParts = case mpriv of
                            Nothing -> [newPartsPub]
                            Just pr -> [("1a_secret",pr),newPartsPub]
                          hcn = case mhc of
                            Just h  -> h  { hcHashName = parts2hn (hcParts h ++ newParts)
                                          , hcHexName = mkHashFromBS bs
                                          , hcParts = (hcParts h ++ newParts)
                                          , hcCsid = "1a"
                                          , hcKey = pub
                                          , hcPublic = Public1a pubkey
                                          , hcPrivate = Just $ Private1a privkey
                                          }
                            Nothing -> HC { hcHashName = parts2hn newParts
                                          , hcHexName =  mkHashFromBS bs
                                          , hcParts = newParts
                                          , hcCsid = "1a"
                                          , hcKey = pub
                                          , hcPublic = Public1a pubkey
                                          , hcPrivate = Just $ Private1a privkey
                                          }

          logT $ "crypt_loadkey_1a:parts=" ++ show (hcParts $ fromJust hc')
          return hc'

mkHashFromBS :: B8.ByteString -> Hash
mkHashFromBS bs =
  let
    digest = SHA.sha1 $ BL.fromChunks [bs]
  in
   Hash (show digest)

mkHashFromB64 :: String -> Hash
mkHashFromB64 str =
  let
    digest = SHA.sha1 $ BL.fromChunks [B8.pack str]
  in
   -- B64.encode $ BL.unpack $ SHA.bytestringDigest digest
   Hash (show digest)

testhash :: IO ()
testhash = do
  let b64 = "o0UL/D6qQ+dcSX7hCoyMjLDYeA6dNScZ+YY/fcX4fyCtsSO2u9L5Lg=="
  let Right bs = decode $ B8.pack b64
  let digest = SHA.sha1 $ BL.fromChunks [bs]
  putStrLn (show digest)<commit_msg>Load id for crypto 1a<commit_after>module TeleHash.Crypto1a
  (
    cset_1a
  --  init
  , crypt_keygen_1a
  , crypt_loadkey_1a
  ) where

-- | Implement Cypher Suite 1a for TeleHash, as per
-- https://github.com/telehash/telehash.org/blob/master/cs/1a.md

{-

    SHA1 - well trusted and performs well on small devices
    ECC secp160r1 - small key sizes, balance of strong crypto (~1024bit) and still supportable with low cpu
    HMAC-SHA1 - common implementations available for embedded environments
    AES-128-CTR - low impact cipher, many implementations including hardware ones

-}


import Crypto.PubKey.ECC.ECDSA
import Crypto.PubKey.ECC.Generate
import Crypto.Random
import Crypto.Types.PubKey.ECC
import Data.ByteString.Base64
import Data.List
import Data.Maybe
import Data.Word
import Crypto.Number.Serialize
import TeleHash.Utils
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Data.ByteString as B
import qualified Data.ByteString.Base16 as B16
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.UTF8 as BU
import qualified Data.Digest.Pure.SHA as SHA

curve = getCurveByName SEC_p160r1

-- ---------------------------------------------------------------------

cset_1a = CS
  {
  csLoadkey = crypt_loadkey_1a
  }
-- ---------------------------------------------------------------------

-- | Generate base64 encoded public and matching private keys
crypt_keygen_1a :: CPRG t => t -> ((B.ByteString, B.ByteString), t)
crypt_keygen_1a g = ((pub_b64,priv_b64),g')
  where
    ((pub,priv) ,g') = generate g curve
    (PublicKey _ pubk) = pub
    (PrivateKey _ privk) = priv

    pub_b64 = encode $ pointTow8s pubk

    (Just p20) = i2ospOf 20 privk
    priv_b64 = encode p20

{-
int crypt_keygen_1a(packet_t p)
{
  char b64[ECC_BYTES*4];
  uint8_t id_private[ECC_BYTES], id_public[ECC_BYTES*2];

  // create line ephemeral key
  ecc_make_key(id_public, id_private);

  base64enc(b64,id_public,ECC_BYTES*2);
  packet_set_str(p,"1a",b64);

  base64enc(b64,id_private,ECC_BYTES);
  packet_set_str(p,"1a_secret",b64);

  return 0;
}

-}

-- ---------------------------------------------------------------------

crypt_loadkey_1a :: Maybe HashContainer -> String -> Maybe String
   -> TeleHash (Maybe HashContainer)
crypt_loadkey_1a mhc pub mpriv = do
  -- base64 decode it
  let mbs = decode $ B8.pack pub
  case mbs of
    Left _err -> do
      logT $ "invalid public key b64decode failed:" ++ pub
      return Nothing
    Right bs -> do
      if B8.length bs /= 40
        then do logT $ "invalid public key wrong len:" ++ pub
                return Nothing
        else do
          -- convert the ByteString into a pair of Integer
          let (b1,b2) = B.splitAt 20 bs
              i1 = os2ip b1
              i2 = os2ip b2
          -- create the public key
              pubkey = PublicKey curve (Point i1 i2)
              hc' = case mpriv of
                Nothing -> mhc
                Just priv -> hcp
                  where
                    mbsp = decode $ B8.pack priv
                    hcp = case mbsp of
                      Left _err -> mhc
                      Right bsp -> Just $ hcn {hcPrivate = Just $ Private1a privkey}
                        where
                          i = os2ip bsp
                          privkey = PrivateKey curve i
                          hexName = mkHashFromBS bs
                          newParts = [("1a",unHash hexName)]
                          hcn = case mhc of
                            Just h  -> h  { hcHashName = parts2hn (hcParts h ++ newParts)
                                          , hcHexName = hexName
                                          , hcParts = (hcParts h ++ newParts)
                                          , hcCsid = "1a"
                                          , hcKey = pub
                                          , hcPublic = Public1a pubkey
                                          , hcPrivate = Just $ Private1a privkey
                                          }
                            Nothing -> HC { hcHashName = parts2hn newParts
                                          , hcHexName = hexName
                                          , hcParts = newParts
                                          , hcCsid = "1a"
                                          , hcKey = pub
                                          , hcPublic = Public1a pubkey
                                          , hcPrivate = Just $ Private1a privkey
                                          }

          logT $ "crypt_loadkey_1a:parts=" ++ show (hcParts $ fromJust hc')
          return hc'

mkHashFromBS :: B8.ByteString -> Hash
mkHashFromBS bs =
  let
    digest = SHA.sha1 $ BL.fromChunks [bs]
  in
   Hash (show digest)

mkHashFromB64 :: String -> Hash
mkHashFromB64 str =
  let
    digest = SHA.sha1 $ BL.fromChunks [B8.pack str]
  in
   -- B64.encode $ BL.unpack $ SHA.bytestringDigest digest
   Hash (show digest)

testhash :: IO ()
testhash = do
  let b64 = "o0UL/D6qQ+dcSX7hCoyMjLDYeA6dNScZ+YY/fcX4fyCtsSO2u9L5Lg=="
  let Right bs = decode $ B8.pack b64
  let digest = SHA.sha1 $ BL.fromChunks [bs]
  putStrLn (show digest)
{'fraction_non_alphanumeric': 0.11284672914891603, 'fraction_numerical': 0.02688630124017798, 'mean_word_length': 1.4402864402864404, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 35, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 52, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6781361', 'n_tokens_mistral': 3204, 'n_tokens_neox': 3034, 'n_words': 852}