pqdr: more tests, pass e2e version to rcEncrypt from config (#1040)

* pqdr: more tests, pass e2e version to rcEncrypt from config

* fix
This commit is contained in:
Evgeny Poberezkin
2024-03-10 13:29:03 +00:00
committed by GitHub
parent 054b6edb14
commit 851ed2d02e
5 changed files with 59 additions and 53 deletions
+34 -26
View File
@@ -1143,24 +1143,26 @@ enqueueMessage c cData sq msgFlags aMessage =
-- this function is used only for sending messages in batch, it returns the list of successes to enqueue additional deliveries
enqueueMessageB :: forall m t. (AgentMonad' m, Traversable t) => AgentClient -> t (Either AgentErrorType (ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage)) -> m (t (Either AgentErrorType ((AgentMsgId, PQEncryption), Maybe (ConnData, [SndQueue], AgentMsgId))))
enqueueMessageB c reqs = do
getAVRange <- asks $ smpAgentVRange . config
reqMids <- withStoreBatch c $ \db -> fmap (bindRight $ storeSentMsg db getAVRange) reqs
cfg <- asks config
reqMids <- withStoreBatch c $ \db -> fmap (bindRight $ storeSentMsg db cfg) reqs
forME reqMids $ \((cData, sq :| sqs, _, _, _), InternalId msgId, pqSecr) -> do
submitPendingMsg c cData sq
let sqs' = filter isActiveSndQ sqs
pure $ Right ((msgId, pqSecr), if null sqs' then Nothing else Just (cData, sqs', msgId))
where
storeSentMsg :: DB.Connection -> (PQSupport -> VersionRangeSMPA) -> (ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage) -> IO (Either AgentErrorType ((ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage), InternalId, PQEncryption))
storeSentMsg db getAVRange req@(cData@ConnData {connId, pqSupport}, sq :| _, pqEnc_, msgFlags, aMessage) = fmap (first storeError) $ runExceptT $ do
storeSentMsg :: DB.Connection -> AgentConfig -> (ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage) -> IO (Either AgentErrorType ((ConnData, NonEmpty SndQueue, Maybe PQEncryption, MsgFlags, AMessage), InternalId, PQEncryption))
storeSentMsg db cfg req@(cData@ConnData {connId, pqSupport}, sq :| _, pqEnc_, msgFlags, aMessage) = fmap (first storeError) $ runExceptT $ do
let AgentConfig {smpAgentVRange, e2eEncryptVRange} = cfg
internalTs <- liftIO getCurrentTime
(internalId, internalSndId, prevMsgHash) <- liftIO $ updateSndIds db connId
let privHeader = APrivHeader (unSndId internalSndId) prevMsgHash
agentMsg = AgentMessage privHeader aMessage
agentMsgStr = smpEncode agentMsg
internalHash = C.sha256Hash agentMsgStr
(encAgentMessage, pqEnc) <- agentRatchetEncrypt db cData agentMsgStr e2eEncUserMsgLength pqEnc_
currentE2EVersion = maxVersion $ e2eEncryptVRange PQSupportOff
(encAgentMessage, pqEnc) <- agentRatchetEncrypt db cData agentMsgStr e2eEncUserMsgLength pqEnc_ currentE2EVersion
-- agent version range is determined by the connection suppport of PQ encryption, that is may be enabled when message is sent
let agentVersion = maxVersion $ getAVRange pqSupport
let agentVersion = maxVersion $ smpAgentVRange pqSupport
msgBody = smpEncode $ AgentMsgEnvelope {agentVersion, encAgentMessage}
msgType = agentMessageType agentMsg
msgData = SndMsgData {internalId, internalSndId, internalTs, msgType, msgFlags, msgBody, pqEncryption = pqEnc, internalHash, prevMsgHash}
@@ -2488,11 +2490,14 @@ confirmQueue (Compatible agentVersion) c cData@ConnData {connId, pqSupport} sq s
withStore' c $ \db -> setSndQueueStatus db sq Confirmed
where
mkConfirmation :: AgentMessage -> m MsgBody
mkConfirmation aMessage = withStore c $ \db -> runExceptT $ do
void . liftIO $ updateSndIds db connId
let pqEnc = CR.pqSupportToEnc pqSupport
(encConnInfo, _) <- agentRatchetEncrypt db cData (smpEncode aMessage) e2eEncConnInfoLength (Just pqEnc)
pure . smpEncode $ AgentConfirmation {agentVersion, e2eEncryption_, encConnInfo}
mkConfirmation aMessage = do
-- the version to be used when PQSupport is disabled
currentE2EVersion <- asks $ maxVersion . ($ PQSupportOff) . e2eEncryptVRange . config
withStore c $ \db -> runExceptT $ do
void . liftIO $ updateSndIds db connId
let pqEnc = CR.pqSupportToEnc pqSupport
(encConnInfo, _) <- agentRatchetEncrypt db cData (smpEncode aMessage) e2eEncConnInfoLength (Just pqEnc) currentE2EVersion
pure . smpEncode $ AgentConfirmation {agentVersion, e2eEncryption_, encConnInfo}
mkAgentConfirmation :: AgentMonad m => AgentClient -> ConnData -> SndQueue -> SMPServerWithAuth -> ConnInfo -> SubscriptionMode -> m AgentMessage
mkAgentConfirmation c cData sq srv connInfo subMode = do
@@ -2505,18 +2510,21 @@ enqueueConfirmation c cData sq connInfo e2eEncryption_ = do
submitPendingMsg c cData sq
storeConfirmation :: AgentMonad m => AgentClient -> ConnData -> SndQueue -> Maybe (CR.SndE2ERatchetParams 'C.X448) -> AgentMessage -> m ()
storeConfirmation c cData@ConnData {connId, pqSupport, connAgentVersion = v} sq e2eEncryption_ agentMsg = withStore c $ \db -> runExceptT $ do
internalTs <- liftIO getCurrentTime
(internalId, internalSndId, prevMsgHash) <- liftIO $ updateSndIds db connId
let agentMsgStr = smpEncode agentMsg
internalHash = C.sha256Hash agentMsgStr
pqEnc = CR.pqSupportToEnc pqSupport
(encConnInfo, pqEncryption) <- agentRatchetEncrypt db cData agentMsgStr e2eEncConnInfoLength (Just pqEnc)
let msgBody = smpEncode $ AgentConfirmation {agentVersion = v, e2eEncryption_, encConnInfo}
msgType = agentMessageType agentMsg
msgData = SndMsgData {internalId, internalSndId, internalTs, msgType, msgBody, pqEncryption, msgFlags = SMP.MsgFlags {notification = True}, internalHash, prevMsgHash}
liftIO $ createSndMsg db connId msgData
liftIO $ createSndMsgDelivery db connId sq internalId
storeConfirmation c cData@ConnData {connId, pqSupport, connAgentVersion = v} sq e2eEncryption_ agentMsg = do
-- the version to be used when PQSupport is disabled
currentE2EVersion <- asks $ maxVersion . ($ PQSupportOff) . e2eEncryptVRange . config
withStore c $ \db -> runExceptT $ do
internalTs <- liftIO getCurrentTime
(internalId, internalSndId, prevMsgHash) <- liftIO $ updateSndIds db connId
let agentMsgStr = smpEncode agentMsg
internalHash = C.sha256Hash agentMsgStr
pqEnc = CR.pqSupportToEnc pqSupport
(encConnInfo, pqEncryption) <- agentRatchetEncrypt db cData agentMsgStr e2eEncConnInfoLength (Just pqEnc) currentE2EVersion
let msgBody = smpEncode $ AgentConfirmation {agentVersion = v, e2eEncryption_, encConnInfo}
msgType = agentMessageType agentMsg
msgData = SndMsgData {internalId, internalSndId, internalTs, msgType, msgBody, pqEncryption, msgFlags = SMP.MsgFlags {notification = True}, internalHash, prevMsgHash}
liftIO $ createSndMsg db connId msgData
liftIO $ createSndMsgDelivery db connId sq internalId
enqueueRatchetKeyMsgs :: forall m. AgentMonad m => AgentClient -> ConnData -> NonEmpty SndQueue -> CR.RcvE2ERatchetParams 'C.X448 -> m ()
enqueueRatchetKeyMsgs c cData (sq :| sqs) e2eEncryption = do
@@ -2546,11 +2554,11 @@ enqueueRatchetKey c cData@ConnData {connId, pqSupport} sq e2eEncryption = do
pure internalId
-- encoded AgentMessage -> encoded EncAgentMessage
agentRatchetEncrypt :: DB.Connection -> ConnData -> ByteString -> (VersionSMPA -> PQSupport -> Int) -> Maybe PQEncryption -> ExceptT StoreError IO (ByteString, PQEncryption)
agentRatchetEncrypt db ConnData {connId, connAgentVersion = v, pqSupport} msg getPaddedLen pqEnc_ = do
agentRatchetEncrypt :: DB.Connection -> ConnData -> ByteString -> (VersionSMPA -> PQSupport -> Int) -> Maybe PQEncryption -> CR.VersionE2E -> ExceptT StoreError IO (ByteString, PQEncryption)
agentRatchetEncrypt db ConnData {connId, connAgentVersion = v, pqSupport} msg getPaddedLen pqEnc_ currentE2EVersion = do
rc <- ExceptT $ getRatchet db connId
let paddedLen = getPaddedLen v pqSupport
(encMsg, rc') <- liftE (SEAgentError . cryptoError) $ CR.rcEncrypt rc paddedLen msg pqEnc_
(encMsg, rc') <- liftE (SEAgentError . cryptoError) $ CR.rcEncrypt rc paddedLen msg pqEnc_ currentE2EVersion
liftIO $ updateRatchet db connId rc' CR.SMDNoChange
pure (encMsg, CR.rcSndKEM rc')
+2
View File
@@ -37,9 +37,11 @@ module Simplex.Messaging.Agent.Protocol
VersionSMPA,
VersionRangeSMPA,
pattern VersionSMPA,
duplexHandshakeSMPAgentVersion,
ratchetSyncSMPAgentVersion,
deliveryRcptsSMPAgentVersion,
pqdrSMPAgentVersion,
currentSMPAgentVersion,
supportedSMPAgentVRange,
e2eEncConnInfoLength,
e2eEncUserMsgLength,
+9 -14
View File
@@ -76,7 +76,6 @@ module Simplex.Messaging.Crypto.Ratchet
RatchetKEM (..),
RatchetKEMAccepted (..),
RatchetKey (..),
ratchetVersions,
fullHeaderLen,
applySMDiff,
encodeMsgHeader,
@@ -496,20 +495,17 @@ data RatchetVersions = RatchetVersions
instance ToJSON RatchetVersions where
-- TODO v5.7 or v5.8 change to the default record encoding
toJSON (RatchetVersions v1 v2) = toJSON (v1, v2)
toEncoding (RatchetVersions v1 v2) = toEncoding (v1, v2)
toJSON RatchetVersions {current, maxSupported} = toJSON (current, maxSupported)
toEncoding RatchetVersions {current, maxSupported} = toEncoding (current, maxSupported)
instance FromJSON RatchetVersions where
-- TODO v6.0 replace with the default record parser
-- TODO v5.7 or v5.8 replace comment below with "tuple for backward"
-- this parser supports JSON record encoding for forward compatibility
parseJSON v = (tupleP <|> recordP v) >>= toRV
parseJSON v = toRV <$> (tupleP <|> recordP v)
where
tupleP = parseJSON v
recordP = J.withObject "RatchetVersions" $ \o -> (,) <$> o J..: "current" <*> o J..: "maxSupported"
toRV (v1, v2) = maybe (fail "bad version range") (pure . ratchetVersions) $ safeVersionRange v1 v2
ratchetVersions :: VersionRangeE2E -> RatchetVersions
ratchetVersions (VersionRange v1 v2) = RatchetVersions {current = v1, maxSupported = v2}
toRV (current, maxSupported) = RatchetVersions {current, maxSupported}
data SndRatchet a = SndRatchet
{ rcDHRr :: PublicKey a,
@@ -849,9 +845,9 @@ connPQEncryption = \case
IKUsePQ -> PQSupportOn
IKNoPQ pq -> pq -- default for creating connection is IKNoPQ PQEncOn
rcEncrypt :: AlgorithmI a => Ratchet a -> Int -> ByteString -> Maybe PQEncryption -> ExceptT CryptoError IO (ByteString, Ratchet a)
rcEncrypt Ratchet {rcSnd = Nothing} _ _ _ = throwE CERatchetState
rcEncrypt rc@Ratchet {rcSnd = Just sr@SndRatchet {rcCKs, rcHKs}, rcDHRs, rcKEM, rcNs, rcPN, rcAD = Str rcAD, rcSupportKEM, rcEnableKEM, rcVersion} paddedMsgLen msg pqEnc_ = do
rcEncrypt :: AlgorithmI a => Ratchet a -> Int -> ByteString -> Maybe PQEncryption -> VersionE2E -> ExceptT CryptoError IO (ByteString, Ratchet a)
rcEncrypt Ratchet {rcSnd = Nothing} _ _ _ _ = throwE CERatchetState
rcEncrypt rc@Ratchet {rcSnd = Just sr@SndRatchet {rcCKs, rcHKs}, rcDHRs, rcKEM, rcNs, rcPN, rcAD = Str rcAD, rcSupportKEM, rcEnableKEM, rcVersion} paddedMsgLen msg pqEnc_ supportedE2EVersion = do
-- state.CKs, mk = KDF_CK(state.CKs)
let (ck', mk, iv, ehIV) = chainKdf rcCKs
v = current rcVersion
@@ -862,8 +858,7 @@ rcEncrypt rc@Ratchet {rcSnd = Just sr@SndRatchet {rcCKs, rcHKs}, rcDHRs, rcKEM,
-- This sets max version to support PQ encryption.
-- Current version upgrade happens when peer decrypts the message.
-- TODO note that maxSupported will not downgrade here below current (v).
-- TODO PQ currentE2EEncryptVersion should be passed via config
maxSupported' = max currentE2EEncryptVersion $ if pqEnc_ == Just PQEncOn then pqRatchetE2EEncryptVersion else v
maxSupported' = max supportedE2EVersion $ if pqEnc_ == Just PQEncOn then pqRatchetE2EEncryptVersion else v
rcVersion' = rcVersion {maxSupported = maxSupported'}
-- enc_header = HENCRYPT(state.HKs, header)
(ehAuthTag, ehBody) <- encryptAEAD rcHKs ehIV (paddedHeaderLen v rcSupportKEM') rcAD (msgHeader v maxSupported')
+2 -2
View File
@@ -358,7 +358,7 @@ testVersionJSON = do
testDecodeRV $ (1 :: Int, 2 :: Int)
testDecodeRV $ J.object ["current" .= (1 :: Int), "maxSupported" .= (2 :: Int)]
where
rv v1 v2 = ratchetVersions $ mkVersionRange (VersionE2E v1) (VersionE2E v2)
rv v1 v2 = RatchetVersions (VersionE2E v1) (VersionE2E v2)
testDecodeRV :: ToJSON a => a -> Expectation
testDecodeRV a = J.eitherDecode' (J.encode a) `shouldBe` Right (rv 1 2)
@@ -585,7 +585,7 @@ testRatchetVersions pq =
encrypt_ :: AlgorithmI a => Maybe PQEncryption -> (TVar ChaChaDRG, Ratchet a, SkippedMsgKeys) -> ByteString -> IO (Either CryptoError (ByteString, Ratchet a, SkippedMsgDiff))
encrypt_ pqEnc_ (_, rc, _) msg =
-- print msg >>
runExceptT (rcEncrypt rc paddedMsgLen msg pqEnc_)
runExceptT (rcEncrypt rc paddedMsgLen msg pqEnc_ currentE2EEncryptVersion)
>>= either (pure . Left) checkLength
where
checkLength (msg', rc') = do
+12 -11
View File
@@ -176,7 +176,6 @@ smpCfgV7 = (smpCfg agentCfg) {serverVRange = V.mkVersionRange batchCmdsSMPVersio
ntfCfgV2 :: ProtocolClientConfig NTFVersion
ntfCfgV2 = (smpCfg agentCfg) {serverVRange = V.mkVersionRange (VersionNTF 1) authBatchCmdsNTFVersion}
-- TODO PQ test next version with PQ
agentCfgVPrev :: AgentConfig
agentCfgVPrev =
agentCfg
@@ -187,10 +186,13 @@ agentCfgVPrev =
smpCfg = smpCfgVPrev
}
-- agent config for the next client version
agentCfgV7 :: AgentConfig
agentCfgV7 =
agentCfg
{ sndAuthAlg = C.AuthAlg C.SX25519,
smpAgentVRange = \_ -> V.mkVersionRange duplexHandshakeSMPAgentVersion $ max pqdrSMPAgentVersion currentSMPAgentVersion,
e2eEncryptVRange = \_ -> V.mkVersionRange CR.kdfX3DHE2EEncryptVersion $ max CR.pqRatchetE2EEncryptVersion CR.currentE2EEncryptVersion,
smpCfg = smpCfgV7,
ntfCfg = ntfCfgV2
}
@@ -436,7 +438,6 @@ canCreateQueue allowNew (srvAuth, srvVersion) (clntAuth, clntVersion) =
let v = basicAuthSMPVersion
in allowNew && (isNothing srvAuth || (srvVersion >= v && clntVersion >= v && srvAuth == clntAuth))
-- TODO PQ test next version with PQ
testMatrix2 :: ATransport -> (PQSupport -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec
testMatrix2 t runTest = do
it "v7" $ withSmpServerV7 t $ runTestCfg2 agentCfgV7 agentCfgV7 3 $ runTest PQSupportOn
@@ -448,9 +449,11 @@ testMatrix2 t runTest = do
it "prev to current" $ withSmpServer t $ runTestCfg2 agentCfgVPrev agentCfg 3 $ runTest PQSupportOff
it "current to prev" $ withSmpServer t $ runTestCfg2 agentCfg agentCfgVPrev 3 $ runTest PQSupportOff
-- TODO PQ test next version with PQ
testRatchetMatrix2 :: ATransport -> (PQSupport -> AgentClient -> AgentClient -> AgentMsgId -> IO ()) -> Spec
testRatchetMatrix2 t runTest = do
it "ratchet next" $ withSmpServerV7 t $ runTestCfg2 agentCfgV7 agentCfgV7 3 $ runTest PQSupportOn
it "ratchet next to current" $ withSmpServerV7 t $ runTestCfg2 agentCfgV7 agentCfg 3 $ runTest PQSupportOn
it "ratchet current to next" $ withSmpServerV7 t $ runTestCfg2 agentCfg agentCfgV7 3 $ runTest PQSupportOn
it "ratchet current" $ withSmpServer t $ runTestCfg2 agentCfg agentCfg 3 $ runTest PQSupportOn
it "ratchet prev" $ withSmpServer t $ runTestCfg2 agentCfgRatchetVPrev agentCfgRatchetVPrev 3 $ runTest PQSupportOff
it "ratchets prev to current" $ withSmpServer t $ runTestCfg2 agentCfgRatchetVPrev agentCfg 3 $ runTest PQSupportOff
@@ -2478,14 +2481,12 @@ testDeliveryReceiptsVersion t = do
ackMessage a' bId 10 $ Just ""
get b' =##> \case ("", c, Rcvd 10) -> c == aId; _ -> False
ackMessage b' aId 11 Nothing
-- TODO PQ this part hangs when waiting for Rcvd, because connection tries to upgrade to PQ encryption.
-- replacing 2 PQSupportOn with PQEncOff above prevents hanging.
-- (12, _) <- A.sendMessage a' bId PQEncOn SMP.noMsgFlags "hello 2"
-- get a' ##> ("", bId, SENT 12)
-- get b' =##> \case ("", c, Msg' 12 PQEncOff "hello 2") -> c == aId; _ -> False
-- ackMessage b' aId 12 $ Just ""
-- get a' =##> \case ("", c, Rcvd 12) -> c == bId; _ -> False
-- ackMessage a' bId 13 Nothing
(12, _) <- A.sendMessage a' bId PQEncOn SMP.noMsgFlags "hello 2"
get a' ##> ("", bId, SENT 12)
get b' =##> \case ("", c, Msg' 12 PQEncOff "hello 2") -> c == aId; _ -> False
ackMessage b' aId 12 $ Just ""
get a' =##> \case ("", c, Rcvd 12) -> c == bId; _ -> False
ackMessage a' bId 13 Nothing
disconnectAgentClient a'
disconnectAgentClient b'