Status of otr4j & otrr (and OTRv4)

❝The status of otr4j and otrr, including OTRv4 support❞
Contents

This post is published two years later than originally planned. It started out with an attempt to clarify the state of affairs from my side, considering the significant amount of harassment and attacks, of which a lot was based in the mistaken idea that there was malicious intent.

This is a summary of the changes in earlier years and the current state of OTR-related projects, including a bit of history to clear up potential confusion along the way.

History

Some history on my early contributions to otr4j, the presence of multiple otr4j repositories, and emergence of the community-fork.

Contributions

I originally worked on Jitsi’s IRC plug-in (in my personal time). At the time, it was deprecated because of a problematic license, so I did a new implementation based on a more liberally licensed IRC client-library. That sparked the need for OTR to support fragmentation. IRC will only support up to 510 bytes message-payloads, minus some variable amount for nick and protocol. To make OTR work reliably, we would need to fragment messages that surpass this limit.

During the time I was working on IRC protocol support, I noticed a message from Emil Ivov who fixed (… and I can’t find the exact commit/mail anymore) … a byte-array comparison somewhere (I think) around the OTR plug-in. It compared a value with itself, i.e. same byte-array instance provided twice, consequently would always be true. IIRC, it was concerning some comparsion of public key or fingerprint, but I’m working from memory on this. This stuck with me, thinking that if there is one such bug, it might be worth reviewing this code. As I worked on fragmentation, I got (a little bit) more familiar with the otr4j code-base.

At the time I started to touch on otr4j code, OTR protocol version 3 was already part of the code-base and development had slowed significantly. I introduced a new wave of changes with my introduction of fragmentation support, at the time.

Community-fork

After some time, some members from Guardian Project initated the idea of hosting a commonly supported community-fork of jitsi/otr4j. The idea being that we create a common working-space among the various projects that depended on otr4j and in some cases hosted their own fork. Although each of the forks usually had only minor differences, there were differences, and in case of fixes, each needed to be kept up-to-date. Some were lagging behind given not all changes were equally important.

Some of the Guardian Project members created the forked repo and invited everyone with reasonable involvement in any of the most prominent otr4j repos to be involved. Some agreed, at the time, others most likely weren’t interested because their focus had shifted. Given that there were multiple otr4j repositories and multiple teams working on, or at least with, otr4j this made perfect sense. Similarly, some developers were concerned with the practices of requiring signing of a CLA to contribute. So otr4j/otr4j would be neutral, common-ground repository where shared effort would manifest.

The community-fork, though, did not gain much traction. Some of the invited participants were at this point only minorly dependent on their fork of otr4j and/or switched to Jitsi’s original repo. At this point, interest was waning. The Jitsi-team didn’t see immediate need. Guardian Project remained and started off changes with some additional integration-level tests for testing the implementation (essentially) as a whole. There were tests already, but these additions took a stricter, more programmatic approach to testing a full session.

As the community-fork got created, I was enthusiastic about the idea. I had, at this point, found a number of issues and I knew of a few more issues, and it seemed to me that a community-carried initiative would be beneficial for everyone. However, I likely viewed this code-base with a slightly different perspective than others. I was concerned with mistakes caused by a developer misunderstanding the code, as opposed to the occasional unintentional bug that slips through and that could be caught with a peer-review. Now, probably to slight frustration of some developers, I spent some time bringing a few bits of structure in the code-base. Guardian Project members probably had different priorities, and expected only minor maintenance and improvements. This is completely understandable. That’s essentially where effort wound down on their part. I ended up working on the code-base myself. For better or worse, this was an opportunity to improve structure and take better control over the logic and state-management.

Now, as development progressed for years, more impactful changes were merged and no other members showed interest, it seemed irresponsible to have many former/uninterested/inactive owners of a project repository, of which they didn’t care to be involved it. At this point, I checked with and then removed extraneous owners. Of course, the project is public, so any oversight would still be perfectly possible. You don’t actually need any special permissions to access the code-base.

Redesign and refactoring

The otr specification is quite elegantly written. Starting from a very abstract high-level overview, to levels with increasing detail. I’ve read through it many times, looking up many details just to make sure things were in order. Whenever I was reading through it carefully, I was both amazed and amused by how they managed to put (subtle hints at) implementation details in the specification itself, without being distracting, in a way that emphasizes or resolves possible ambiguity or misunderstanding. Furthermore, I am a big fan of how the various state-machines are explicitly defined, such that there can be no misunderstanding of how data is handled and when, i.e. in what state. I doubt people realize, at first glance, how critical it is to have clearly defined boundaries when sensitive data is being handled. However, the original implementation of otr4j and frankly implementations in general, take a more procedural approach to structuring logic, so these clear well-defined boundaries were simply not there. Consequently, developers could make mistakes and mismanage data.

Now, a common response is: “people shouldn’t touch the code if they do not understand what they are doing”. This may be true, but even the original developers may make such mistakes when they haven’t touched or looked at the code for a while. And there is always a chance with (large) contributions, as proved to be the case with contribution of protocol version 3 code. That’s why I started introducing the State-pattern at various parts of the protocol, consequently making the boundaries part of code structure and syntax, thus inescapably strict.

Note that the State-pattern makes mistakes impossible because data and corresponding logic are isolated in a class. Only logic intended for that state is present and thus able to access that data. Furthermore, it makes mistaken assumptions of a specific state error-prone, i.e. upon a wrong assumption you end up in an unexpected class, with access to its dedicated data only. Incorrect logic and assumptions, either are made evident because of an error or are transparently handled with logic corresponding to the actual state (as opposed to the assumed state).

I say “inescapably”, because the State-pattern takes advantage of polymorphism to allow for a protocol’s implementation to consist of multiple classes, with each class only implementing (or rather representing) one specific state. Let’s take, for example, the state-transitions of core messaging (Plaintext, Encrypted and Finished). In the Encrypted messaging-state, as a secure session is established and end-to-end-encryption is offered. That one state handles the variables containing sensitive data, while the other states, plaintext and finished, have no need for this data. This means, that if a State-pattern is properly implemented, all the logic that is concerned with encrypted messaging-state, must by design be present in the Encrypted messaging-state, and only in that state. This means that a developer is forced to understand the structure and consider the various boundaries, instead of arbitrarily accessing variables in a huge class that manages state with booleans and/or other variables.

The structure, at this point, forces the developer to understand the protocol before making the changes:

Strict, managed, atomic transitions are essentially how a protocol is expected to function. Implementing such protocols as a bunch of variable-manipulation is careless convenience of the programmer but by no means “the only way to implement”.

OTR 3 had a number of state-machines, made explicit in the specification:

For each of the state-machines, each of their states would get a separate class. One might argue “OOP makes things excessively complicated” which is superficial. OOP allows us to isolate, manage and protect sensitive data. It forces the developer to understand states, separation of concerns. It becomes significantly harder to perform an incorrect or unanticipated combination of actions that may result in confusion of states, unexpected results or output. Furthermore, the state-machines form artificial boundaries: the transition is executed in the class that manages the current state (variable) and swaps out the old state for the new when a transition occurs. Even if an incorrect combination of actions is performed, if the transition has occurred, access to former state’s data is lost. In an incorrect series of actions or assumptions, you might run into formerly mentioned error, or you might perform an action properly because logic is located at/connected to the corresponding state as opposed to the assumed state. In both cases, the transition is done and data is cleaned up.

Each class by itself will be better focused on its own concern: that one state it represents. The finished state, for example, ends up very small, as you would expect, because most actions are either not possible or not allowed, due to having to transition back into the plaintext (plain, unencrypted) state first.

The Finished state exists such that when the other party ends end-to-end-encryption, your client transitions into the Finished-state first, where OTR will refuse to send the message you might have already been typing, thus preventing you from accidentally sending messages in the clear.

Yes, multiple classes for managing one protocol’s state may still be annoying, but mostly when you try to cut corners or fail to understand the scope or implications of your change. Note that OOP’s infamous “banana, monkey and the whole jungle"-problem is not an issue with proper design and structure.

OTRv4 effort establishing

At some point, the emails regarding draft version for review appeared on the OTR mailinglist. As the draft reviews were posted, I started looking into OTRv4. I had a freshly refactored, by this time quite richly tested, code-base with a lot of improvements. It was the ideal test-case to see how well it was possible to introduce the OTRv4 mechanics into this new, stricter design. This mostly went fine, and it is at such moments when the need arises for a distinction between an “encrypted” status and individual states as there are now multiple states that represent an “encrypted” messaging-state, although for different versions of the protocol.

Furthermore, while others were reviewing the drafts by reading and providing (general) feedback, I was able to look more closely at specific matters of implementation, including the descriptive imperative logic for the DAKE. Even mathematical errors are more obvious when one has to look in detail, e.g. mistaken naming of constants because different specifications use different naming conventions, mistaking a modulus for an order.

Sidenote: it is best to keep implementation concerns separate from a specification. The attempt to describe an algorithm in procedural steps at the low abstraction level of programming, does not contribute to clarity or understandability. The mix-up of concerns make the specification hard to read and will invariably include irrelevant details like how one replaces one key by another expressed through pseudo-variable-names. At this point, the reader needs to keep track of “variables” with varying content. These details obscure the true intentions of the process. One better defines names (labels) for various pieces of data (by purpose) and have the developer sort out how they juggle data and variables “in flight”. It makes descriptions declarative and more concise. (Specifications often add snippets of code or a reference implementation isolated in an appendix.)

I put in effort to be precise and accurate in implementing what is stated in the specification, such that I could uncover even small errors. I made it a point to provide detailed feedback w.r.t. errors or inaccuracies in the specification, considering that I had a different perspective on the matter with more detail and closer to (implementation) reality. This also uncovered subtle inaccuracies like confusion of mathematical symbols (from standards with different conventions) and logical errors. I also made it a point to, as part of the refactored implementation, provide safety-nets for handling all-zero values and other such problem-indicators which could catch yet other classes of issues.

Moving repositories

Now, from what I could tell at the time (as I wasn’t involved yet) Jitsi’s otr4j repository, ironically, didn’t used to be the primary/original repository for otr4j, as indicated on Github. Instead, someone else was first in cloning their original Google Code-repository and put it on Github. The Jitsi team forked from that, leaving it in a bit of a misrepresented state where Github would show that repository as a fork of that first repository. This was later corrected in a move of several repositories, making jitsi/otr4j the actual original repository, and otr4j/otr4j an immediate fork of it. This truthfully reflects the situation concerning otr4j.

Refactoring otr4j

Over the course of several years, starting with the creation of the community-fork and continuing when the OTRv4 implementation was added, is the effort of refacoring and improving the code-structure, including abstractions at hospots and points of friction between OTRv3 and OTRv4 concerns. Some programming wisdom states that one can best determine the necessary abstraction when there are 2 or 3 actual concrete implementation needs, because by that time you’ve left the domain of speculation and have concrete designs/concepts to work from.

This listing is not exhaustive, but extensive enough to make the point.

I discussed some of this work in an earlier post.

Now, it is important to realize that “OOP” (as it is generally understood) has gotten quite a bad reputation. That reputation, for many cases, is deserved. However, that doesn’t invalidate right ideas. And the general understanding that a very procedural structure means that much of state ends up in one place, and one needs to be very careful to evaluate the proper conditionals before updating state.

I may have a somewhat unexpected biased view in that when I was still at school, I was following, i.e. reading, the Subversion development mailinglist (discussions) for a long time, and learned a few things about their development process and ideas. I noticed that the general ideas of OOP, individually, all have merit and all have their reasons for existence. So instead of generally hating on OOP, I was always looking for how to apply it properly (and sparingly), instead of throwing in the full “enterprise” “OOP” arsenal of “best”-practices. And, as I have mentioned before, otr4j is small/contained enough as a library that it is feasible to make such improvements.

It is for those reasons that the State-pattern is an obvious candidate to improve the implementation: it separates and isolates critical state in a small object that represents exactly that state, and includes logic that manages precisely that state, i.e. those variables/fields. Meaning that, from that point on, there can no longer be confusion on the state, instead there can only be confusion in the logic leading up to calling that logic. It pushes potential issues further outward and sets up guards against the critical inner cores. Additionally, there is the advantage that the OTR specification already identified states explicitly. There already has been proper consideration for the strict separation between states. (Which is not surprising in itself, given that this involves a protocol.)

At the time, I set the following 4 goals for the implementation:

  1. Correctness of protocol (and) implementation.
  2. Encapsulation of cryptographic material to prevent mistakes, misuse, excessive exposure, broad dissemination.
  3. Design that prevents or makes obvious programming errors.
  4. Simplicity: restricted implementation to only as much complexity and abstraction as needed.

Although by now this should be obvious, ① because we discovered some serious issues causing problems in interoperability, correctness or security; ② introduce state-machines to manage state correctly independent of larger context, and to make things manageable and understandable; ③ to benefit from the type-system and design-/structure-boundaries to make more critical mistakes impossible to make; ④ to eradicate unnecessary complexity from excessive exception-handling, instantiation of stateless classes just to reach utility functions, etc.

The few full-session tests that were added by Guardian Project, were very helpful even if not ideally implemented. I later refactored the tests to throw exceptions as soon as an expected message was not present in the queue, instead of having to wait out a timeout. These tests were single-threaded, so if the message wasn’t there yet, it wouldn’t have arrived anyways. This was mostly a matter of using the proper functions for queue-interactions and the tests would be fast and also immediately fail upon any unexpected behavior. Furthermore, whenever possible including for other fully integrated tests, I used the smallest possible queue-sizes available, so both with too few and too many messages there would be exceptions. This makes it easy to find any behavior deviating from the expected or usual. The original addition of the tests themselves was greatly helpful, because it allowed for more invasive changes while knowing there is some baseline to compare against.

Artifacts of protocol version 1 and 2

Some artifacts of protocol version 1 and 2 are visible in how the code-base was implemented. Instance-tags were introduced to distinguish between multiple clients on the same account, but some of its handling was left implicit. Consequently, there was some need for assumptions w.r.t. the active instance-tag, which caused state to be misrepresented in the UI. The “slave session” (the given name in otr4j for any number of outgoing sessions corresponding to an account) was previously introduced to represent a single instance-tag (client) but as a recursive reference, thus resulting in unnecessarily duplication or mixing of state, and consequently a need for additional understanding on developers. This was one criticism that I set out to solve differently in otrr.

Extending otr4j with OTRv4 support

Extending the refactored otr4j code-base with OTRv4 support entailed some new challenges.

First of all, there was no suitable elliptic curve Ed448 implementation. I wrote a preliminary “amateurish” implementation to get things rolling. It is currently still being used, but ideally I just want to move away from it. The slight difficulty is that OTRv4 requires some primitive curve operations, so grabbing just any Ed448 implementation is likely to fail due to lack of access to low-level operations. The protocol version 4 specification requires curve operations for key-exchange ECDH operation and it’s SMP protocol (among others).

As mentioned, I put some extra effort in to implement the protocol precisely (as reasonable) according to the draft, such that I could identify inaccuracies in specification text. As necessary, I reported bugs or proposed corrections.

Having two clearly distinct protocol versions, there was a better opportunity to progressively separate high-level session handling from outgoing instances. Furthermore, support mechanisms such as an indication of a status “Encrypted” that tells the library in logic, or the client, that a session is encrypted regardless of protocol version. The introduction of new algorithms for DAKE, SMP and subtleties in state-management guide necessary abstractions in the library.

Due to the introduction of a new (draft) protocol, I also adopted more practices of (sanity) checking results and intermediate values, and for example doing checks for all-zero values. That way, in case of logic error in an algorithm, it would also be uncovered. Similarly, with inaccuracies, confusion of mathematical symbols in spec, etc. These errors, if missed upon reading, often get caught with extra attention during an implementation or possibly by failure of the program (or test).

A late addition, is the use of a composite return-type for decrypted content with context (instance-tags, valid and/or error indication), i.e. state within which the message was processed, as well as the message. This makes important identifiers and indicators explicit, thus avoiding need to guess in what state that session’s protocol is in. I also moved to explicitly mentioning outgoing instance in function calls such that there cannot be a “bad default” or wrong assumption during processing, and reduces the chance for false or mis-representations of an OTR-session in the UI.

Reviewing and annotating (fork of) OTRv4 spec

Let’s discuss the most significant issues here. All of the comments can be found at the forked repository with review-comments.

  1. To verify an incoming message, we first need to rotate the keys. This requires that key rotations are performed provisionally, i.e. it must be possible to roll-back the rotations, as we might discover that a message is invalid or malicious. (Note that this includes any potential message-keys stored to skip to the next ratchet.)
  2. The “old” idea, that we can queue messages to be sent as soon as an encrypted session is established, is overly ambitious. This most likely originated from protocol version 2. However, with introduction of instances in protocol version 3, one may at any time establish sessions with multiple clients. There is no way for us to know whether the first session that is established is also the one intended for the messages. Furthermore, with OTRv4’s stronger cryptography and hybrid scheme, it is very likely that another “old” client present will establish a version 3 session faster than this client will establish an OTRv4-session. Consequently, one would likely send the queued-up messages to a version 3 session. (Which is possibly at a problematic location and/or presently out of control.)
  3. There are also issues that already existed in previous versions of the OTR protocol that were never properly addressed or insufficiently highlighed. Examples of these:
    • the encoding of content to be encrypted, as it is left implicit. Is the original message basic ASCII, UTF-8 encoded or full-blown HTML-encoded content.
    • Extra Symmetric Key, which allows for specifying a context and other data, but with no established convention was practically unused even if it had potential. (Guardian Project had previously attempted to establish some convention, but with limited success.)

There are many smaller comments, points of consideration, and other remarks. I kept a record in a public repository such that anyone may review my thoughts on the matter. There are notes on the smallest matters, in part for nit-picking because I want it to be correct, but also to avoid anything from slipping away that requires some consideration, even if later determined incorrect.

Designing and implementing otrr

otrr is very much a project that includes the “lessons learned” from otr4j, both refactoring and OTRv4 implementation. In particular, the change in design necessary to make it work with the introduction of instances in protocol version 3. Many parts of the implementation roughly in line with the refactored/redesigned otr4j, benefiting from having learned the subtleties that exist in the protocol. Rust is reasonably different in a lot of areas that can be of benefit in making an implementation more straight-forward and more concise. In some ways, Java is only convoluted because of bad practices, so ironically, writing in Rust sometimes emphasizes what solution would work better for otr4j.

The implementation is started from scratch, and aims to be straight-forward and with no more abstraction than necessary. The goals are very similar to what they are/were for otr4j. Make it work. Fail upon incorrect, illegal or anomalous behavior. A similar construction for the state-pattern is applied, making state transitions for messaging-states, (D)AKE-states and SMP(4)-states a generally supported mechanism.

Incorrect (or malicious) use of the protocol is, to some extent, mitigated by the strict state handling. Want to start or end SMP in the same message that also ends an encrypted session? The messaging-state transitioned away at session end, and now SMP is no longer accessible. The strict state handling provides confidentiality that would otherwise have been solved with a lot of if-statements, conditional branching and hoping that you properly checked all the right variables at all the right locations.

Some right choices:

I have subsequently adopted these changes in otr4j, because they worked out well for otrr and provide their own benefits.

“Echo-network” and interoperability

After the implementation of two libraries with implementations of OTR version 3 and 4, and due to lack of early adopters, I started implementing a minimalistic mechanism for client-to-client communication, with a simple “relaying server” that provided some insight into in-transit messages, purely for testing and observation purposes.

“Echo-network” is a relaying server, a trivial protocol for sending messages and addressing another client(-connection) and some functions to assist in client-interactions. It is deliberately minimal, as it only needs to connect two dummy-clients in order to see library implementations interact at the “integration”-level. The improved otr4j implementation, otrr, and the original otr4j may all interact with each-other. In particular, otr4j and otrr interacting uncovered some early bugs in both implementations, that were quickly fixed. The observability on the relaying server showed (D)AKE messages and encrypted content passing by, thus proving proper functioning of the implementations. And the ability to maintain a long-running established OTR-session proved that at least these two implementations were aligned and in-sync. If not without bugs, then at least symmetrically buggy, where OTR session-management is concerned.

This trivial relaying server was not further extended, as this already entered a phrase of prolonged harassment.

Issues

High-level commentary

Let’s discuss individual packages and comment on high-level design choices and their impact and consequences.

General

API (net.java.otr4j.api)

The api-package contains all the typical types that the user/caller interacts with. The API-package becomes more important as access to classes get more restricted. Access can be more restricted because proper code-structure allows a lot more of the logic to be strictly internal, i.e. private and package-private.

Crypto and Crypto/Ed448 (OTRv4)

All cryptographic logic concentrated in a package. Note to distinguish the logic for handling cryptographic concerns, because OTR3 session-keys and OTRv4 double-ratchet are internal logic to state-management itself.

Ed448 elliptic curve is provided in a separate package. This thin wrapper allows for easily swapping out implementations, given limited support of Ed448 with primitive operations exposed is available in Java-runtime or dependencies.

I/O

Fundamental logic for parsing and encoding OTR-encoded payloads, basic message-types and fragment format. Specific variations of OTR messages are contained in messages-package. Hides most of the encoding internals, while exposing the API for encoding specific messages and fields. Offers an interface for composite OTR-encodable types.

Messages

Specific OTR-encoded messages in use by OTR protocol version 2, 3 and 4, AKE (OTR2/3) and DAKE (OTRv4).

These classes are public. Implementations focus on OTR-encoding, verification and validation. These implementations make use of the established API for performing OTR-encoding. EncodedMessageParser provides the entry-point for parsing and decoding specific OTR-encoded payloads. Validators and ValidationException provide some common validation logic and a package-specific exception for failure-cases. These message-classes allow for easy handling while instances are being processed, and easy serialization when the message is sent.

Session

General OTR session-management, message- and fragment-handling, periodic maintenance.

Assembler provides both the general assembling logic and delegation to either the OTR 2/3 in-order assembler or the OTRv4 out-of-order assembler. Fragmenter partitions a message according to protocol rules and (chat) network limits.

SessionImpl provides the core logic for routing messages, interacting with the host application, transitioning messaging-states, etc. This class is not itself exposed. Instead, instances may be provided as Session (interface, from api-package). SessionImpl provides session management, message processing and directing, (D)AKE.

Current implementation still relies on recursively calling SessionImpl instances, which is sub-optimal now that OTR 3 introduced the notion of instance-tags in order to distinguish multiple clients, i.e. multiple established OTR sessions, on the same account. By recent separation of functionality between interfaces Session and Instance, we preselect for eventual separation of general session management from OTR instances. (Note that otrr was designed from the start with this distinction and multiplicity in mind.)

Session-API

Generic types serving as common basis for both SMP (OTR2/3) and SMP4 (OTRv4), for use in session management logic.

Session-state

The representation of the messaging-state of OTR.

All messaging-state implementations, except for Plaintext, are internal to the package. Only the states (implementations) themselves have the ability to prepare transitions. Consequently, one needs to ensure that the logic within the package is sound and correct to ensure that messaging-state transitions happen as and when they are supposed to. The core logic is concentrated in this one package. It also means that messaging-state implementations have full agency to be extremely strict with deviations from the protocol and incorrect, incomplete or suspicious messages or deviations during processing.

Messaging-state transitions

State.java (interface), StateEncrypted.java (interface), AbstractOTRState.java (abstract), StatePlaintext.java, StateEncrypted3.java, StateEncrypted4.java, StateFinished.java are all part of the messaging-state state-machine. All together forming a self-sufficient, internally consistent state transition system that has complete control over transitions into encrypted messaging-states. Together with the fact that only the Plaintext state is accessible from outside the package, it means that unless the state-machine is satisfied with proper input, no transition into an encrypted state will be happening, and no bug or oversight or logical error outside state-package can inadvertently access data incorrectly or force an incorrect transition or assume an incorrect state. The only influence (general) session management has over the messaging-state is whether to accept or ignore the transition requested where a new state-instance is provided. Either keep the instance, which might be an “Encrypted” messaging-state, or drop it. Regardless, only an (internally constructed) state instance passed back to session management may be handled.

Outside of the package, the only influence is whether correct input is passed in, and whether we accept or reject the resulting state transitions. Within the same package, logic is able to construct other state instances, but not manipulate internal workings of a state. Only within the class for a particular state, are the variables for that state accessible. Consequently, if one wants to verify (or change, for that matter) how data in the “Encrypted”-state is handled, only logic within the same class is able to do so. Verification and analysis of such handling of sensitive data is therefore almost completely concentrated within the one corresponding class. Deceptive logic, shortcuts and developer mistakes, will have to “fit” within this restrictive structure too, i.e. fit the State-pattern. (Note that you cannot arbitrarily define public functions. They require a counterpart, and consequently justification, for other states as well. Again, contributing to the understanding and proper structuring of logic.)

Calling it “restrictive” is slightly misleading, because vast numbers of possible errors, whether logical or by omission or by misunderstanding or by confusion, are made impossible due to structure. Incorrectly timed actions, e.g. handling SMP while not in “Encrypted” state, will simply fail with an exception. This not only (forcefully) prevents mistakes, i.e. no state confusion possible, it also signals (or mitigates) logical errors leading up to this. Note that, because each of the state-machines themselves determine when to transition, maliciously constructed messages that deliberately violate protocol to benefit e.g. state confusion, may not succeed. For example, a message that both ends an encrypted session and also starts an SMP exchange, will fail, because the TLV_DISCONNECT payload is processed including the state transition. Even if we (attempt to) process the SMP TLV next, the transitioned state will not provide us access to SMP. Note that this isn’t about properly checking conditionals: the logical path to SMP no longer leads to SMP, because the session transitioned away.

Session/AKE and Session/DAKE (OTRv4)

State-machine for the AKE. AuthState (interface), AbstractAuthState (abstract), StateInitial, StateAwaitingDHKey, StateAwaitingRevealSig, StateAwaitingSig represent AKE states. SecurityParameters is structure that contains the security parameters resulting from successfully completing the AKE, then passed on to the session in order to initialize the encrypted messaging-state. AuthContext/DAKEContext represents the interaction with its callers.

The OTR 2 and 3 Authenticated Key Exchange.

Each state is constant/final. Upon construction of an instance, input is verified. Only fields/variables that are relevant at that moment, exists. A timestamp is tracked as a technical aid to determine whether a newly initiated AKE should override one instance’s (i.e. instance-tag) current AKE progress.

We do not maintain/manage all state-variables simultaneously, so there cannot be confusion. There cannot be “out-of-sync”, or wrong logic (as-in mismatch between logic and state), or outdated parameters. We do not manage timestamp × version × r × remote-public-key-hash × remote-public-key-encrypted × remote-public-key × local-keypair × shared-secret all at once and any combination of. The specification is clear on which state transitions into which other state when receiving which message. Logic is tied to an AKE-state. There cannot be a matter of incomplete transitions, or state-confusion, or inconsistent state, partially updated state. It is easy to maintain internal consistency as fields are not updated. AKE-state is either consistent or fails to transition altogether. All but initial state are package-private, meaning that these are accessible only from within the package. The only way to progress through AKE protocol, is through logic of the package. The package focuses completely on AKE (OTRv2/3) protocol.

The same approach is taken for OTRv4. I leave it as an exercise to the reader to inspect the state and corresponding logic if so desired.

The OTRv4 Deniable Authenticated Key Exchange (DAKE).

Session/SMP and Session/SMP4 (OTRv4)

OTR 2 and 3 SMP state management (subpackage smp) and exception handling.

OTR 2 and 3 SMP state-machine (subpackage smp). It represents the state-machine and state transitions for OTR 2 and 3 SMP, with implementations for each of the strictly defined states.

The OTRv4 SMP state-machine (subpackage smpv4) represents the valid transitions, i.e. the processing and validation logic, the message-handling and -construction, and state-transitioning. The state-machine brings strict handling and separation of states and state-data. None of the actual state classes are publicly accessible.

OTRv4 SMP messages (also subpackage smpv4) are implemented to serialize into bytes according to OTR-encoding, and decode back into workable message instances. None of the actual message-classes are publicly accessible.

Arguably, one could say that these could be captured in an array instead of separate types, as is done in SMP for OTR 2/3, though we’re working with multiple types of values here. We get type-safety from defining fields explicitly, as well as decent names to refer to. Unlike (“enterprise”) “OOP”, types are declared with package-private fields, because we’re fine with accessing (final) fields directly. Let’s not make things more complicated than necessary.

The OTRv4 variant of the Socialist Millionaire’s Protocol. Classes in this package are generally in either one of two functions, either part of the SMP state-machine (consisting of SMPState, StateExpect1, StateExpect2, StateExpect3, StateExpect4), or part of the SMP message-payloads sent back and forth between two clients (consisting of SMPMessage, SMPMessage1, SMPMessage2, SMPMessage3, SMPMessage4).

The restricted access may be considered excessive from the point-of-view of “Why? It can’t really hurt, can it?”, but the advantage is that we know where SMP4 processing must happen, because this logic (and data) is not accessible anywhere else.

Reviewing this package would ensure that SMP messages are properly processed. What is not covered is whether the SMP messages are properly passed into and out of SMP4. Those are problems of a different quality. Again, purposes of classes are rather singular and straight-forward, and restricted access means the area of influence/manipulation is relatively small.

Utils

Generic utilities not already available in JDK. In a separate package such that these are grouped and independent from specific otr4j logic. Following the standard naming scheme that takes the plural of a class it provides utilities for. No instantiation; public static functions; instantly available. Obviates the need for dependencies for trivialities. Having utilities available to provide common logic ensures that other classes can focus on OTR’s mechanics instead.

Attacks and lack of criticism

Now, I’ve received a lot of criticism and attacks over supposed disingenuous intentions. However, none of the attacks are actually attacking my work. Furthermore, I have provided some significant surface area for attacks, not only in terms of quantity but also in scope and reach of the things I’ve worked on and written about. Scale and scope were selected intentionally as I was/am trying to tackle more challenging and sometimes complicated questions or challenges.

False claims of “bloated”/“corporate” approach

One oft-heard complaint is that of turning otr4j into a bloated code-base, though none of the criticisms are based on actual inspection.

On the surface, the code-base might seem bloated, but there are a number of changes that resulted in the current form:

Apart from the comments here, in the sections on refactoring, I write in more detail on the kinds of changes that have been applied.

At no time has there been discourse concerning these “criticisms”. And at no point did clarification result in discussion. The attacks are founded in shallow harassment and incompetence rather than substantial criticism. And if there would be substantial criticism on my results, then they have not made these errors/mistakes known in last 7+ years. I would expect clear and obvious errors in my work to be a more effective way of attacking me. (And there were opportunities, because I myself did correct errors and bugs over time.)

Direct results

The direct results of the work on otr4j/otr4j and adoption and reviewing of the OTRv4 spec are obvious. My personal comments, whether major, minor or emphasized subtleties, can be found in the forked otrv4-repo, and in some stray cases (minor) comments are only present in the code-base.

Now, concerning the direct results, there were plenty of opportunities to attack:

  1. My work on otr4j/otr4j, which could have been reviewed and attacked. Not only the code, but also the myriad of FIXME (well, not anymore by now) and TODO comments that I left (for myself and others) that document all of the aspects and (minor) details that were identified but not yet resolved/considered. Which would give quite significant insight in the process as a whole.
  2. My review criticisms of the OTRv4 protocol specification. These criticisms were gathered during the implementation of OTRv4 support in otr4j.

Note that any comments and issues previously reported concerning OTRv4, were reported at the time I was already sufficiently caught up with the implementation to touch upon these areas, and either through interpretation or by proof of a failing implementation, found out there were issues in the specification. There have been previous attacks stating that the reports were false, as a way to distract from making progress. This is simply not true. Several issues will have references to the implementation and traces/progress reports that indicate that execution failed.

Indirect results

Indirect results of the effort on otr4j/otr4j include some blog posts and ideas, including a redefined notion of OOP, in which I tried to eradicate some of the unwanted aspects of “OOP” as it is most often criticized. Essentially in the same fashion as I applied OOP to enforce a beneficial structure to reduce bugs and mistakes due to developer misunderstanding.

  1. Various blog-posts, although earlier blog-posts may obviously contain more material to argue that is discussed further in later posts.
  2. Concept of a “unified” OOP, which criticizes the whole notion of “OOP” as it is generally explained as in incomplete arbitrary selection of properties and aspects. It being a fragmented notion of a larger whole that is known by most people but not discussed as a single all-encompassing notion, to the detriment of the individual ideas.
  3. Minimal objects in software-engineering, as a guide and idea for minimizing objects. Emphasizing the notion of making an “object” a purely practical boundary and composition for managing state, enforcing invariants, attaching necessary logic.
  4. Or the post on simplicity, which as a tangent, identifies 3 dimensions that (seem to) contribute to complexity as a general concept. Now, this is admittedly a rather strange tangent, but it is interesting to see exactly those dimensions continuously pop up time and again. Off and on, I’m still looking for examples that cannot be classified in any of those three dimensions, i.e. counter-examples that prove the idea wrong.

Specifically the work on OOP and software design originate from long-standing criticism that I have voiced quite often, and I am still fully backing. One of the most prominent that we rely too much on arbitrary boundaries such as arbitrary fixed numbers and too little on the natural boundaries that emerge from properly applying e.g. the notion of OOP. Now, “properly” applying is the tricky word here, because without proper understanding it is not possible to “apply” it. That’s why these two significant blog posts on software engineering come in, which are ironically much more a properly composed whole of all the ideas already out there, than they are my own creation. The insight that I predominantly provide, is the insight of how the things fit together that were all floating around there for a long time. I resolved the inconsistencies in the many stories.

None of these indirect results were intended as “the next step”. These were topics that became significant matters and I decided to look into them as a tangent. After spending a relatively short amount of time on them, I jumped back onto ongoing projects. Mostly, however, attacks picked these topics as excuses to decide that if I don’t spend 100% of my waking hours on OTR or otr4j, that I’m not serious about the matter.

Academic feedback labeled “malicious”

There was a relatively short but very aggressive and serious attempt at labeling what should be understood as “academic feedback” as malicious. A specification for a security protocol should be precise, accurate, exact where possible. I provided feedback for inaccuracies and ambiguities. I pointed out issues that would go wrong upon retries. And so on. There was an effort to label providing this feedback as malicious. In particular, that my criticism, confusion or failures were “an attempt to stall the effort”. However, various errors and inaccuracies were discovered through this feedback.

In actuality, a specification for this kind of purpose should be held to a high standard. The claim that, “Yeah, a cryptographer should implement this, actually” is bullshit. The specification needs to be correct. If you mix up symbols and formalisms in your mathematical parts, you have made an error. The reviews exist to find those errors and have them fixed. There is nothing malicious in making an authoritative document clear, understandable and correct.

Even just a year or so ago, I noticed an ambiguity, something subtle. The specification documents using Edwards encoding. X448, i.e. the key-exchange based on Curve448, uses Montgomery notation but takes a hash of that value to be used as the key-exchange secret. The specification does not mention to perform an X448 key-exchange but instead provides the pseudo-code for the key-exchange. If you go by the reference to X448, you would end up with different shared secret (based on Montgomery encoding) than you would if you go by pseudo-code (essentially the same algorithm) and specification’s encoding, i.e. Edwards encoding. This would be fine if you go by specification alone, but may fail if you recognize the corresponding RFC that is mentioned or because you want to use a library/dependency for key-exchange.

Reviewing otr4j/otr4j

As for otr4j, by now –given the extensive discussion in previous sections– it should be clear that there is much more isolation and separation of logic and state than there was before. It is much more feasible to review individual parts, and consequently much less of a challenge to take, e.g. a specific package, and try to comprehend it fully. Call them packages, modules, namespaces or “little programs”, it doesn’t matter. The fact of the matter is that, once it is established that there is no malicious use of reflection, one can simply rely on access modifiers to scope the impact. Most state-machines are completely restricted to their own package, meaning that no logic outside of the package can reference the state by specific type. Incidentally, a state-machine will expose its initial state directly, if there is no risk involved. Regardless of the chosen approach, it is the logic within state implementations that determines whether transitioning is justified, so there is little room for manipulation. (And if there was, this would be a bug in the implementation.) Consequently, a particular package can be reviewed by validating the API and the package itself.

Contributing back to jitsi/otr4j

Concerning contributing the otr4j/otr4j work back to jitsi/otr4j. Apart from the hostility from multiple parties, there are also multiple issues. First is the matter that multiple people have contributed to the otr4j/otr4j repository, meaning that I don’t have full control over the work. This is not an issue for the community fork, given that it always set out to be a collaborative effort under the original open source license. I do not personally have an interest in changing the license, but to contribute back I would have to change the license to align with the changes in jitsi/otr4j. Furthermore, concerning the CLA requirement of jitsi/otr4j, although I originally signed the CLA, it also means that any contributions I offer are required some transfer/grants of rights and guarantees. I cannot grant these rights for other people’s contributions.

The otr4j/otr4j community fork, that now includes the implementation of OTRv4, is simply set up as a shared, common working-space for otr4j. Given that an acceptable license is in place (LGPL-3.0) and there are no business interests that conflict with this license, there is simply no need to change it. To my knowledge, community interests are sufficiently covered. It is why the community-fork was started in the first place. This has been discussed before, and I then also stated that I cannot offer the contribution. Even if all contributions are from people who have already signed the relevant Jitsi CLA, I could still not contribute these changes back on their behalf, because there is no guarantee –or at least I cannot claim these guarantees– that the contributions made to the community-fork were made with the same considerations. Furthermore, I cannot grant or claim the requested conditions by proxy for other people or have permission to do so.

Elligibility for OTRv4 funding (nlnet)

Another thing I was attacked over quite extensively, is the OTRv4 nlnet-funding. I am not part of the team that defined OTRv4, and never was. To my knowledge I was/am not elligible to nlnet funding that was assigned, and there also was never any talks on elligibility. I started extending (the fork of) otr4j with OTRv4 support because I had just established a well-structured/-designed stable code-base, and the spec started to stabilize to the point that I was willing to spend time on an implementation regardless of whether and how it would change. This, in part, is because I was confident about the structure that was currently established and the amount of tests available to ensure that protocol version 2 and 3 support would not fail without noticing. For another part, me working on the implementation of the spec meant that I had insight in the kinds of errors that one catches during implementation, so I figured I could provide timely feedback.

As I am and never was part of that team, I also had no involvement at all in defining their goals. As an answer to some of my attackers: “I never made any of these promises.” and received no funding or money of any other kind for the work. This was all motivated based on my own principles and goals, and supported by my own free personal time. The motivation was many-fold, given that I had already used the code-base to establish a better structure, attempt of take more control of the code-base through checks, static analysis, and eradicating (classes of) bugs, enhancing predictability. With OTRv4 in the picture, I had the possibility of diving (deeper into) elliptic curve cryptography and figure out those aspects, as well as confirm my (at this point unproven) claims that this structure and design are beneficial.

Futhermore, with an implementation of OTRv4 alongside the specification, it would be possible to find more errors and subtle issues, and given that such a specification is supposed to be understandable by developers (sure, maybe with some background in cryptography) it would help to “proof-read” the document and resolve any ambiguities and issues that might cause confusion during implementation. As was evident on multiple occasions, subtle issues do slip in because there is only so much overview one can possible have when working on a sizable text. These errors include: unclear sentences that might be interpreted in multiple ways only one being the right way, steps skipped/missed or errors describing algorithms, other notations or incorrect names, typos, etc. Sometimes the implementation would point out a mistake due to a sequence of steps that isn’t at all evident at first glance. Sometimes a simple confusion of mathematical symbols was overlooked by everyone, but as I tried, deliberately, to be as exact to the words of the specification as possible, I would run into the confusion during implementation. (Note that obviously one can reason about a trivial mistake in wording and implement it properly, but there is no guarantee that everyone identifies and understands the same mistakes and ambiguities the same way. So I figured, if I can point out the things that are unclear or ambiguous to me, that fixing them in the specification meant that no-one else would have to scratch their heads over them.)

On the topic of communication in private: there was no private communication on OTRv4. The feedback was provided through github issues and PRs. I tried to respond quickly if possible, so upon receiving a notification I would try to follow up.

Note that this effort of implementing OTRv4 in otr4j, was done in my personal time next to a full-time job. Note that this entails learning the things I didn’t yet know about (this flavor of) elliptic curve cryptography. otrr was implemented with a similar approach, although for different reasons: otrr was a nice opportunity to have a reasonably-sized (initial) project in rust, a possibility to create a second implementation such that implementation can be tested for interoperability and debugged, and as a way of having an implementation of OTRv4 available that isn’t running on the JVM or some other virtual machine/runtime. (With the caveat, that almost anything can be explained as having a runtime exception bare-bones embedded system development, but here I’m referring to something more extensive than the C runtime, such as a language-interpreter and virtual-machine.)

Supposed “final project”

Apparently, some manipulation has been going around in the background, claiming that my work on otr4j is supposed to be some “final project” for educational purposes. That was never the case. This links to other false claims. There were no planned or agreed upon “completion” or “performance” or “prestige” “awards” or whatever else might have been claimed. I started working on otr4j for bug-fixes and improvements. I took the opportunity, considering the size of the code-base, to learn from refactoring and improvements efforts as this code-base is small enough to comprehend. In part, that’s the reason why I wrote the other (seemingly unrelated) articles. I applied further restrictions and static analysis to guard against kinds of deterioration and violation of structure. At the moment OTRv4 appeared to be suitably maturing, I jumped on that opportunity. There are no others involved in the effort, except for the few early on, and I didn’t kick people out with nefarious intentions. And, regardless, commits correctly reflect involvement.

Supposed company involvement

There were sudden rumors of involvement of a company, then of use of a work laptop. I used personal equipment and did the work in personal time. There have been efforts to fabricate claims of involvement of other people in the implementation effort, and subsequently with some effort of intimidation for “exposing an illegal project”, while it is obviously legal in NL as funding for the specification came from here. There have been efforts (a second time) to fabricate claims of spending company time on otr4j. The second time was also conveniently fabricated by refusing to acknowledge that I spent time working on several courses.

The improvements to otr4j are my personal effort and contribution.

Retaliation, not principles

There is also a multi-year effort to claim I should hand over the work and results I produced. This is done in various manipulative ways. Notice that the primary concern cannot be human rights, principles, or civilized values. All of my effort on otr4j (and more) are published under open-source license. If the primary concern was security/privacy/confidentiality/human safety, then they could have forked the project and continued. Nothing in the way I published my contributions enforces them to interact or cooperate with me. These attacks, these claims that I should hand over my work, these claims that I should prove myself, are all based on misguided revenge efforts, not concern for human rights. Their actions do not uphold any values they claim to support and/or protect.

Stolen Yubikey-nano

Part of the ongoing harassment effort was excused with bullshit claims of “different way of living” without prior indication. There has been no explanation why I should suddenly shift my expectations in NL on this matter, or why I should suddenly consider criminal behavior common-place and likely.

The scenario most likely went like this,, i.e. I did not see him do this: a visiting utilities worker that slipped out for a moment behind my back when I held something up to create enough space and reach for him. He was gone for some tens of seconds, enough to grab the yubikey-nano from another room. This was likely done in an adversarial effort in contempt of my ongoing effort on otr4j, otrr, OTRv4 spec feedback, etc. There is a very likely connection with either Eindhoven, with one of a few possible explanations, and/or local abusive people.

I know they were aiming to acquire the master-key, and possibly encryption key. The Yubikey-nano only contained subkeys for authentication and signing, so it was easily made useless, i.e. authorizations rejected and subkey revoked. I immediately created empty commits to otr4j and otrr repositories, such that latest commit, signed with a different (secure) subkey, consolidated prior commits signed with the revoked subkey. I made mention of it in README.md.

8 years and ongoing of bullshit attacks

The above are just an exerpt of most prominently targeted attacks that can be lead back to otr4j. The last 8 years have been an ongoing effort to attack me over bullshit reasons. I have posted on many events prior. I have yet to discover why I should “suddenly” normalize this as expected standard of living in the Netherlands.

I haven’t gone into detail about the initial few years, in which there were regular “surprise events”, occasionally also requiring sirens. I later overheard discussions on whether or not OTR protocol is or isn’t illegal. There were likely some efforts to “plan” these sirens as attempts at threats. Given enough timely occurrences, one starts to wonder why these events occur, for example often after pushing a series of new commits to github.

This whole effort has been about arbitrary harassment and attacks and then claiming I’m not good enough for expecting some standards of living, common sense and reason, or civilized behavior.

This whole shitshow up to now, has already been about arbitrary bullshit “rules” and demands, that make no sense and only serve to frustrate efforts for most of which I already expected nothing in return and contributed hundreds if not thousands of hours then, and certainly thousands of hours at time of publication.

More recent derogatory mentions

More recently there were several mentions of “off-the-record” and “private messaging” that were clearly intended as mocking, with derogatory intention. One occurrence, some years ago at a meeting at the local municipality. Afterwards the screen powered on (from standby) so likely some shitty effort to emphasize some manipulation going on in the background, while at this supposedly private meeting.

Some prior occurrences making dismissive comments and referring to WhatsApp or Signal at abusive organization. Again, nobody ever asked questions. The prevalent pattern is that everyone went with their faulty preconceptions. I even took effort to, on several occasions, refocus on this topic that I valued the otr project which then gets dismissed or ignored. This is yet another one of those “Never argue with stupid people. They will drag you down to their level and beat you with experience.” kind of situations.

Note that none ever asked me about otr or “off-the-record”. I could have trivially explained the origins as a research project at a university. (I was not involved then.) Very similarly to how I have been under attack for almost last 8 years about twisted malicious narratives concerning my work on otr4j and many variations of claims where I did not do this work, had malicious intentions, etc.

What’s next on my part?

I have previously outlined how I think off-the-record (otr) fits in the larger ecosystem. There are people, likely with malicious agenda, who claim otr is obsolete. I do not agree with this, but as mentioned, I do think otr fills a very specific niche which is usually forgotten. OTR always was a protocol that layers on top of an existing messaging platforms. However, it is often compared to full-blown custom-designed secure-messaging solutions. Almost by definition, otr does not compare: unless the messaging infrastructure, that OTR would layer on top of, perfectly supplements the security properties that are outside the scope of otr, it wouldn’t. And if otr is necessary to improve security, it is unlikely that an underlying infrastructure provides exactly those missing properties.

On the other hand, otr is essentially a unique solution to securing messaging infrastructure that is otherwise not providing this level of confidentiality and privacy. Even if OTRv4 is not ideal in this day and age with PQC-hybrid cryptographic solutions starting to mature, it can still function as precursor, awaiting some necessary evolution to modernize.

Note that otr4j has not had a thorough audit. Due to me having to deal with rather persistent online (and to some extent off-line) harassment, I’ve had to scale down my effort. I have not received feedback w.r.t. bugs or critical errors.

“NoMsg” PoC

A couple of years ago, I did a proof-of-concept implementation to explore possibilities w.r.t. OTR layered on top of an existing messaging infrastructure, specifically Nostr. The leading question: what messaging infrastructure would be suitable to provide interesting characteristics on top of which OTR could provide end-to-end-encryption and some notion of a personal identity. Essentially, the identity could layer over different messaging infrastructure and accounts. Nostr would be interesting because any generated public key is an account without the need for registration. I elaborated in a mail that outlined the initial idea. I did not immediately continue this because other projects were underway and there is only so much one person can do. However, whether I continue this at a later moment or someone else will or someone else uses it for ideas, the idea is outlined and made public.

Changelog

For feedback, please respond to this post.

This article will receive updates, if necessary.


This post is part of the Coordinated harassment series series.
Other posts in this series: