-- | RSA key operations for libp2p peer identity, using crypton.
--
-- Wire formats follow the libp2p peer-ids spec:
-- - Public key: DER-encoded SubjectPublicKeyInfo (PKIX).
-- - Private key: DER-encoded PKCS#1 RSAPrivateKey.
-- - Signatures: RSASSA-PKCS1-v1_5 over SHA-256.
--
-- All functions operate on raw 'ByteString' so this module does not depend on
-- "LibP2P.Crypto.Key" (avoids an import cycle with the dispatcher).
module LibP2P.Crypto.RSA
  ( generate
  , sign
  , verify
  , derivePublicKey
  ) where

import Crypto.Hash.Algorithms (SHA256 (..))
import Crypto.Number.Basic (numBytes)
import qualified Crypto.PubKey.RSA as RSA
import qualified Crypto.PubKey.RSA.PKCS15 as PKCS15
import Data.ASN1.BinaryEncoding (DER (..))
import Data.ASN1.Encoding (decodeASN1', encodeASN1')
import Data.ASN1.Types (ASN1 (..), ASN1ConstructionType (..), fromASN1, toASN1)
import Data.ByteString (ByteString)
import Data.X509 (PubKey (PubKeyRSA))

-- | Public exponent used for generated keys (65537).
publicExponent :: Integer
publicExponent :: Integer
publicExponent = Integer
0x10001

-- | Modulus size in bytes for locally generated keys (2048-bit).
-- Imported keys may use any modulus size; see 'decodePrivateKey'.
keySizeBytes :: Int
keySizeBytes :: Int
keySizeBytes = Int
256

-- | Generate a new RSA key pair, returning (public SPKI DER, private PKCS#1 DER).
generate :: IO (ByteString, ByteString)
generate :: IO (ByteString, ByteString)
generate = do
  (pub, priv) <- Int -> Integer -> IO (PublicKey, PrivateKey)
forall (m :: * -> *).
MonadRandom m =>
Int -> Integer -> m (PublicKey, PrivateKey)
RSA.generate Int
keySizeBytes Integer
publicExponent
  pure (encodePublicKey pub, encodePrivateKey priv)

-- | Sign a message with a PKCS#1-DER private key (RSASSA-PKCS1-v1_5, SHA-256).
-- Deterministic (no blinder), so it is pure.
sign :: ByteString -> ByteString -> Either String ByteString
sign :: ByteString -> ByteString -> Either String ByteString
sign ByteString
privDer ByteString
msg = do
  priv <- ByteString -> Either String PrivateKey
decodePrivateKey ByteString
privDer
  case PKCS15.sign Nothing (Just SHA256) priv msg of
    Left Error
err -> String -> Either String ByteString
forall a b. a -> Either a b
Left (String -> Either String ByteString)
-> String -> Either String ByteString
forall a b. (a -> b) -> a -> b
$ String
"RSA.sign: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Error -> String
forall a. Show a => a -> String
show Error
err
    Right ByteString
sig -> ByteString -> Either String ByteString
forall a b. b -> Either a b
Right ByteString
sig

-- | Verify a signature against an SPKI-DER public key (RSASSA-PKCS1-v1_5, SHA-256).
verify :: ByteString -> ByteString -> ByteString -> Bool
verify :: ByteString -> ByteString -> ByteString -> Bool
verify ByteString
pubDer ByteString
msg ByteString
sig =
  case ByteString -> Either String PublicKey
decodePublicKey ByteString
pubDer of
    Left String
_ -> Bool
False
    Right PublicKey
pub -> Maybe SHA256 -> PublicKey -> ByteString -> ByteString -> Bool
forall hashAlg.
HashAlgorithmASN1 hashAlg =>
Maybe hashAlg -> PublicKey -> ByteString -> ByteString -> Bool
PKCS15.verify (SHA256 -> Maybe SHA256
forall a. a -> Maybe a
Just SHA256
SHA256) PublicKey
pub ByteString
msg ByteString
sig

-- | Derive the SPKI-DER public key from a PKCS#1-DER private key.
derivePublicKey :: ByteString -> Either String ByteString
derivePublicKey :: ByteString -> Either String ByteString
derivePublicKey ByteString
privDer = PublicKey -> ByteString
encodePublicKey (PublicKey -> ByteString)
-> (PrivateKey -> PublicKey) -> PrivateKey -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PrivateKey -> PublicKey
RSA.private_pub (PrivateKey -> ByteString)
-> Either String PrivateKey -> Either String ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ByteString -> Either String PrivateKey
decodePrivateKey ByteString
privDer

-- | Encode an RSA public key as DER SubjectPublicKeyInfo.
encodePublicKey :: RSA.PublicKey -> ByteString
encodePublicKey :: PublicKey -> ByteString
encodePublicKey PublicKey
pub = DER -> [ASN1] -> ByteString
forall a. ASN1Encoding a => a -> [ASN1] -> ByteString
encodeASN1' DER
DER (PubKey -> ASN1S
forall a. ASN1Object a => a -> ASN1S
toASN1 (PublicKey -> PubKey
PubKeyRSA PublicKey
pub) [])

-- | Decode a DER SubjectPublicKeyInfo into an RSA public key.
decodePublicKey :: ByteString -> Either String RSA.PublicKey
decodePublicKey :: ByteString -> Either String PublicKey
decodePublicKey ByteString
bs =
  case DER -> ByteString -> Either ASN1Error [ASN1]
forall a.
ASN1Decoding a =>
a -> ByteString -> Either ASN1Error [ASN1]
decodeASN1' DER
DER ByteString
bs of
    Left ASN1Error
err -> String -> Either String PublicKey
forall a b. a -> Either a b
Left (String -> Either String PublicKey)
-> String -> Either String PublicKey
forall a b. (a -> b) -> a -> b
$ String
"RSA.decodePublicKey: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> ASN1Error -> String
forall a. Show a => a -> String
show ASN1Error
err
    Right [ASN1]
asn1 -> case [ASN1] -> Either String (PubKey, [ASN1])
forall a. ASN1Object a => [ASN1] -> Either String (a, [ASN1])
fromASN1 [ASN1]
asn1 of
      Right (PubKeyRSA PublicKey
pub, [ASN1]
_) -> PublicKey -> Either String PublicKey
forall a b. b -> Either a b
Right PublicKey
pub
      Right (PubKey, [ASN1])
_ -> String -> Either String PublicKey
forall a b. a -> Either a b
Left String
"RSA.decodePublicKey: not an RSA public key"
      Left String
err -> String -> Either String PublicKey
forall a b. a -> Either a b
Left (String -> Either String PublicKey)
-> String -> Either String PublicKey
forall a b. (a -> b) -> a -> b
$ String
"RSA.decodePublicKey: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
err

-- | Encode an RSA private key as DER PKCS#1 RSAPrivateKey.
encodePrivateKey :: RSA.PrivateKey -> ByteString
encodePrivateKey :: PrivateKey -> ByteString
encodePrivateKey PrivateKey
priv =
  DER -> [ASN1] -> ByteString
forall a. ASN1Encoding a => a -> [ASN1] -> ByteString
encodeASN1' DER
DER ([ASN1] -> ByteString) -> [ASN1] -> ByteString
forall a b. (a -> b) -> a -> b
$
    [ ASN1ConstructionType -> ASN1
Start ASN1ConstructionType
Sequence
    , Integer -> ASN1
IntVal Integer
0 -- version (two-prime)
    , Integer -> ASN1
IntVal (PublicKey -> Integer
RSA.public_n (PrivateKey -> PublicKey
RSA.private_pub PrivateKey
priv))
    , Integer -> ASN1
IntVal (PublicKey -> Integer
RSA.public_e (PrivateKey -> PublicKey
RSA.private_pub PrivateKey
priv))
    , Integer -> ASN1
IntVal (PrivateKey -> Integer
RSA.private_d PrivateKey
priv)
    , Integer -> ASN1
IntVal (PrivateKey -> Integer
RSA.private_p PrivateKey
priv)
    , Integer -> ASN1
IntVal (PrivateKey -> Integer
RSA.private_q PrivateKey
priv)
    , Integer -> ASN1
IntVal (PrivateKey -> Integer
RSA.private_dP PrivateKey
priv)
    , Integer -> ASN1
IntVal (PrivateKey -> Integer
RSA.private_dQ PrivateKey
priv)
    , Integer -> ASN1
IntVal (PrivateKey -> Integer
RSA.private_qinv PrivateKey
priv)
    , ASN1ConstructionType -> ASN1
End ASN1ConstructionType
Sequence
    ]

-- | Decode a DER PKCS#1 RSAPrivateKey into an RSA private key.
decodePrivateKey :: ByteString -> Either String RSA.PrivateKey
decodePrivateKey :: ByteString -> Either String PrivateKey
decodePrivateKey ByteString
bs =
  case DER -> ByteString -> Either ASN1Error [ASN1]
forall a.
ASN1Decoding a =>
a -> ByteString -> Either ASN1Error [ASN1]
decodeASN1' DER
DER ByteString
bs of
    Left ASN1Error
err -> String -> Either String PrivateKey
forall a b. a -> Either a b
Left (String -> Either String PrivateKey)
-> String -> Either String PrivateKey
forall a b. (a -> b) -> a -> b
$ String
"RSA.decodePrivateKey: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> ASN1Error -> String
forall a. Show a => a -> String
show ASN1Error
err
    Right
      ( Start ASN1ConstructionType
Sequence
          : IntVal Integer
_ver
          : IntVal Integer
n
          : IntVal Integer
e
          : IntVal Integer
d
          : IntVal Integer
p
          : IntVal Integer
q
          : IntVal Integer
dP
          : IntVal Integer
dQ
          : IntVal Integer
qinv
          : End ASN1ConstructionType
Sequence
          : [ASN1]
_
        ) ->
        PrivateKey -> Either String PrivateKey
forall a b. b -> Either a b
Right
          RSA.PrivateKey
            { private_pub :: PublicKey
RSA.private_pub =
                -- The modulus size must be derived from n itself: keys from
                -- other implementations are not necessarily 2048-bit.
                RSA.PublicKey {public_size :: Int
RSA.public_size = Integer -> Int
numBytes Integer
n, public_n :: Integer
RSA.public_n = Integer
n, public_e :: Integer
RSA.public_e = Integer
e}
            , private_d :: Integer
RSA.private_d = Integer
d
            , private_p :: Integer
RSA.private_p = Integer
p
            , private_q :: Integer
RSA.private_q = Integer
q
            , private_dP :: Integer
RSA.private_dP = Integer
dP
            , private_dQ :: Integer
RSA.private_dQ = Integer
dQ
            , private_qinv :: Integer
RSA.private_qinv = Integer
qinv
            }
    Right [ASN1]
_ -> String -> Either String PrivateKey
forall a b. a -> Either a b
Left String
"RSA.decodePrivateKey: unexpected ASN.1 structure"