[Tkabber-dev] r2066 - in trunk/tkabber-plugins: . otr otr/tclotr

tkabber-svn at jabber.ru tkabber-svn at jabber.ru
Mon Jan 20 19:06:34 MSK 2014


Author: sergei
Date: 2014-01-20 19:06:34 +0400 (Mon, 20 Jan 2014)
New Revision: 2066

Modified:
   trunk/tkabber-plugins/ChangeLog
   trunk/tkabber-plugins/otr/otr.tcl
   trunk/tkabber-plugins/otr/tclotr/data.tcl
   trunk/tkabber-plugins/otr/tclotr/message.tcl
   trunk/tkabber-plugins/otr/tclotr/otr.tcl
Log:
	* otr/tclotr/otr.tcl: Do not send the OTR query message after an OTR
	  error if no ALLOW_V2 or ALLOW_V3 is in the policy flags.

	* otr/tclotr/data.tcl: Removed already done todo item.

	* otr/tclotr/otr.tcl, otr/tclotr/message.tcl: Implemented the old MAC
	  keys revelation, checking for the peer's counter monotonicity,
	  ignoring verification and decryption errors if IGNORE_UNREADABLE
	  message flag is set.

	* otr/tclotr/data.tcl, otr/tclotr/otr.tcl: Implemented assembling
	  incoming messages from fragments.

	* otr/tclotr/otr.tcl, otr/otr.tcl: Implemented sending heartbeat
	  messages (the OTR plugin doesn't use this yet).


Modified: trunk/tkabber-plugins/ChangeLog
===================================================================
--- trunk/tkabber-plugins/ChangeLog	2014-01-19 19:08:14 UTC (rev 2065)
+++ trunk/tkabber-plugins/ChangeLog	2014-01-20 15:06:34 UTC (rev 2066)
@@ -1,3 +1,21 @@
+2014-01-20  Sergei Golovan <sgolovan at nes.ru>
+
+	* otr/tclotr/otr.tcl: Do not send the OTR query message after an OTR
+	  error if no ALLOW_V2 or ALLOW_V3 is in the policy flags.
+
+	* otr/tclotr/data.tcl: Removed already done todo item.
+
+	* otr/tclotr/otr.tcl, otr/tclotr/message.tcl: Implemented the old MAC
+	  keys revelation, checking for the peer's counter monotonicity,
+	  ignoring verification and decryption errors if IGNORE_UNREADABLE
+	  message flag is set.
+
+	* otr/tclotr/data.tcl, otr/tclotr/otr.tcl: Implemented assembling
+	  incoming messages from fragments.
+
+	* otr/tclotr/otr.tcl, otr/otr.tcl: Implemented sending heartbeat
+	  messages (the OTR plugin doesn't use this yet).
+
 2014-01-19  Sergei Golovan <sgolovan at nes.ru>
 
 	* otr/tclotr/otr.tcl: Fixed checking data message hash for protocol

Modified: trunk/tkabber-plugins/otr/otr.tcl
===================================================================
--- trunk/tkabber-plugins/otr/otr.tcl	2014-01-19 19:08:14 UTC (rev 2065)
+++ trunk/tkabber-plugins/otr/otr.tcl	2014-01-20 15:06:34 UTC (rev 2066)
@@ -195,7 +195,7 @@
         return
     }
 
-    set result [::otr::new $::OTRPrivateKey [get_policy $xlib $jid]]
+    set result [::otr::new $::OTRPrivateKey -policy [get_policy $xlib $jid]]
     array set res $result
     set ctx($xlib,$jid) $res(token)
     foreach state {authstate msgstate smpstate} {
@@ -248,7 +248,7 @@
 	    }
 	}
 	foreach j $jids {
-	    ::otr::setPolicy $ctx($xl,$j) [get_policy $xl $j]
+	    ::otr::configure $ctx($xl,$j) -policy [get_policy $xl $j]
 	}
     }
 }

Modified: trunk/tkabber-plugins/otr/tclotr/data.tcl
===================================================================
--- trunk/tkabber-plugins/otr/tclotr/data.tcl	2014-01-19 19:08:14 UTC (rev 2065)
+++ trunk/tkabber-plugins/otr/tclotr/data.tcl	2014-01-20 15:06:34 UTC (rev 2066)
@@ -85,7 +85,7 @@
 #       message type and sender and recipient instance tags (for version 3).
 
 proc ::otr::data::binaryMessage {data} {
-    if {![regexp {^\?OTR:(.*).$} $data -> message]} {
+    if {![scan $data {?OTR:%[^.].} message] == 1} {
         return -code error "Message doesn't contain OTR marker"
     }
     set binary [::base64::decode $message]
@@ -100,6 +100,35 @@
     }
 }
 
+# ::otr::data::binaryMessageFragment --
+#
+#       Decode the OTR message fragment and return its piece number, the total
+#       number of pieces, the message fragment and the sender and receiver's
+#       instance tags for version 3 packages.
+#
+# Arguments:
+#       data        Received OTR message fragment.
+#
+# Result:
+#       Tuple {version part numpatrs message ?sinstance rinstance?} where
+#       version is the protocol version, part is a piece number, numparts is a
+#       total number of parts, message is an OTR message fragment, sinstance
+#       is a sender's sinstance tag, rinstance is a receiver's instance tag.
+#
+# Side effects:
+#       Error is raised if this data is not an OTR message fragment.
+
+proc ::otr::data::binaryMessageFragment {data} {
+    if {[scan $data {?OTR|%x|%x,%u,%u,%[^,],} \
+                    sinstance rinstance part numparts message] == 5} {
+        return [list 3 $part $numparts $message $sinstance $rinstance]
+    } elseif {[scan $data {?OTR,%u,%u,%[^,],} part numparts message] == 3} {
+        return [list 2 $part $numparts $message]
+    } else {
+        return -code error "Message doesn't contain OTR part marker"
+    }
+}
+
 # ::otr::data::queryMessage --
 #
 #       Return the OTR query message.
@@ -228,7 +257,6 @@
     if {$str1 eq $WSv3 || $str2 eq $WSv3 || $str3 eq $WSv3} {
         lappend res 3
     }
-    # TODO: remove the whitespace tag
     return $res
 }
 

Modified: trunk/tkabber-plugins/otr/tclotr/message.tcl
===================================================================
--- trunk/tkabber-plugins/otr/tclotr/message.tcl	2014-01-19 19:08:14 UTC (rev 2065)
+++ trunk/tkabber-plugins/otr/tclotr/message.tcl	2014-01-20 15:06:34 UTC (rev 2066)
@@ -51,8 +51,9 @@
 # Side effects:
 #       None.
 
-proc ::otr::message::createDataMessage {version flags skeyid rkeyid x1 x2 gy
-                                        ctrtop humanreadable tlvlist args} {
+proc ::otr::message::createDataMessage {version flags skeyid rkeyid skey smac x
+                                        ctrtop humanreadable tlvlist
+                                        oldmackeys args} {
     variable Flags
 
     set sinstance 0x100
@@ -79,28 +80,27 @@
     append res [::otr::data::encode BYTE $binflags]
     append res [::otr::data::encode INT $skeyid]
     append res [::otr::data::encode INT $rkeyid]
-    set gx [::otr::crypto::DHGx $x2]
+    set gx [::otr::crypto::DHGx $x]
     append res [::otr::data::encode MPI $gx]
     append res [::otr::data::encode CTR $ctrtop]
 
     set plaintext [createDataMessagePlaintext $humanreadable $tlvlist]
 
-    lassign [::otr::crypto::AESKeys $gy $x1] skey smac rkey rmac
     set cryptotext [::otr::crypto::aes $plaintext $skey $ctrtop]
     append res [::otr::data::encode DATA $cryptotext]
 
     set hmac [::sha1::hmac -bin -key $smac $res]
     append res [::otr::data::encode MAC $hmac]
-    # TODO
-    set oldmackeys ""
-    append res [::otr::data::encode DATA $oldmackeys]
+
+    append res [::otr::data::encode DATA [join $oldmackeys ""]]
     ::otr::data::encodeMessage $res
 }
 
 # ::otr::message::processDataMessage --
 
 proc ::otr::message::processDataMessage {version msgstate smpstate data
-                                         skeyid1 rkeyid1 gy1 x args} {
+                                         flags skeyid rkeyid gy ctrtop
+                                         rkey rmac args} {
     variable Flags
 
     set sinstance 0x100
@@ -117,22 +117,20 @@
         return -code error "Option -smpcommand is mandatory"
     }
 
-    set info "Encrypted message can't be deciphered"
-    set error [list [::otr::data::errorMessage \
-                            "Encrypted message can't be deciphered"]]
+    if {"IGNORE_UNREADABLE" in $flags} {
+        set err {}
+    } else {
+        set err [list info "Encrypted message can't be deciphered" \
+                      replyerr [list [::otr::data::errorMessage \
+                                    "Encrypted message can't be deciphered"]]]
+    }
 
     if {[catch {parseDataMessage $data} res]} {
-        return [list debug "Parsing data message failed: $res" \
-                     info $info replyerr $error]
+        return [linsert $err 0 debug "Parsing data message failed: $res"]
     }
 
-    lassign $res flags skeyid rkeyid gy ctrtop cryptotext hmac oldmackeys
+    lassign $res cryptotext hmac oldmackeys
 
-    if {$skeyid != $skeyid1 || $rkeyid != $rkeyid1} {
-        return [list debug "Serials don't match" \
-                     info $info replyerr $error]
-    }
-
     # Reassemble the message and verify its hash
 
     set msg ""
@@ -155,22 +153,19 @@
     append msg [::otr::data::encode CTR $ctrtop]
     append msg [::otr::data::encode DATA $cryptotext]
 
-    lassign [::otr::crypto::AESKeys $gy1 $x] skey smac rkey rmac
-
     set myhmac [::sha1::hmac -bin -key $rmac $msg]
 
     if {$myhmac ne $hmac} {
-        return [list debug "Data message hash verification failed" \
-                     info $info replyerr $error]
+        return [linsert $err 0 debug "Data message hash verification failed"]
     }
 
-    # Decrypt yhe payload and parse it
+    # Decrypt the payload and parse it
 
     set plaintext [::otr::crypto::aes $cryptotext $rkey $ctrtop]
 
     if {[catch {parseDataMessagePlaintext $plaintext} res]} {
-        return [list debug "Data message plaintext decoding failed: $res" \
-                     info $info replyerr $error]
+        return [linsert $err 0 debug \
+                               "Data message plaintext decoding failed: $res"]
     }
 
     lassign $res message tlvlist
@@ -223,10 +218,8 @@
                 # after the user will supply the shared secret
 
                 {*}$smpcommand set data1     $value
-                {*}$smpcommand set dhpubkey  $gy1
-                {*}$smpcommand set dhprivkey $x
 
-                return [list nextkey $gy interaction smp]
+                return [list interaction smp]
             }
             3 {
                 # SMP message 2
@@ -249,7 +242,6 @@
                 }
                 return [list msgstate    $msgstate \
                              smpstate    $smpstate \
-                             nextkey     $gy \
                              smpprogress $answer \
                              reply       [list "" [list $tlv $payload]] \
                              {*}$msg]
@@ -278,7 +270,6 @@
                 }
                 return [list msgstate    $msgstate \
                              smpstate    $smpstate \
-                             nextkey     $gy \
                              smpprogress $answer \
                              reply       [list "" [list $tlv $payload]] \
                              {*}$msg]
@@ -309,7 +300,6 @@
                 }
                 return [list msgstate    $msgstate \
                              smpstate    $smpstate \
-                             nextkey     $gy \
                              smpprogress $answer \
                              reply       [list "" $tlvp] \
                              {*}$msg]
@@ -321,7 +311,6 @@
                 {*}$smpcommand clear
                 return [list msgstate    $msgstate \
                              smpstate    $smpstate \
-                             nextkey     $gy \
                              smpprogress SMP_ABORT \
                              {*}$msg]
             }
@@ -336,7 +325,6 @@
                     {*}$smpcommand clear
                     return [list msgstate    $msgstate \
                                  smpstate    $smpstate \
-                                 nextkey     $gy \
                                  smpprogress SMP_CHEATING \
                                  reply       {"" {6 ""}} \
                                  {*}$msg]
@@ -346,23 +334,21 @@
                 set value [string range $value [expr {$idx+1}] end]
 
                 {*}$smpcommand set data1     $value
-                {*}$smpcommand set dhpubkey  $gy1
-                {*}$smpcommand set dhprivkey $x
 
-                return [list nextkey $gy interaction smp question $question]
+                return [list interaction smp question $question]
             }
         }
     }
     return [list msgstate $msgstate \
                  smpstate $smpstate \
-                 nextkey  $gy \
                  {*}$msg]
 }
 
 # ::otr::message::parseDataMessage --
 #
-#       Parse the OTR data message (without version, packet type and sender
-#       and receiver instance tags which were extracted earlier).
+#       Parse the OTR data message (without version, packet type, sender
+#       and receiver instance tags, flags and sender and receiver keyids
+#       which were extracted earlier).
 #
 # Arguments:
 #       data            Payload to parse.
@@ -376,21 +362,10 @@
 proc ::otr::message::parseDataMessage {data} {
     variable Flags
 
-    lassign [::otr::data::decode BYTE $data] binflags data
-    set flags {}
-    foreach f [array names Flags] {
-        if {[expr {$binflags & $Flags($f)}]} {
-            lappend flags $f
-        }
-    }
-    lassign [::otr::data::decode INT $data] skeyid data
-    lassign [::otr::data::decode INT $data] rkeyid data
-    lassign [::otr::data::decode MPI $data] gy data
-    lassign [::otr::data::decode CTR $data] ctrtop data
     lassign [::otr::data::decode DATA $data] cryptotext data
     lassign [::otr::data::decode MAC $data] hmac data
     lassign [::otr::data::decode DATA $data] oldmackeys
-    list $flags $skeyid $rkeyid $gy $ctrtop $cryptotext $hmac $oldmackeys
+    list $cryptotext $hmac $oldmackeys
 }
 
 # ::otr::message::getDataMessageKeyids --
@@ -420,12 +395,14 @@
                 }
             }
             lassign [::otr::data::decode INT $data] skeyid data
-            lassign [::otr::data::decode INT $data] rkeyid
-            list $flags $skeyid $rkeyid
+            lassign [::otr::data::decode INT $data] rkeyid data
+            lassign [::otr::data::decode MPI $data] gy data
+            lassign [::otr::data::decode CTR $data] ctrtop data
+            list $flags $skeyid $rkeyid $gy $ctrtop $data
          } res]} {
         return $res
     } else {
-        return {{} 0 0}
+        return {{} 0 0 0 0 ""}
     }
 }
 

Modified: trunk/tkabber-plugins/otr/tclotr/otr.tcl
===================================================================
--- trunk/tkabber-plugins/otr/tclotr/otr.tcl	2014-01-19 19:08:14 UTC (rev 2065)
+++ trunk/tkabber-plugins/otr/tclotr/otr.tcl	2014-01-20 15:06:34 UTC (rev 2066)
@@ -58,8 +58,12 @@
 #       Create new OTR instance.
 #
 # Arguments:
-#       key                 Private key (tuple {p q g y x}).
-#       policy              List of policy flags.
+#       privkey             Private key (tuple {p q g y x}).
+#       -policy policy      List of policy flags.
+#       -heartbeat time     (minutes) Interval before which a heartbeat
+#                           message will not be sent.
+#       -maxsize size       (ASCII chars) Max message size to send (not
+#                           implemented yet (TODO)).
 #
 # Result:
 #       Serialized array with fields 'token', 'authstate', 'msgstate'.
@@ -67,13 +71,24 @@
 # Side effects:
 #       The state variable is created.
 
-proc ::otr::new {key {policy {}}} {
+proc ::otr::new {privkey args} {
     variable id
 
     if {![info exists id]} {
         set id 0
     }
 
+    set policy {}
+    set heartbeat 0
+    set maxsize 0
+    foreach {key val} $args {
+        switch -- $key {
+            -policy { set policy $val }
+            -heartbeat { set heartbeat $val }
+            -maxsize { set maxsize $val }
+        }
+    }
+
     if {"ALLOW_V1" in $policy} {
         return -code error "OTR version 1 is not supported"
     }
@@ -88,10 +103,19 @@
     set state(MsgState) MSGSTATE_PLAINTEXT
     set state(SMPState) SMPSTATE_EXPECT1
     set state(StoredMessages) {}
-    set state(PrivateKey) $key
+    set state(PrivateKey) $privkey
     set state(Policy) $policy
+    set state(HeartBeat) $heartbeat
+    set state(LastMessage) [clock seconds]
+    set state(MaxSize) $maxsize
     while {[set state(sinstance) [::otr::crypto::random 32]] < 0x100} {}
 
+    # Init vars for fragmented message
+
+    set state(K) 0
+    set state(N) 0
+    set state(F) ""
+
     # Generate DH private keys (key management 1)
 
     InitDHKeys $token
@@ -102,29 +126,45 @@
          smpstate $state(SMPState)
 }
 
-proc ::otr::free {token} {
+proc ::otr::configure {token key args} {
     variable $token
     upvar 0 $token state
 
-    unset -nocomplain state
+    if {![info exists state(AuthState)]} return
+
+    if {[llength $args] == 0} {
+        switch -- $key {
+            -policy { return $state(Policy) }
+            -heartbeat { return $state(HeartBeat) }
+            -maxsize { return $state(MaxSize) }
+        }
+    }
+
+    set val [lindex $args 0]
+
+    switch -- $key {
+        -policy {
+            if {"ALLOW_V1" in $policy} {
+                return -code error "OTR version 1 is not supported"
+            }
+        
+            set state(Policy) $val
+        }
+        -heartbeat { set state(HeartBeat) $val }
+        -maxsize { set state(MaxSize) $val }
+    }
 }
 
-##############################################################################
-
-proc ::otr::setPolicy {token policy} {
+proc ::otr::free {token} {
     variable $token
     upvar 0 $token state
 
-    if {![info exists state(AuthState)]} return
+    unset -nocomplain state
+}
 
-    if {"ALLOW_V1" in $policy} {
-        return -code error "OTR version 1 is not supported"
-    }
+##############################################################################
 
-    set state(Policy) $policy
-}
-
-proc ::otr::queryPolicy {token item} {
+proc ::otr::QueryPolicy {token item} {
     variable $token
     upvar 0 $token state
 
@@ -327,13 +367,13 @@
 
     switch -- $state(MsgState) {
         MSGSTATE_PLAINTEXT {
-            if {[queryPolicy $token REQUIRE_ENCRYPTION]} {
+            if {[QueryPolicy $token REQUIRE_ENCRYPTION]} {
                 Store $token $body
                 return [list action send \
                              body [::otr::data::queryMessage $state(Policy)]]
-            } elseif {[queryPolicy $token SEND_WHITESPACE_TAG] &&
-                      ([queryPolicy $token ALLOW_V2] ||
-                       [queryPolicy $token ALLOW_V3])} {
+            } elseif {[QueryPolicy $token SEND_WHITESPACE_TAG] &&
+                      ([QueryPolicy $token ALLOW_V2] ||
+                       [QueryPolicy $token ALLOW_V3])} {
                 return [list action send \
                              body "$body[::otr::data::whitespaceTag \
                                                         $state(Policy)]"]
@@ -343,6 +383,10 @@
         }
         MSGSTATE_ENCRYPTED {
             Store $token $body
+
+            # Store the time of last message sent
+            set state(LastMessage) [clock seconds]
+
             set message [CreateEncryptedMessage $token {} $body {}]
             return [list action send \
                          body   $message]
@@ -361,8 +405,14 @@
     variable $token
     upvar 0 $token state
 
-    # TODO: support for message fragmentation
-    if {![catch {::otr::data::binaryMessage $message} data]} {
+    if {![catch {::otr::data::binaryMessageFragment $message} data]} {
+        # Binary OTR message fragment
+
+        Debug $token 2 "OTR binary message fragment"
+
+        return [AssembleBinaryMessage $token $data]
+
+    } elseif {![catch {::otr::data::binaryMessage $message} data]} {
         # Binary OTR message
 
         Debug $token 2 "OTR binary message"
@@ -374,9 +424,11 @@
 
         Debug $token 2 "OTR error message"
 
-        if {[queryPolicy $token ERROR_START_AKE]} {
+        if {[QueryPolicy $token ERROR_START_AKE] &&
+            ([QueryPolicy $token ALLOW_V2] || [QueryPolicy $token ALLOW_V3])} {
             return [list error $error \
-                         reply [list [::otr::data::queryMessage $state(Policy)]]]
+                         reply [list [::otr::data::queryMessage \
+                                                $state(Policy)]]]
         } else {
             return [list error $error]
         }
@@ -407,7 +459,7 @@
 
         set message [::otr::data::removeWhitespaceTag $message]
 
-        if {[queryPolicy $token WHITESPACE_START_AKE] && \
+        if {[QueryPolicy $token WHITESPACE_START_AKE] && \
                     [set version [FindVersion $token $versions]]} {
 
             NewSession $token $version
@@ -467,6 +519,58 @@
     }
 }
 
+proc ::otr::AssembleBinaryMessage {token data} {
+    variable $token
+    upvar 0 $token state
+
+    # Here sinstance is a remote instance tag, rinstance is ours,
+    # because the message is incoming
+
+    lassign $data version k n message sinstance rinstance
+
+    Debug $token 2 "$version $k $n $message"
+
+    if {$version >= 3} {
+        if {$sinstance < 0x100 ||
+                ($rinstance > 0 && $rinstance != $state(sinstance))} {
+            return {}
+        }
+        if {![info exists state(rinstance)]} {
+            set state(rinstance) $sinstance
+        } elseif {$sinstance != $state(rinstance)} {
+            return {}
+        }
+    } else {
+        # Fake rinstance for version 2
+        set state(rinstance) 0x100
+    }
+
+    if {$k == 0 || $n == 0 || $k > $n} {
+        # Do nothing
+    } elseif {$k == 1} {
+        set state(F) $message
+        set state(K) $k
+        set state(N) $n
+    } elseif {$n == $state(N) && $k == $state(K)+1} {
+        append state(F) $message
+        incr state(K)
+    } else {
+        set state(F) ""
+        set state(K) 0
+        set state(N) 0
+    }
+
+    if {$state(N) > 0 && $state(K) == $state(N)} {
+        set data $state(F)
+        set state(F) ""
+        set state(K) 0
+        set state(N) 0
+        return [incomingMessage $token $data]
+    } else {
+        return {}
+    }
+}
+
 proc ::otr::DispatchBinaryMessage {token data} {
     variable $token
     upvar 0 $token state
@@ -481,9 +585,9 @@
     if {![info exists state(version)]} {
         switch -- $type {
             2 {
-                if {$version == 3 && [queryPolicy $token ALLOW_V3]} {
+                if {$version == 3 && [QueryPolicy $token ALLOW_V3]} {
                     set state(version) 3
-                } elseif {$version == 2 && [queryPolicy $token ALLOW_V2]} {
+                } elseif {$version == 2 && [QueryPolicy $token ALLOW_V2]} {
                     set state(version) 2
                 } else {
                     return {}
@@ -509,7 +613,7 @@
     }
 
     if {$version >= 3} {
-        if {$sinstance < 0x100 || \
+        if {$sinstance < 0x100 ||
                 ($rinstance > 0 && $rinstance != $state(sinstance))} {
             return {}
         }
@@ -519,8 +623,7 @@
             return {}
         }
     } else {
-        # Fake sinstance and rinstance for version 2
-        set state(sinstance) 0x100
+        # Fake rinstance for version 2
         set state(rinstance) 0x100
     }
 
@@ -722,45 +825,68 @@
     variable $token
     upvar 0 $token state
 
+    
     set info "Encrypted message can't be deciphered"
     set reply [list [::otr::data::errorMessage $info]]
 
     switch -- $state(MsgState) {
         MSGSTATE_ENCRYPTED {
-            lassign [::otr::message::getDataMessageKeyids $data] flags skeyid rkeyid
+            lassign [::otr::message::getDataMessageKeyids $data] \
+                    flags skeyid rkeyid nextkey ctrtop rest
 
-            # TODO: Check message flags
+            if {"IGNORE_UNREADABLE" in $flags} {
+                set err {}
+            } else {
+                set err [list info $info reply $reply]
+            }
 
             if {$skeyid <= 0 || $rkeyid <= 0} {
                 Debug $token 1 "Data message doesn't contain key serial numbers"
-                return [list info $info reply $reply]
+                return $err
             }
 
             if {$skeyid != $state(keyidy) && $skeyid != $state(keyidy)-1} {
                 Debug $token 1 "The sender's key serial number is unknown"
-                return [list info $info reply $reply]
+                return $err
             }
 
             if {$rkeyid != $state(keyid) && $rkeyid != $state(keyid)-1} {
                 Debug $token 1 "The recipient's key serial number is unknown"
-                return [list info $info reply $reply]
+                return $err
             }
 
             if {$state(gy,$skeyid) <= 0} {
                 Debug $token 1 "The sender's key with this serial number doesn't exist"
-                return [list info $info reply $reply]
+                return $err
             }
 
-            # TODO: track and check if the peer's counter is monotonic
+            if {[info exists state(ctrtop,$skeyid,$rkeyid)]} {
+                if {$state(ctrtop,$skeyid,$rkeyid) >= $ctrtop} {
+                    Debug $token 1 "The sender's counter isn't monotonically increasing"
+                    return $err
+                }
+            } else {
+                array unset ctrtop,*
+            }
+            set state(ctrtop,$skeyid,$rkeyid) $ctrtop
+
+            lassign [::otr::crypto::AESKeys $state(gy,$skeyid) $state(x,$rkeyid)] \
+                    skey smac rkey rmac
+            # Save the receiving MAC key to reveal it later
+            set state(oldmackey,$skeyid,$rkeyid) $rmac
+
             set result [::otr::message::processDataMessage \
                                 $state(version) \
                                 $state(MsgState) \
                                 $state(SMPState) \
-                                $data \
+                                $rest \
+                                $flags \
                                 $skeyid \
                                 $rkeyid \
-                                $state(gy,$skeyid) \
-                                $state(x,$rkeyid) \
+                                $nextkey \
+                                $ctrtop \
+                                $rkey \
+                                $rmac \
                                 -smpcommand [namespace code [list SMPCallback $token]] \
                                 -sinstance $state(rinstance) \
                                 -rinstance $state(sinstance)]
@@ -806,9 +932,12 @@
                                 [CreateEncryptedMessage $token {} $body $tlvlist]
                         }
                         lappend ret reply $repl
+
+                        # Store the time of last message sent
+                        set state(LastMessage) [clock seconds]
                     }
                     default {
-                        # TODO
+                        Debug 1 $token "Trying to autoreply in $state(MsgState) state"
                     }
                 }
             }
@@ -824,6 +953,12 @@
                     if {$rkeyid == $state(keyid)} {
                         incr rkeyid -1
                         unset state(x,$rkeyid)
+                        foreach id [array names state oldmackey,*,$rkeyid] {
+                            # Add the used receiving MAC keys to the
+                            # revealed keys list
+                            lappend state(oldmackeys) $state(id)
+                            unset state($id)
+                        }
                         incr rkeyid 2
                         set state(x,$rkeyid) [::otr::crypto::random 320]
                         incr state(keyid)
@@ -833,10 +968,29 @@
                     if {$skeyid == $state(keyidy)} {
                         incr skeyid -1
                         unset state(gy,$skeyid)
+                        foreach id [array names state oldmackey,$skeyid,*] {
+                            # Add the used receiving MAC keys to the
+                            # revealed keys list
+                            lappend state(oldmackeys) $state(id)
+                            unset state($id)
+                        }
                         incr skeyid 2
-                        set state(gy,$skeyid) $res(nextkey)
+                        set state(gy,$skeyid) $nextkey
                         incr state(keyidy)
+                        set state(ctrtop) 0
                     }
+
+                    # Heartbeat message
+                    if {([lsearch -exact $ret reply] % 2) != 0} {
+                        set curtime [clock seconds]
+                        if {$state(HeartBeat) > 0 &&
+                                $curtime > $state(LastMessage) + 60*$state(HeartBeat)} {
+                            set state(LastMessage) [clock seconds]
+                            lappend ret reply \
+                                [list [CreateEncryptedMessage $token \
+                                                {IGNORE_UNREADABLE} "" {}]]
+                        }
+                    }
                 }
                 default {
                     InitDHKeys $token
@@ -859,6 +1013,8 @@
 
     array unset state x,*
     array unset state gy,*
+    array unset state ctrtop,*
+    array unset state oldmackey,*
 
     # Generate DH private keys (key management 1)
 
@@ -868,6 +1024,7 @@
     set state(keyidy) 0
     set state(gy,0) 0
     set state(gy,-1) 0
+    set state(oldmackeys) {}
 
     # Not exactly D-H related:
 
@@ -940,15 +1097,20 @@
     upvar $token state
 
     # Key management 3
-    set keyid1 [expr {$state(keyid)-1}]
-    set keyid2 $state(keyid)
-    set keyidy $state(keyidy)
-    # TODO: reveal old MACs
+    set keyid $state(keyid);             # Next private key id
+    set keyidx [expr {$state(keyid)-1}]; # Current private key id
+    set keyidy $state(keyidy);           # Current public key id
 
+    lassign [::otr::crypto::AESKeys $state(gy,$keyidy) $state(x,$keyidx)] \
+            skey smac rkey rmac
+
+    set oldmackeys $state(oldmackeys)
+    set state(oldmackeys) {}
+
     ::otr::message::createDataMessage \
-            $state(version) $flags $keyid1 $keyidy $state(x,$keyid1) \
-            $state(x,$keyid2) $state(gy,$keyidy) [incr state(ctrtop)] \
-            $body $tlvlist \
+            $state(version) $flags $keyidx $keyidy $skey $smac \
+            $state(x,$keyid) [incr state(ctrtop)] $body $tlvlist \
+            $oldmackeys \
             -sinstance $state(sinstance) \
             -rinstance $state(rinstance)
 }
@@ -975,9 +1137,9 @@
     variable $token
     upvar 0 $token state
 
-    if {3 in $versions && [queryPolicy $token ALLOW_V3]} {
+    if {3 in $versions && [QueryPolicy $token ALLOW_V3]} {
         return 3
-    } elseif {2 in $versions && [queryPolicy $token ALLOW_V2]} {
+    } elseif {2 in $versions && [QueryPolicy $token ALLOW_V2]} {
         return 2
     } else {
         return 0



More information about the Tkabber-dev mailing list