From 57c9ccfc08fa7f9d884e4089397a5132d4736409 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 6 Dec 2021 09:05:13 +0000 Subject: [PATCH] use base64url encoding for public key in connection requests; only allow accepting invitations that were not accepted (#213) * use base64url encoding for public key in connection requests; only allow accepting invitations that were not accepted * subscribe ContactConnection, fix test to use base64url encoding in key example --- src/Simplex/Messaging/Agent.hs | 2 +- src/Simplex/Messaging/Agent/Protocol.hs | 4 ++-- src/Simplex/Messaging/Agent/Store/SQLite.hs | 19 ++++++++++++++++++- src/Simplex/Messaging/Crypto.hs | 11 ++++++++++- tests/AgentTests.hs | 2 +- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/Simplex/Messaging/Agent.hs b/src/Simplex/Messaging/Agent.hs index ef3ed45d2..59714af13 100644 --- a/src/Simplex/Messaging/Agent.hs +++ b/src/Simplex/Messaging/Agent.hs @@ -340,7 +340,7 @@ subscribeConnection' c connId = Active -> throwError $ CONN SIMPLEX _ -> throwError $ INTERNAL "unexpected queue status" SomeConn _ (RcvConnection _ rq) -> subscribeQueue c rq connId - SomeConn _ (ContactConnection _ _rq) -> pure () + SomeConn _ (ContactConnection _ rq) -> subscribeQueue c rq connId where resumeDelivery :: SndQueue -> m () resumeDelivery SndQueue {server} = do diff --git a/src/Simplex/Messaging/Agent/Protocol.hs b/src/Simplex/Messaging/Agent/Protocol.hs index 6c0c322d3..63102111b 100644 --- a/src/Simplex/Messaging/Agent/Protocol.hs +++ b/src/Simplex/Messaging/Agent/Protocol.hs @@ -399,7 +399,7 @@ serializeConnReq' = \case CMContact -> "contact" queryStr = renderSimpleQuery True [("smp", queues), ("e2e", key)] queues = B.intercalate "," . map serializeSMPQueueUri $ L.toList crSmpQueues - key = C.serializePubKey crEncryptKey + key = C.serializePubKeyUri crEncryptKey connReqP' :: forall m. ConnectionModeI m => Parser (ConnectionRequest m) connReqP' = do @@ -414,7 +414,7 @@ connReqP = do crMode <- "/" *> mode <* "#/?" query <- parseSimpleQuery <$> A.takeTill (\c -> c == ' ' || c == '\n') crSmpQueues <- paramP "smp" smpQueues query - crEncryptKey <- paramP "e2e" C.pubKeyP query + crEncryptKey <- paramP "e2e" C.pubKeyUriP query let cReq = ConnReqData {crScheme, crSmpQueues, crEncryptKey} pure $ case crMode of CMInvitation -> ACR SCMInvitation $ CRInvitation cReq diff --git a/src/Simplex/Messaging/Agent/Store/SQLite.hs b/src/Simplex/Messaging/Agent/Store/SQLite.hs index 13d1819d9..57959f9c3 100644 --- a/src/Simplex/Messaging/Agent/Store/SQLite.hs +++ b/src/Simplex/Messaging/Agent/Store/SQLite.hs @@ -40,7 +40,7 @@ import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (decodeLatin1) -import Database.SQLite.Simple (FromRow, NamedParam (..), Only (..), SQLData (..), SQLError, field) +import Database.SQLite.Simple (FromRow, NamedParam (..), Only (..), SQLData (..), SQLError, ToRow, field) import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.FromField import Database.SQLite.Simple.Internal (Field (..)) @@ -382,6 +382,7 @@ instance (MonadUnliftIO m, MonadError StoreError m) => MonadAgentStore SQLiteSto SELECT contact_conn_id, cr_invitation, recipient_conn_info, own_conn_info, accepted FROM conn_invitations WHERE invitation_id = ? + AND accepted = 0 |] (Only invitationId) where @@ -583,6 +584,22 @@ instance (FromField a, FromField b, FromField c, FromField d, FromField e, fromRow = (,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field <*> field <*> field <*> field <*> field <*> field <*> field + +instance (FromField a, FromField b, FromField c, FromField d, FromField e, + FromField f, FromField g, FromField h, FromField i, FromField j, + FromField k, FromField l) => + FromRow (a,b,c,d,e,f,g,h,i,j,k,l) where + fromRow = (,,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field + <*> field <*> field <*> field <*> field <*> field + <*> field <*> field + +instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f, + ToField g, ToField h, ToField i, ToField j, ToField k, ToField l) => + ToRow (a,b,c,d,e,f,g,h,i,j,k,l) where + toRow (a,b,c,d,e,f,g,h,i,j,k,l) = + [ toField a, toField b, toField c, toField d, toField e, toField f, + toField g, toField h, toField i, toField j, toField k, toField l + ] {- ORMOLU_ENABLE -} -- * Server upsert helper diff --git a/src/Simplex/Messaging/Crypto.hs b/src/Simplex/Messaging/Crypto.hs index ccd8f5f90..b858d4c0b 100644 --- a/src/Simplex/Messaging/Crypto.hs +++ b/src/Simplex/Messaging/Crypto.hs @@ -64,10 +64,12 @@ module Simplex.Messaging.Crypto -- * Encoding of RSA keys serializePrivKey, serializePubKey, + serializePubKeyUri, encodePubKey, publicKeyHash, privKeyP, pubKeyP, + pubKeyUriP, binaryPubKeyP, -- * SHA256 hash @@ -99,6 +101,7 @@ import qualified Data.Attoparsec.ByteString.Char8 as A import Data.Bifunctor (bimap, first) import qualified Data.ByteArray as BA import Data.ByteString.Base64 (decode, encode) +import qualified Data.ByteString.Base64.URL as U import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B import Data.ByteString.Internal (c2w, w2c) @@ -108,7 +111,7 @@ import Data.X509 import Database.SQLite.Simple.FromField (FromField (..)) import Database.SQLite.Simple.ToField (ToField (..)) import Network.Transport.Internal (decodeWord32, encodeWord32) -import Simplex.Messaging.Parsers (base64P, blobFieldParser, parseAll, parseString) +import Simplex.Messaging.Parsers (base64P, base64UriP, blobFieldParser, parseAll, parseString) import Simplex.Messaging.Util (liftEitherError, (<$?>)) -- | A newtype of 'Crypto.PubKey.RSA.PublicKey'. @@ -436,6 +439,9 @@ verify (PublicKey k) (Signature sig) msg = PSS.verify pssParams k msg sig serializePubKey :: PublicKey -> ByteString serializePubKey = ("rsa:" <>) . encode . encodePubKey +serializePubKeyUri :: PublicKey -> ByteString +serializePubKeyUri = ("rsa:" <>) . U.encode . encodePubKey + -- | Base-64 PKCS8 encoding of PSA private key. -- -- Not used as part of SMP protocols. @@ -446,6 +452,9 @@ serializePrivKey = ("rsa:" <>) . encode . encodePrivKey pubKeyP :: Parser PublicKey pubKeyP = decodePubKey <$?> ("rsa:" *> base64P) +pubKeyUriP :: Parser PublicKey +pubKeyUriP = decodePubKey <$?> ("rsa:" *> base64UriP) + -- Binary X509 RSA public key parser. binaryPubKeyP :: Parser PublicKey binaryPubKeyP = decodePubKey <$?> A.takeByteString diff --git a/tests/AgentTests.hs b/tests/AgentTests.hs index 4b8dcd95c..c2bdc5841 100644 --- a/tests/AgentTests.hs +++ b/tests/AgentTests.hs @@ -334,7 +334,7 @@ connect (h1, name1) (h2, name2) = do -- pure (conn1, conn2) samplePublicKey :: ByteString -samplePublicKey = "rsa:MIIBoDANBgkqhkiG9w0BAQEFAAOCAY0AMIIBiAKCAQEAtn1NI2tPoOGSGfad0aUg0tJ0kG2nzrIPGLiz8wb3dQSJC9xkRHyzHhEE8Kmy2cM4q7rNZIlLcm4M7oXOTe7SC4x59bLQG9bteZPKqXu9wk41hNamV25PWQ4zIcIRmZKETVGbwN7jFMpH7wxLdI1zzMArAPKXCDCJ5ctWh4OWDI6OR6AcCtEj+toCI6N6pjxxn5VigJtwiKhxYpoUJSdNM60wVEDCSUrZYBAuDH8pOxPfP+Tm4sokaFDTIG3QJFzOjC+/9nW4MUjAOFll9PCp9kaEFHJ/YmOYKMWNOCCPvLS6lxA83i0UaardkNLNoFS5paWfTlroxRwOC2T6PwO2ywKBgDjtXcSED61zK1seocQMyGRINnlWdhceD669kIHju/f6kAayvYKW3/lbJNXCmyinAccBosO08/0sUxvtuniIo18kfYJE0UmP1ReCjhMP+O+yOmwZJini/QelJk/Pez8IIDDWnY1qYQsN/q7ocjakOYrpGG7mig6JMFpDJtD6istR" +samplePublicKey = "rsa:MIIBoDANBgkqhkiG9w0BAQEFAAOCAY0AMIIBiAKCAQEAtn1NI2tPoOGSGfad0aUg0tJ0kG2nzrIPGLiz8wb3dQSJC9xkRHyzHhEE8Kmy2cM4q7rNZIlLcm4M7oXOTe7SC4x59bLQG9bteZPKqXu9wk41hNamV25PWQ4zIcIRmZKETVGbwN7jFMpH7wxLdI1zzMArAPKXCDCJ5ctWh4OWDI6OR6AcCtEj-toCI6N6pjxxn5VigJtwiKhxYpoUJSdNM60wVEDCSUrZYBAuDH8pOxPfP-Tm4sokaFDTIG3QJFzOjC-_9nW4MUjAOFll9PCp9kaEFHJ_YmOYKMWNOCCPvLS6lxA83i0UaardkNLNoFS5paWfTlroxRwOC2T6PwO2ywKBgDjtXcSED61zK1seocQMyGRINnlWdhceD669kIHju_f6kAayvYKW3_lbJNXCmyinAccBosO08_0sUxvtuniIo18kfYJE0UmP1ReCjhMP-O-yOmwZJini_QelJk_Pez8IIDDWnY1qYQsN_q7ocjakOYrpGG7mig6JMFpDJtD6istR" syntaxTests :: forall c. Transport c => TProxy c -> Spec syntaxTests t = do