Reducir IO (Maybe (IO (Maybe a))) a IO (Maybe a).
Tengo una función que lee una clave Rsa con la función readPrivateKey
de HsOpenSsl, desafortunadamente la firma de mi función es esta String -> IO (Maybe (IO Maybe RsaKey))
. Necesito el formato PEM y una clave Cryptonite.RSA y escribí la función mkRsaKey
para hacer eso a partir de una cadena en formato PEM.
Aquí está el código:
import qualified Crypto.PubKey.RSA as Rsa -- desde cryptonite
import OpenSSL.EVP.PKey -- desde HsOpenSSL
import OpenSSL.PEM -- desde HsOpenSSL
import OpenSSL.RSA -- desde HsOpenSSL
import Prelude
data RsaKey = RsaKey
{ rsaKeyCryptoniteKey :: Rsa.PrivateKey,
rsaKeyStringRepr :: String
}
deriving (Show)
openSslKeyToCryptoniteKey :: RSAKeyPair -> Maybe Rsa.PrivateKey
openSslKeyToCryptoniteKey key = do
let d = rsaD key
let p = rsaP key
let q = rsaQ key
let mdP = rsaDMP1 key
let mdQ = rsaDMQ1 key
let mqinv = rsaIQMP key
let size = rsaSize key
let n = rsaN key
let e = rsaE key
dP <- mdP
dQ <- mdQ
qinv <- mqinv
let pub = Rsa.PublicKey size n e
return $ Rsa.PrivateKey pub d p q dP dQ qinv
openSslKeyToRsaKey :: RSAKeyPair -> IO (Maybe RsaKey)
openSslKeyToRsaKey key = do
stringRepr <- writePublicKey key
let maybeCryptoKey = openSslKeyToCryptoniteKey key
return $ do
cryptoKey <- maybeCryptoKey
return $ RsaKey cryptoKey stringRepr
mkRsaKey :: String -> IO (Maybe (IO (Maybe RsaKey)))
mkRsaKey privateKey = do
someOpenSslKey <- readPrivateKey privateKey PwNone
let openSslKey = toKeyPair someOpenSslKey
return $ openSslKeyToRsaKey <$> openSslKey
Ahora, como puedes ver, la firma del tipo en mi opinión no es óptima, me gustaría tener IO (Maybe RsaKey)
. ¿Cómo puedo lograr esto?
EDIT:
De hecho, logré hacerlo, pero estoy usando unsafePerformIO
:
mkRsaKey :: String -> IO (Maybe RsaKey)
mkRsaKey privateKey = do
someOpenSslKey <- readPrivateKey privateKey PwNone
return $ do
openSslKey <- toKeyPair someOpenSslKey
unsafePerformIO (openSslKeyToRsaKey $ openSslKey)
Según tengo entendido, nunca se debe usar unsafePerformIO
. ¿Habría alguna forma de hacer esto sin él?
davy.ai
Sí, debes evitar usar
unsafePerformIO
tanto como sea posible. En este caso, en lugar de usarunsafePerformIO
, puedes utilizar la funciónjoin
del móduloControl.Monad
para aplanar el monadIO
anidado.Así es como puedes modificar la función
mkRsaKey
para que devuelvaIO (Maybe RsaKey)
:Explicación:
RSAKeyPair
deprivateKey
utilizandoreadPrivateKey
.Maybe RsaKey
envuelto en un monadIO
utilizandoopenSslKeyToRsaKey
.join
para aplanar el tipo resultanteMaybe (IO (Maybe RsaKey))
aIO (Maybe RsaKey)
. Esto se debe a quejoin
“fusiona” dos monads anidados del mismo tipo en uno solo, reduciendo el nivel de anidamiento.De esta manera, no necesitas usar
unsafePerformIO
y tienes una implementación segura demkRsaKey
.