From 0c866105d2964cd44984831ae4bde2461e538e15 Mon Sep 17 00:00:00 2001 From: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com> Date: Thu, 23 Dec 2021 21:20:41 +0400 Subject: [PATCH] chain of two certificates - offline (identity) and online; switch certificates to v3 (#238) --- apps/smp-server/Main.hs | 103 +++++++++++++++++----- src/Simplex/Messaging/Agent.hs | 4 +- src/Simplex/Messaging/Agent/Env/SQLite.hs | 4 +- src/Simplex/Messaging/Server/Env/STM.hs | 5 +- src/Simplex/Messaging/Transport.hs | 17 ++-- tests/AgentTests.hs | 2 +- tests/SMPAgentClient.hs | 7 +- tests/SMPClient.hs | 9 +- tests/fixtures/.gitignore | 1 + tests/fixtures/README.md | 25 ++++++ tests/fixtures/ca.crt | 11 +++ tests/fixtures/ca.key | 4 + tests/fixtures/example.crt | 9 -- tests/fixtures/example.key | 4 - tests/fixtures/openssl.cnf | 16 ++++ tests/fixtures/server.crt | 11 +++ tests/fixtures/server.key | 4 + 17 files changed, 179 insertions(+), 57 deletions(-) create mode 100644 tests/fixtures/.gitignore create mode 100644 tests/fixtures/README.md create mode 100644 tests/fixtures/ca.crt create mode 100644 tests/fixtures/ca.key delete mode 100644 tests/fixtures/example.crt delete mode 100644 tests/fixtures/example.key create mode 100644 tests/fixtures/openssl.cnf create mode 100644 tests/fixtures/server.crt create mode 100644 tests/fixtures/server.key diff --git a/apps/smp-server/Main.hs b/apps/smp-server/Main.hs index 310369973..283232622 100644 --- a/apps/smp-server/Main.hs +++ b/apps/smp-server/Main.hs @@ -22,7 +22,7 @@ import Simplex.Messaging.Server.Env.STM import Simplex.Messaging.Server.StoreLog (StoreLog, openReadStoreLog, storeLogFilePath) import Simplex.Messaging.Transport (ATransport (..), TLS, Transport (..), encodeFingerprint, loadFingerprint) import Simplex.Messaging.Transport.WebSockets (WS) -import System.Directory (createDirectoryIfMissing, doesFileExist, removeFile) +import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, removeDirectoryRecursive, removeFile) import System.Exit (exitFailure) import System.FilePath (combine) import System.IO (IOMode (..), hFlush, stdout) @@ -43,6 +43,7 @@ serverConfig = -- below parameters are set based on ini file /etc/opt/simplex/smp-server.ini transports = undefined, storeLog = undefined, + caCertificateFile = undefined, serverPrivateKeyFile = undefined, serverCertificateFile = undefined } @@ -56,12 +57,19 @@ cfgDir = "/etc/opt/simplex" logDir :: FilePath logDir = "/var/opt/simplex" +-- TODO remove file paths from ini defaultStoreLogFile :: FilePath defaultStoreLogFile = combine logDir "smp-server-store.log" iniFile :: FilePath iniFile = combine cfgDir "smp-server.ini" +caPrivateKeyFile :: FilePath +caPrivateKeyFile = combine cfgDir "ca.key" + +defaultCACertificateFile :: FilePath +defaultCACertificateFile = combine cfgDir "ca.crt" + defaultPrivateKeyFile :: FilePath defaultPrivateKeyFile = combine cfgDir "server.key" @@ -82,12 +90,14 @@ main = do putStrLn "Error: server is already initialized. Start it with `smp-server start` command" fingerprint <- loadSavedFingerprint printConfig cfg fingerprint + checkCAPrivateKeyFile exitFailure Left _ -> do cfg <- initializeServer opts putStrLn "Server was initialized. Start it with `smp-server start` command" fingerprint <- loadSavedFingerprint printConfig cfg fingerprint + warnCAPrivateKeyFile ServerStart -> runExceptT (getConfig opts) >>= \case Right cfg -> runServer cfg @@ -111,9 +121,9 @@ getConfig opts = do pure $ makeConfig ini storeLog makeConfig :: IniOpts -> Maybe (StoreLog 'ReadMode) -> ServerConfig -makeConfig IniOpts {serverPort, enableWebsockets, serverPrivateKeyFile, serverCertificateFile} storeLog = +makeConfig IniOpts {serverPort, enableWebsockets, caCertificateFile, serverPrivateKeyFile, serverCertificateFile} storeLog = let transports = (serverPort, transport @TLS) : [("80", transport @WS) | enableWebsockets] - in serverConfig {transports, storeLog, serverPrivateKeyFile, serverCertificateFile} + in serverConfig {transports, storeLog, caCertificateFile, serverPrivateKeyFile, serverCertificateFile} printConfig :: ServerConfig -> String -> IO () printConfig ServerConfig {storeLog} fingerprint = do @@ -126,8 +136,8 @@ initializeServer :: ServerOpts -> IO ServerConfig initializeServer opts = do createDirectoryIfMissing True cfgDir ini <- createIni opts - createKeyAndCertificate ini opts - saveFingerprint $ serverCertificateFile (ini :: IniOpts) + createX509 ini opts + saveFingerprint $ caCertificateFile (ini :: IniOpts) storeLog <- openStoreLog opts ini pure $ makeConfig ini storeLog @@ -136,38 +146,59 @@ runServer cfg = do savedFingerprint <- loadSavedFingerprint checkSavedFingerprint savedFingerprint printConfig cfg savedFingerprint + checkCAPrivateKeyFile forM_ (transports cfg) $ \(port, ATransport t) -> putStrLn $ "listening on port " <> port <> " (" <> transportName t <> ")" runSMPServer cfg where checkSavedFingerprint :: String -> IO () checkSavedFingerprint savedFingerprint = do - fingerprint <- loadFingerprint $ serverCertificateFile (cfg :: ServerConfig) + fingerprint <- loadFingerprint $ caCertificateFile (cfg :: ServerConfig) if savedFingerprint == (B.unpack . encodeFingerprint) fingerprint then putStrLn "stored fingerprint is valid" else putStrLn "stored fingerprint is invalid" >> exitFailure +checkCAPrivateKeyFile :: IO () +checkCAPrivateKeyFile = + doesFileExist caPrivateKeyFile >>= (`when` (alert >> warnCAPrivateKeyFile)) + where + alert = putStrLn $ "WARNING: " <> caPrivateKeyFile <> " is present on the server!" + +warnCAPrivateKeyFile :: IO () +warnCAPrivateKeyFile = + putStrLn $ + "----------\n\ + \We highly recommend to remove CA private key file from the server and keep it securely in place of your choosing.\n\ + \In case server's TLS credential is compromised you will be able to regenerate it using this key,\n\ + \thus keeping server's identity and allowing clients to keep established connections. Key location:\n" + <> caPrivateKeyFile + <> "\n----------" + deleteServer :: IO () deleteServer = do ini <- runExceptT readIni deleteIfExists iniFile case ini of - Right IniOpts {storeLogFile, serverPrivateKeyFile, serverCertificateFile} -> do + -- TODO delete only cfgDir and logDir once file paths are removed from ini + Right IniOpts {storeLogFile, caCertificateFile, serverPrivateKeyFile, serverCertificateFile} -> do + deleteDirIfExists cfgDir deleteIfExists storeLogFile + deleteIfExists caCertificateFile deleteIfExists serverPrivateKeyFile deleteIfExists serverCertificateFile - deleteIfExists fingerprintFile Left _ -> do + deleteDirIfExists cfgDir deleteIfExists defaultStoreLogFile + deleteIfExists defaultCACertificateFile deleteIfExists defaultPrivateKeyFile deleteIfExists defaultCertificateFile - deleteIfExists fingerprintFile data IniOpts = IniOpts { enableStoreLog :: Bool, storeLogFile :: FilePath, serverPort :: ServiceName, enableWebsockets :: Bool, + caCertificateFile :: FilePath, serverPrivateKeyFile :: FilePath, serverCertificateFile :: FilePath } @@ -180,9 +211,10 @@ readIni = do storeLogFile = opt defaultStoreLogFile "STORE_LOG" "file" ini serverPort = opt defaultServerPort "TRANSPORT" "port" ini enableWebsockets = (== Right "on") $ lookupValue "TRANSPORT" "websockets" ini + caCertificateFile = opt defaultCACertificateFile "TRANSPORT" "ca_certificate_file" ini serverPrivateKeyFile = opt defaultPrivateKeyFile "TRANSPORT" "private_key_file" ini serverCertificateFile = opt defaultCertificateFile "TRANSPORT" "certificate_file" ini - pure IniOpts {enableStoreLog, storeLogFile, serverPort, enableWebsockets, serverPrivateKeyFile, serverCertificateFile} + pure IniOpts {enableStoreLog, storeLogFile, serverPort, enableWebsockets, caCertificateFile, serverPrivateKeyFile, serverCertificateFile} where opt :: String -> Text -> Text -> Ini -> String opt def section key ini = either (const def) T.unpack $ lookupValue section key ini @@ -203,6 +235,9 @@ createIni ServerOpts {enableStoreLog} = do <> defaultStoreLogFile <> "\n\n\ \[TRANSPORT]\n\n\ + \# ca_certificate_file: " + <> defaultCACertificateFile + <> "\n\ \# private_key_file: " <> defaultPrivateKeyFile <> "\n\ @@ -219,28 +254,47 @@ createIni ServerOpts {enableStoreLog} = do storeLogFile = defaultStoreLogFile, serverPort = defaultServerPort, enableWebsockets = True, + caCertificateFile = defaultCACertificateFile, serverPrivateKeyFile = defaultPrivateKeyFile, serverCertificateFile = defaultCertificateFile } --- To generate self-signed certificate: --- https://blog.pinterjann.is/ed25519-certificates.html - -createKeyAndCertificate :: IniOpts -> ServerOpts -> IO () -createKeyAndCertificate IniOpts {serverPrivateKeyFile, serverCertificateFile} ServerOpts {pubkeyAlgorihtm} = do +createX509 :: IniOpts -> ServerOpts -> IO () +createX509 IniOpts {caCertificateFile, serverPrivateKeyFile, serverCertificateFile} ServerOpts {pubkeyAlgorihtm} = do + createOpensslConf + -- CA certificate (identity/offline) + run $ "openssl genpkey -algorithm " <> pubkeyAlgorihtm <> " -out " <> caPrivateKeyFile + run $ "openssl req -new -x509 -days 999999 -config " <> opensslConfFile <> " -extensions v3_ca -key " <> caPrivateKeyFile <> " -out " <> caCertificateFile + -- server certificate (online) run $ "openssl genpkey -algorithm " <> pubkeyAlgorihtm <> " -out " <> serverPrivateKeyFile - run $ "openssl req -new -key " <> serverPrivateKeyFile <> " -subj \"/CN=localhost\" -out " <> csrPath - run $ "openssl x509 -req -days 999999 -in " <> csrPath <> " -signkey " <> serverPrivateKeyFile <> " -out " <> serverCertificateFile - run $ "rm " <> csrPath + run $ "openssl req -new -config " <> opensslConfFile <> " -reqexts v3_req -key " <> serverPrivateKeyFile <> " -out " <> serverCsrFile + run $ "openssl x509 -req -days 999999 -copy_extensions copy -in " <> serverCsrFile <> " -CA " <> caCertificateFile <> " -CAkey " <> caPrivateKeyFile <> " -out " <> serverCertificateFile where - run :: String -> IO () run cmd = void $ readCreateProcess (shell cmd) "" - csrPath :: String - csrPath = combine cfgDir "localhost.csr" + opensslConfFile = combine cfgDir "openssl.cnf" + serverCsrFile = combine cfgDir "server.csr" + createOpensslConf :: IO () + createOpensslConf = + -- TODO revise https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.3, https://www.rfc-editor.org/rfc/rfc3279#section-2.3.5 + writeFile + opensslConfFile + "[req]\n\ + \distinguished_name = req_distinguished_name\n\ + \prompt = no\n\n\ + \[req_distinguished_name]\n\ + \CN = localhost\n\n\ + \[v3_ca]\n\ + \subjectKeyIdentifier = hash\n\ + \authorityKeyIdentifier = keyid:always\n\ + \basicConstraints = critical,CA:true\n\n\ + \[v3_req]\n\ + \basicConstraints = CA:FALSE\n\ + \keyUsage = digitalSignature, nonRepudiation, keyAgreement\n\ + \extendedKeyUsage = serverAuth\n" saveFingerprint :: FilePath -> IO () -saveFingerprint serverCertificateFile = do - fingerprint <- loadFingerprint serverCertificateFile +saveFingerprint caCertificateFile = do + fingerprint <- loadFingerprint caCertificateFile writeFile fingerprintFile $ (B.unpack . encodeFingerprint) fingerprint <> "\n" loadSavedFingerprint :: IO String @@ -256,6 +310,9 @@ fileExists path = do deleteIfExists :: FilePath -> IO () deleteIfExists path = doesFileExist path >>= (`when` removeFile path) +deleteDirIfExists :: FilePath -> IO () +deleteDirIfExists path = doesDirectoryExist path >>= (`when` removeDirectoryRecursive path) + confirm :: String -> IO () confirm msg = do putStr $ msg <> " (y/N): " diff --git a/src/Simplex/Messaging/Agent.hs b/src/Simplex/Messaging/Agent.hs index 4fb662da8..63fc15274 100644 --- a/src/Simplex/Messaging/Agent.hs +++ b/src/Simplex/Messaging/Agent.hs @@ -105,13 +105,13 @@ runSMPAgent t cfg = do -- This function uses passed TMVar to signal when the server is ready to accept TCP requests (True) -- and when it is disconnected from the TCP socket once the server thread is killed (False). runSMPAgentBlocking :: (MonadRandom m, MonadUnliftIO m) => ATransport -> TMVar Bool -> AgentConfig -> m () -runSMPAgentBlocking (ATransport t) started cfg@AgentConfig {tcpPort, agentCertificateFile, agentPrivateKeyFile} = do +runSMPAgentBlocking (ATransport t) started cfg@AgentConfig {tcpPort, caCertificateFile, agentCertificateFile, agentPrivateKeyFile} = do runReaderT (smpAgent t) =<< newSMPAgentEnv cfg where smpAgent :: forall c m'. (Transport c, MonadUnliftIO m', MonadReader Env m') => TProxy c -> m' () smpAgent _ = do -- tlsServerParams not in env to avoid breaking functional api w/t key and certificate generation - tlsServerParams <- liftIO $ loadTLSServerParams agentCertificateFile agentPrivateKeyFile + tlsServerParams <- liftIO $ loadTLSServerParams caCertificateFile agentCertificateFile agentPrivateKeyFile runTransportServer started tcpPort tlsServerParams $ \(h :: c) -> do liftIO . putLn h $ "Welcome to SMP agent v" <> currentSMPVersionStr c <- getAgentClient diff --git a/src/Simplex/Messaging/Agent/Env/SQLite.hs b/src/Simplex/Messaging/Agent/Env/SQLite.hs index b2b81deed..55dd48172 100644 --- a/src/Simplex/Messaging/Agent/Env/SQLite.hs +++ b/src/Simplex/Messaging/Agent/Env/SQLite.hs @@ -32,6 +32,7 @@ data AgentConfig = AgentConfig smpCfg :: SMPClientConfig, retryInterval :: RetryInterval, reconnectInterval :: RetryInterval, + caCertificateFile :: FilePath, agentPrivateKeyFile :: FilePath, agentCertificateFile :: FilePath } @@ -63,7 +64,8 @@ defaultAgentConfig = increaseAfter = 10_000_000, maxInterval = 10_000_000 }, - -- ! we do not generate these key and certificate + -- ! we do not generate these + caCertificateFile = "/etc/opt/simplex-agent/ca.crt", agentPrivateKeyFile = "/etc/opt/simplex-agent/agent.key", agentCertificateFile = "/etc/opt/simplex-agent/agent.crt" } diff --git a/src/Simplex/Messaging/Server/Env/STM.hs b/src/Simplex/Messaging/Server/Env/STM.hs index e2888d7c6..501b8987f 100644 --- a/src/Simplex/Messaging/Server/Env/STM.hs +++ b/src/Simplex/Messaging/Server/Env/STM.hs @@ -32,6 +32,7 @@ data ServerConfig = ServerConfig msgIdBytes :: Int, storeLog :: Maybe (StoreLog 'ReadMode), blockSize :: Int, + caCertificateFile :: FilePath, serverPrivateKeyFile :: FilePath, serverCertificateFile :: FilePath } @@ -92,13 +93,13 @@ newSubscription = do return Sub {subThread = NoSub, delivered} newEnv :: forall m. (MonadUnliftIO m, MonadRandom m) => ServerConfig -> m Env -newEnv config = do +newEnv config@ServerConfig {caCertificateFile, serverCertificateFile, serverPrivateKeyFile} = do server <- atomically $ newServer (serverTbqSize config) queueStore <- atomically newQueueStore msgStore <- atomically newMsgStore idsDrg <- drgNew >>= newTVarIO s' <- restoreQueues queueStore `mapM` storeLog (config :: ServerConfig) - tlsServerParams <- liftIO $ loadTLSServerParams (serverCertificateFile config) (serverPrivateKeyFile config) + tlsServerParams <- liftIO $ loadTLSServerParams caCertificateFile serverCertificateFile serverPrivateKeyFile return Env {config, server, queueStore, msgStore, idsDrg, storeLog = s', tlsServerParams} where restoreQueues :: QueueStore -> StoreLog 'ReadMode -> m (StoreLog 'WriteMode) diff --git a/src/Simplex/Messaging/Transport.hs b/src/Simplex/Messaging/Transport.hs index 4cc61063e..2539dec48 100644 --- a/src/Simplex/Messaging/Transport.hs +++ b/src/Simplex/Messaging/Transport.hs @@ -213,13 +213,13 @@ startTCPClient host port clientParams = withSocketsDo $ resolve >>= tryOpen err ctx <- connectTLS clientParams sock getClientConnection ctx -loadTLSServerParams :: FilePath -> FilePath -> IO T.ServerParams -loadTLSServerParams certificateFile privateKeyFile = +loadTLSServerParams :: FilePath -> FilePath -> FilePath -> IO T.ServerParams +loadTLSServerParams caCertificateFile certificateFile privateKeyFile = fromCredential <$> loadServerCredential where loadServerCredential :: IO T.Credential loadServerCredential = - T.credentialLoadX509 certificateFile privateKeyFile >>= \case + T.credentialLoadX509Chain certificateFile [caCertificateFile] privateKeyFile >>= \case Right credential -> pure credential Left _ -> putStrLn "invalid credential" >> exitFailure fromCredential :: T.Credential -> T.ServerParams @@ -288,8 +288,9 @@ mkTLSClientParams host port keyHash = do validateCertificateChain :: Maybe C.KeyHash -> HostName -> ByteString -> X.CertificateChain -> IO [XV.FailedReason] validateCertificateChain _ _ _ (X.CertificateChain []) = pure [XV.EmptyChain] -validateCertificateChain keyHash host port cc@(X.CertificateChain sc@[cert]) = - let fp = XV.getFingerprint cert X.HashSHA256 +validateCertificateChain _ _ _ (X.CertificateChain [_]) = pure [XV.EmptyChain] +validateCertificateChain keyHash host port cc@(X.CertificateChain sc@[_, caCert]) = + let fp = XV.getFingerprint caCert X.HashSHA256 in if maybe True (sameFingerprint fp) keyHash then x509validate else pure [XV.UnknownCA] @@ -299,11 +300,11 @@ validateCertificateChain keyHash host port cc@(X.CertificateChain sc@[cert]) = x509validate = XV.validate X.HashSHA256 hooks checks certStore cache serviceID cc where hooks = XV.defaultHooks - checks = XV.defaultChecks {XV.checkLeafV3 = False} -- TODO create v3 certificates? https://stackoverflow.com/a/18242720 + checks = XV.defaultChecks certStore = XS.makeCertificateStore sc - cache = XV.exceptionValidationCache [] -- we manually check fingerprint only of the offline certificate (TODO 2 certificates) + cache = XV.exceptionValidationCache [] -- we manually check fingerprint only of the identity certificate (ca.crt) serviceID = (host, port) -validateCertificateChain _ _ _ (X.CertificateChain (_ : _)) = pure [XV.AuthorityTooDeep] +validateCertificateChain _ _ _ _ = pure [XV.AuthorityTooDeep] supportedParameters :: T.Supported supportedParameters = diff --git a/tests/AgentTests.hs b/tests/AgentTests.hs index 988733523..266d899de 100644 --- a/tests/AgentTests.hs +++ b/tests/AgentTests.hs @@ -368,7 +368,7 @@ syntaxTests t = do -- TODO: ERROR no connection alias in the response (it does not generate it yet if not provided) -- TODO: add tests with defined connection alias it "using same server as in invitation" $ - ("311", "a", "JOIN https://simpex.chat/invitation#/?smp=smp%3A%2F%2F" <> (U.encode . decodeLenient) "f80NoyPgNXR5n/fRVfmRTtkRps6/xDrQLmiuz9qFUJU=" <> "%40localhost%3A5001%2F3456-w%3D%3D%23&e2e=" <> urlEncode True samplePublicKey <> " 14\nbob's connInfo") + ("311", "a", "JOIN https://simpex.chat/invitation#/?smp=smp%3A%2F%2F" <> (U.encode . decodeLenient) "9VjLsOY5ZvB4hoglNdBzJFAUi/vP4GkZnJFahQOXV20=" <> "%40localhost%3A5001%2F3456-w%3D%3D%23&e2e=" <> urlEncode True samplePublicKey <> " 14\nbob's connInfo") >#> ("311", "a", "ERR SMP AUTH") describe "invalid" $ do -- TODO: JOIN is not merged yet - to be added diff --git a/tests/SMPAgentClient.hs b/tests/SMPAgentClient.hs index 9918e3adb..97236922b 100644 --- a/tests/SMPAgentClient.hs +++ b/tests/SMPAgentClient.hs @@ -156,7 +156,7 @@ cfg :: AgentConfig cfg = defaultAgentConfig { tcpPort = agentTestPort, - smpServers = L.fromList ["localhost:5001#f80NoyPgNXR5n/fRVfmRTtkRps6/xDrQLmiuz9qFUJU="], + smpServers = L.fromList ["localhost:5001#9VjLsOY5ZvB4hoglNdBzJFAUi/vP4GkZnJFahQOXV20="], tbqSize = 1, dbFile = testDB, smpCfg = @@ -166,8 +166,9 @@ cfg = tcpTimeout = 500_000 }, retryInterval = (retryInterval defaultAgentConfig) {initialInterval = 50_000}, - agentPrivateKeyFile = "tests/fixtures/example.key", - agentCertificateFile = "tests/fixtures/example.crt" + caCertificateFile = "tests/fixtures/ca.crt", + agentPrivateKeyFile = "tests/fixtures/server.key", + agentCertificateFile = "tests/fixtures/server.crt" } withSmpAgentThreadOn_ :: (MonadUnliftIO m, MonadRandom m) => ATransport -> (ServiceName, ServiceName, String) -> m () -> (ThreadId -> m a) -> m a diff --git a/tests/SMPClient.hs b/tests/SMPClient.hs index 77eec9a9a..7fafa023b 100644 --- a/tests/SMPClient.hs +++ b/tests/SMPClient.hs @@ -38,13 +38,13 @@ testPort2 :: ServiceName testPort2 = "5002" testKeyHashStr :: ByteString -testKeyHashStr = "f80NoyPgNXR5n/fRVfmRTtkRps6/xDrQLmiuz9qFUJU=" +testKeyHashStr = "9VjLsOY5ZvB4hoglNdBzJFAUi/vP4GkZnJFahQOXV20=" testBlockSize :: Int testBlockSize = 16 * 1024 -- TODO move to Protocol testKeyHash :: Maybe C.KeyHash -testKeyHash = Just "f80NoyPgNXR5n/fRVfmRTtkRps6/xDrQLmiuz9qFUJU=" +testKeyHash = Just "9VjLsOY5ZvB4hoglNdBzJFAUi/vP4GkZnJFahQOXV20=" testStoreLogFile :: FilePath testStoreLogFile = "tests/tmp/smp-server-store.log" @@ -67,8 +67,9 @@ cfg = msgIdBytes = 24, storeLog = Nothing, blockSize = testBlockSize, - serverPrivateKeyFile = "tests/fixtures/example.key", - serverCertificateFile = "tests/fixtures/example.crt" + caCertificateFile = "tests/fixtures/ca.crt", + serverPrivateKeyFile = "tests/fixtures/server.key", + serverCertificateFile = "tests/fixtures/server.crt" } withSmpServerStoreLogOn :: (MonadUnliftIO m, MonadRandom m) => ATransport -> ServiceName -> (ThreadId -> m a) -> m a diff --git a/tests/fixtures/.gitignore b/tests/fixtures/.gitignore new file mode 100644 index 000000000..e988acd1b --- /dev/null +++ b/tests/fixtures/.gitignore @@ -0,0 +1 @@ +server.csr diff --git a/tests/fixtures/README.md b/tests/fixtures/README.md new file mode 100644 index 000000000..8946cab31 --- /dev/null +++ b/tests/fixtures/README.md @@ -0,0 +1,25 @@ +To generate fixtures: + +(keep these instructions and *openssl.cnf* consistent with certificate generation on server) + +```sh +# CA certificate (identity/offline) +openssl genpkey -algorithm ED448 -out ca.key +openssl req -new -x509 -days 999999 -config openssl.cnf -extensions v3_ca -key ca.key -out ca.crt +# server certificate (online) +openssl genpkey -algorithm ED448 -out server.key +openssl req -new -config openssl.cnf -reqexts v3_req -key server.key -out server.csr +openssl x509 -req -days 999999 -copy_extensions copy -in server.csr -CA ca.crt -CAkey ca.key -out server.crt +# to pretty-print +openssl x509 -in ca.crt -text -noout +openssl req -in server.csr -text -noout +openssl x509 -in server.crt -text -noout +``` + +To compute fingerprint for tests: + +```sh +stack ghci --ghci-options src/Simplex/Messaging/Transport.hs +> fingerprint <- loadFingerprint "tests/fixtures/ca.crt" +> encodeFingerprint fingerprint +``` diff --git a/tests/fixtures/ca.crt b/tests/fixtures/ca.crt new file mode 100644 index 000000000..290d8f32d --- /dev/null +++ b/tests/fixtures/ca.crt @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBijCCAQqgAwIBAgIUf/txCk9PXE4nY2gQ/B/HG2sNzmswBQYDK2VxMBQxEjAQ +BgNVBAMMCWxvY2FsaG9zdDAgFw0yMTEyMjMxNzEzMjNaGA80NzU5MTExOTE3MTMy +M1owFDESMBAGA1UEAwwJbG9jYWxob3N0MEMwBQYDK2VxAzoAXlJkn15EFUS21zLI +I+HSKlhvt88LSXK70KkN4JRRLrXPaTYfpSchFZWmSuLmx5m6rmSg5Ywj9d2Ao1Mw +UTAdBgNVHQ4EFgQUxJBTkCx02jIpcUKU4fJYcnce59QwHwYDVR0jBBgwFoAUxJBT +kCx02jIpcUKU4fJYcnce59QwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXEDcwDlxmpY +U7j3CIVnMKAGA1rqML5lvKrDTS6DidTiq90dkMTyoXv8AE4omdiGobMnB3HZPl+B +CpdDUYCfQfkNdi8Hqj3V9viqcgahbn5mGnjUAK1+Ix6r7KLm2zeKcfGEG008ykGW +TMUFDvkQqRIlFDdOPAA= +-----END CERTIFICATE----- diff --git a/tests/fixtures/ca.key b/tests/fixtures/ca.key new file mode 100644 index 000000000..ca14015e0 --- /dev/null +++ b/tests/fixtures/ca.key @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEcCAQAwBQYDK2VxBDsEOZvjURTKSor4A7+45hnY721WD06L3E4UMKh9zntEY83C +CCv1Jju2fffDmtIFl6EXytF/nyEPGQfS5A== +-----END PRIVATE KEY----- diff --git a/tests/fixtures/example.crt b/tests/fixtures/example.crt deleted file mode 100644 index a46800fbb..000000000 --- a/tests/fixtures/example.crt +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBLzCBsAIUDS2s4hUHeT9gYpGcf7SJNnyReDUwBQYDK2VxMBQxEjAQBgNVBAMM -CWxvY2FsaG9zdDAgFw0yMTEyMTQxMDM3NTFaGA80NzU5MTExMDEwMzc1MVowFDES -MBAGA1UEAwwJbG9jYWxob3N0MEMwBQYDK2VxAzoAMb/HUcgN/sU2rm1YHoTMFVTu -ptY7hKjDm8mRxUWXzvHS0S6vqZRfJuCBQms4MSlTv+z1LjzDevUAMAUGAytlcQNz -AA2eyrpA0O2TzNCeVEs0Dp/uXTzQPWJHD8fN0DCwSJf7xIY01jNmcvx/IFYnGCd+ -uQ/7vm6kcUFNgKhVWY9e7xLjYqeBirHTQiTRrh+9mKDOwmsSOhnz3acYPgrJ2QUO -zDtZa16ppKRA5ucLJ4AXaacOAA== ------END CERTIFICATE----- diff --git a/tests/fixtures/example.key b/tests/fixtures/example.key deleted file mode 100644 index 246fdd397..000000000 --- a/tests/fixtures/example.key +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY----- -MEcCAQAwBQYDK2VxBDsEOcj0BvnNHWg2dsOnww++p/PHxnl+KGWFXre57wXEredA -j0xo78ZgadAeY0Y5mO4nfb8lk3CBz+ojGA== ------END PRIVATE KEY----- diff --git a/tests/fixtures/openssl.cnf b/tests/fixtures/openssl.cnf new file mode 100644 index 000000000..ab5344606 --- /dev/null +++ b/tests/fixtures/openssl.cnf @@ -0,0 +1,16 @@ +[req] +distinguished_name = req_distinguished_name +prompt = no + +[req_distinguished_name] +CN = localhost + +[v3_ca] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always +basicConstraints = critical,CA:true + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = digitalSignature, nonRepudiation, keyAgreement +extendedKeyUsage = serverAuth diff --git a/tests/fixtures/server.crt b/tests/fixtures/server.crt new file mode 100644 index 000000000..2adee7641 --- /dev/null +++ b/tests/fixtures/server.crt @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBpjCCASagAwIBAgIUaZBiYKJjueUsvwoaeK9mh+F2mn0wBQYDK2VxMBQxEjAQ +BgNVBAMMCWxvY2FsaG9zdDAgFw0yMTEyMjMxNzEzMzNaGA80NzU5MTExOTE3MTMz +M1owFDESMBAGA1UEAwwJbG9jYWxob3N0MEMwBQYDK2VxAzoA/q7ngl2MOKDeHVgC +4aNgO4+pOQ7cfHJhgVTKz0W6CCK9Ce39B0N+cRy6/dPzGCSSOYNKyGE0rnWAo28w +bTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIDyDATBgNVHSUEDDAKBggrBgEFBQcDATAd +BgNVHQ4EFgQUQP8dENbwDxWZNX2QwauT1Ple6aswHwYDVR0jBBgwFoAUxJBTkCx0 +2jIpcUKU4fJYcnce59QwBQYDK2VxA3MATscvAiT11CqXODKwx/0uLan3mKRLfJrP +gqshoOmIG4HUXoSPZwjgARaCKTwFwMlLmMJt6wd7c8iAnKdfghvDvE+fgSKDe1d4 +tVKQt+RWUzMb5w4WyqivxmKQyIBHNHzkj3Qh54P6JLpfMz29j84/pxIA +-----END CERTIFICATE----- diff --git a/tests/fixtures/server.key b/tests/fixtures/server.key new file mode 100644 index 000000000..579ac813c --- /dev/null +++ b/tests/fixtures/server.key @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEcCAQAwBQYDK2VxBDsEOQANqfrmSygKW1iiDCgf/G/y2AH1lp5NurM3Q73fp9Aw +nznRFYq6BvM03cMOkqtFpQd15A+DZr248A== +-----END PRIVATE KEY-----