es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

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?

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Sí, debes evitar usar unsafePerformIO tanto como sea posible. En este caso, en lugar de usar unsafePerformIO, puedes utilizar la función join del módulo Control.Monad para aplanar el monad IO anidado.

    Así es como puedes modificar la función mkRsaKey para que devuelva IO (Maybe RsaKey):

    mkRsaKey :: String -> IO (Maybe RsaKey)
    mkRsaKey privateKey = do
      someOpenSslKey <- readPrivateKey privateKey PwNone
      maybeRsaKeyIO <- openSslKeyToRsaKey <$> toKeyPair someOpenSslKey
      return $ join maybeRsaKeyIO
    

    Explicación:

    • Primero extraemos el RSAKeyPair de privateKey utilizando readPrivateKey.
    • Luego lo convertimos a un Maybe RsaKey envuelto en un monad IO utilizando openSslKeyToRsaKey.
    • Finalmente, utilizamos join para aplanar el tipo resultante Maybe (IO (Maybe RsaKey)) a IO (Maybe RsaKey). Esto se debe a que join “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 de mkRsaKey.

Comments are closed.