smith-cli-0.0.1: src/Smith/Cli/KeyPair.hs
{-# LANGUAGE OverloadedStrings #-}
module Smith.Cli.KeyPair (
EncodedRSAKeyPair (..)
, Comment (..)
, newRSAKeyPair
, encodeRSAKeyPair
) where
import qualified Crypto.PubKey.OpenSsh as OpenSsh
import qualified Crypto.Types.PubKey.RSA as RSA
import Data.Text (Text)
import qualified Data.Text.Encoding as Text
import qualified OpenSSL.RSA as OpenSSL
import System.IO (IO)
newtype Comment =
Comment {
getComment :: Text
} deriving (Eq, Ord, Show)
data EncodedRSAKeyPair =
EncodedRSAKeyPair {
sshRSAPublicKey :: Text
, sshRSAPrivateKey :: Text
} deriving (Eq, Ord, Show)
-- RSA exponent matches ssh-keygen, see OpenSSL constant RSA_F4.
-- https://github.com/openssh/openssh-portable/blob/7d68e262944c1fff1574600fe0e5e92ec8b398f5/sshkey.c#L1518
newRSAKeyPair :: IO OpenSSL.RSAKeyPair
newRSAKeyPair =
OpenSSL.generateRSAKey'
4096
(0x10001 {- OpenSSL.RSA_F4 = 65537 = 0x10001 -})
encodeRSAKeyPair :: Comment -> OpenSSL.RSAKeyPair -> Maybe EncodedRSAKeyPair
encodeRSAKeyPair comment openssl =
let
public =
RSA.PublicKey {
RSA.public_size = OpenSSL.rsaSize openssl
, RSA.public_n = OpenSSL.rsaN openssl
, RSA.public_e = OpenSSL.rsaE openssl
}
mprivate = do
dP <- OpenSSL.rsaDMP1 openssl
dQ <- OpenSSL.rsaDMQ1 openssl
qinv <- OpenSSL.rsaIQMP openssl
pure $ RSA.PrivateKey {
RSA.private_pub = public
, RSA.private_d = OpenSSL.rsaD openssl
, RSA.private_p = OpenSSL.rsaP openssl
, RSA.private_q = OpenSSL.rsaQ openssl
, RSA.private_dP = dP
, RSA.private_dQ = dQ
, RSA.private_qinv = qinv
}
in
flip fmap mprivate $ \private ->
EncodedRSAKeyPair
(Text.decodeUtf8 . OpenSsh.encodePublic . OpenSsh.OpenSshPublicKeyRsa public . Text.encodeUtf8 . getComment $ comment)
(Text.decodeUtf8 . OpenSsh.encodePrivate $ OpenSsh.OpenSshPrivateKeyRsa private)