<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Stefan Sechelmann on Medium]]></title>
        <description><![CDATA[Stories by Stefan Sechelmann on Medium]]></description>
        <link>https://medium.com/@offbeata?source=rss-ea6716657cf4------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*b7DM_sKKM1KV21lSoDNcDw.jpeg</url>
            <title>Stories by Stefan Sechelmann on Medium</title>
            <link>https://medium.com/@offbeata?source=rss-ea6716657cf4------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 17 May 2026 19:21:30 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@offbeata/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[A collaborative whiteboard — integrating Excalidraw into Sopher]]></title>
            <link>https://blog.sopher.io/a-collaborative-whiteboard-integrating-excalidraw-into-sopher-fbddbb0d1a19?source=rss-ea6716657cf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/fbddbb0d1a19</guid>
            <category><![CDATA[collaboration-tools]]></category>
            <category><![CDATA[whiteboards]]></category>
            <category><![CDATA[e2ee]]></category>
            <category><![CDATA[sopher]]></category>
            <dc:creator><![CDATA[Stefan Sechelmann]]></dc:creator>
            <pubDate>Thu, 01 Apr 2021 13:47:54 GMT</pubDate>
            <atom:updated>2021-04-01T13:47:54.999Z</atom:updated>
            <content:encoded><![CDATA[<h3>A collaborative whiteboard — integrating Excalidraw into Sopher</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PUGaRCsqwcE9gi7jhq9m6w.jpeg" /></figure><p>Sopher has been part of the <a href="https://updatedeutschland.org">UpdateDeutschland</a> 48h Sprint Hackathon initiative which took place from 19th to 21st of March 2021. It was clear quickly that there was one particular challenge posed by the City of Freiburg that had the potential for a match.</p><blockquote>Wie können wir kollaboratives Arbeiten in Zeiten von Home Office und Home Schooling für städtische Mitarbeiter:innen, Schüler:innen und andere Akteuren:innen ermöglichen, ohne gegen den Datenschutz zu verstoßen?</blockquote><blockquote>How can we enable collaborative work in times of home office and home schooling for urban employees, students and other stakeholders without violating data protection?</blockquote><p>During the Hackathon we met in video conferences organized by the UpdateDeutschland team and learned that the City of Freiburg had the concrete goal of implementing a collaborative whiteboard for their employees and possibly for other employees of the public sector.</p><p>As a result we produced a short video that showcases our activity during the three days:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FAyZ_PXNOvWg%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DAyZ_PXNOvWg&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FAyZ_PXNOvWg%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/689120e3e7d897ad4bbb7b71abda61d0/href">https://medium.com/media/689120e3e7d897ad4bbb7b71abda61d0/href</a></iframe><p>At the same time during the night from Saturday to Sunday we integrated an existing open source SaSS whiteboard into Sopher.</p><h4>Integrating Excalidraw into Sopher.</h4><p>The open source collaborative whiteboard <a href="https://excalidraw.com">Excalidraw</a> supports end-to-end encryption (E2EE), see <a href="https://blog.excalidraw.com/end-to-end-encryption/">E2EE in Excalidraw</a>. Combining the communication features of Sopher with the collaborative features of Excalidraw seemed a perfect match for the challenge of the Hackathon. We integrated the SaSS deployment of Excalidraw into Sopher using simple key derivation in each channel. Here we derive a symmetric key from the channel&#39;s identity key like so</p><pre>k_group = hkdf(tbp(identity), &#39;whiteboard&#39;)<br>k_contact = hkdf(ecdh(id_1, id_2), &#39;whiteboard&#39;)</pre><p>In a group the key depends on the group identity key via its thumbprint and in the case of a one-to-one whiteboard we derive a ECDH shared secret first and then derive the whiteboard secret from that.</p><h4>Further steps</h4><p>In order to be useful we need to take into account that employees use Single-Sign-On mechanisms and already have accounts at their respective organization. To not overuse the resources of the Excalidraw team we plan to host our own instance of Excalidraw infrastructure and link to that in Sopher.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fbddbb0d1a19" width="1" height="1" alt=""><hr><p><a href="https://blog.sopher.io/a-collaborative-whiteboard-integrating-excalidraw-into-sopher-fbddbb0d1a19">A collaborative whiteboard — integrating Excalidraw into Sopher</a> was originally published in <a href="https://blog.sopher.io">Becoming sopher</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Implementing the Signal E2EE protocol for sopher.io]]></title>
            <link>https://blog.sopher.io/implementing-the-signal-e2ee-protocol-for-sopher-io-b33470bbc2e4?source=rss-ea6716657cf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/b33470bbc2e4</guid>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[signal]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[encryption]]></category>
            <dc:creator><![CDATA[Stefan Sechelmann]]></dc:creator>
            <pubDate>Wed, 29 Aug 2018 11:49:08 GMT</pubDate>
            <atom:updated>2018-08-29T11:49:08.240Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*davaj1epfvtNfZ9nvrp8Tw.png" /><figcaption>A conformally parameterized discrete version of a Catenoid minimal surface. Just like the elliptic curves over finite fields used in elliptic curve cryptography it can be interpreted a discrete version of a corresponding Riemann surface. See <a href="http://annals.math.princeton.edu/2006/164-1/p03">Bobenko et. al.</a> for more information about the mathematical background.</figcaption></figure><p>Sopher.io provides end-to-end encrypted communication for teams. We offer end-to-end encryption for each and every communication that happens between your and your team member’s devices. In this post we want to answer the question how we got there and what are the special properties of our implementation compared to the Signal protocol as specified by <a href="https://whispersystems.org/docs/">Open Whisper Systems</a> (OWS).</p><p>When we started our implementation in December 2016 OWS had just released descriptions of their algorithms for the handshake protocol <a href="https://whispersystems.org/docs/specifications/x3dh/">X3DH</a> and for the <a href="https://whispersystems.org/docs/specifications/doubleratchet/">Double-Ratchet-Algorithm</a> to the public domain. That was great since we would not have to dig around in their source code. An earlier attempt to implement the Signal protocol by the encrypted messenger Wire <a href="https://medium.com/@wireapp/axolotl-and-proteus-788519b186a7">conjured up legal</a> problems due to similarities with the source code of OWS&#39;s implementation. The description of multi-device support <a href="https://whispersystems.org/docs/specifications/sesame/">Sesame</a> was added a bit later and so we came up with our own which is pretty similar. But first things first, what are the properties we expect from a message protocol that suits our needs?</p><h3>Properties of an ideal protocol</h3><p>One key aspect of the technical side of Sopher is the fact that we do not operate an application server. Everything happens in the client and if we really need to store information online we upload it to standard cloud storage such as Dropbox, Google Drive, S3 or the like. This poses a few challenges to the implementation of the messaging protocol. In particular we faced the following tasks:</p><ul><li>Only use cryptographic primitives provided by the WebCrypto API</li><li>Symmetric asynchronous handshake</li><li>Message acknowledgement</li><li>Multi-device support</li></ul><h3>A browser implementation of the Signal protocol using Typescript</h3><p>For the browser implementation of the Sopher client we use Typescript. This enables us to leverage the power of asynchronous programming using the keywords async and await. Not to mention the beauty of working with an editor (mostly Visual Studio Code) that really understands the structure and dependencies of the project. If you are not familiar with Typescript check out their <a href="https://www.typescriptlang.org/">Documentation</a>, its worth it.</p><p>In the next sections we will walk you through the differences to vanilla Signal as described in the OWS <a href="https://whispersystems.org/docs/">specs</a>.</p><h4>WebCrypto primitives</h4><p>At the current stage of development we support browsers that implement the WebCrypto API sufficiently well (Chrome, Firefox). This has several advantages security-wise and implementation-wise. It is more secure since we can leverage the power of non-exportable keys. A <em>CryptoKey </em>can be flagged non-extractable when generated by the API:</p><pre>let key = await crypto.subtle.generateKey(<br>  {<br>    name: &#39;ECDH&#39;,<br>    namedCurve: &#39;P-384&#39;<br>  },<br>  false, // make the key non-extractable<br>  [&#39;deriveKey&#39;, &#39;deriveBits&#39;]<br>);</pre><p>This makes it extra hard for an attacker to steal the keys. Nevertheless those keys can be stored in IndexedDB for subsequent usage.</p><p>Obviously sticking to WebCrypto greatly simplifies the implementation as we do not have to create fallback strategies for browsers without WebCrypto. This would add a great amount of overhead since one would have to use WebWorkers for encryption to avoid locking the browser context.</p><p>The X3DH protocol recommends using the X25519 or X448 elliptic curves for the use in Diffi-Hellmann key agreements. However those curves are not part of WebCrypto implementations neither in Chrome nor Firefox, let alone Safari. Safari however added ECDH support to its Technology Preview in May 2017. We therefore decided to use the standard NIST curve P-384 to perform Diffie-Hellmann key agreements and DSA signatures.</p><p>All other cryptographic primitives recommended by OWS are available in WebCrypto implementations. We use AES-CBC for content encryption and HMAC with SHA-512 for payload signatures as described in the <a href="https://tools.ietf.org/html/rfc7518#section-5.2.5">JWA specification</a>. One could consider using direct encryption as described in the <a href="https://tools.ietf.org/html/rfc7518#section-4.5">spec</a> to save a little space here. On the other hand we need per-recipient encryption for group messaging anyway. One way to implement this is via the <a href="https://tools.ietf.org/html/rfc7516#section-3.2">multiple encryption</a> of a content encryption key.</p><h4>Symmetric asynchronous handshake</h4><p>We basically follow the specification of X3DH when establishing a session between devices. The one major difference is in the way we determine in advance who is Alice and who is Bob to avoid the hassle of maintaining more than one session as described in the <a href="https://whispersystems.org/docs/specifications/sesame/">Sesame algorithm</a>.</p><p>In <em>Sopher</em> a shared file in a cloud storage acts the role of the server. A client publishes his public (pre-)keys for other clients to pick up and start a session. In particular we store the following information in a JWS structure signed with the device <em>key:</em></p><pre>{<br>  DID: &quot;c6e4b0b5b8ffcfeb4e0...&quot;,<br>  name: &quot;Chrome&quot;,<br>  timestamp: 1535538368530,<br>  key: {crv: &quot;P-384&quot;, kty: &quot;EC&quot;, ...},<br>  pipeKey: {crv: &quot;P-384&quot;, kty: &quot;EC&quot;, ...},<br>  signedPrekey: {crv: &quot;P-384&quot;, kty: &quot;EC&quot;, ...},<br>  onetimePrekeys: [<br>    {crv: &quot;P-384&quot;, kty: &quot;EC&quot;, ...},<br>    {crv: &quot;P-384&quot;, kty: &quot;EC&quot;, ...},<br>    {crv: &quot;P-384&quot;, kty: &quot;EC&quot;, ...},<br>    ...<br>  ],<br>  invitations: [<br>    {<br>      salt: &quot;jJTjeLvmeeYyT8...&quot;,<br>      url: &quot;eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0...&quot;<br>    },<br>    ...<br>  ],<br>  ,<br>  ...<br>}&quot;</pre><p>We establish a transport encryption by using Diffie-Hellmann key agreement with the corresponding <em>pipeKeys</em> and invitation <em>salts</em> of the devices. The resulting symmetric key is used to encrypt both the url of the message pipe (the file that contains all encrypted messages) given in the invitation attribute and the contents of this file. If Alice wants to start sending messages to Bob she uses the corresponding device keys as IKa and IKb, the <em>signedPrekey </em>of Bob as SPKb and one of the public keys contained in the <em>onetimePrekeys </em>array as OPKb. In addition to that she generates her own ephemeral key pair EKa. Refer to the <a href="https://whispersystems.org/docs/specifications/x3dh/#the-x3dh-protocol">X3DH protocol</a> for the details on how to arrive at a shared secret using all of these keys.</p><p>In the initial messages that are included in the first and second chain of the Double Ratchet Algorithm we include the following information:</p><pre>export interface InitialMessageData {<br>  IK: JsonWebKey;<br>  EK: JsonWebKey;<br>  SPK_THUMB: string;<br>  OPK_THUMB: string;<br>  AD: string;<br>}</pre><p>We identify the selected <em>signedPrekey</em> and <em>onetimePrekey</em> by their respective thumbprints as defined by <a href="https://tools.ietf.org/html/rfc7638">JWK thumbprint</a>. AD takes the value literally as defined in the <a href="https://whispersystems.org/docs/specifications/x3dh/#sending-the-initial-message">X3DH spec</a>. The public keys are serialized using the JWK specification.</p><p>The X3DH protocol is asymmetric by nature: a device starts sending messages by downloading a set of (pre-)keys from the server and calculating a shared secret from it. In the next step the recipient receives one of the first messages that includes all necessary information to complete the session handshake.</p><p>In our setting the situation is symmetric: Any device can start sending messages as soon as it learns about the public device profile of a contact’s device. Once the other device receives these messages it should either pick up the remote session or adapt its own session it created before. To achieve this we fix the roles of Alice and Bob as described in the OWS spec in advance by using a natural order of public EC identity keys by comparing x coordinates. The device with the smaller key is Alice, the one with the greater acts the role of Bob.</p><p>When a device starts sending messages the situation is perfectly symmetric. Each device downloads a set of keys, performs X3DH as specified and starts sending. Only until a device receives the first message, then the state either has to be switched to a new shared secret (in the case of Bob) by performing the corresponding X3DH step. Or, in the case of Alice, just the receiving chain has to be initialized with the shared secret defined by the other device&#39;s initial messages:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f37b0cf80a9a54f4ef3813788fd02a51/href">https://medium.com/media/f37b0cf80a9a54f4ef3813788fd02a51/href</a></iframe><p>By doing so we avoid keeping track of more than one session per pair of devices.</p><h4>Message acknowledgement</h4><p>We need acknowledgement for messages in order to decide which messages we have to store online (We write a file for all pending messages to cloud storage if the device is offline) for later retrieval at the device of the recipient.</p><p>In order to accomplish message acknowledgement we include in each message header the missing messages that are stored in <em>MKSKIPPED </em>as <a href="https://whispersystems.org/docs/specifications/doubleratchet/#state-variables-1">described in the spec</a><em>.</em> The corresponding header attribute <em>mm</em> is an array that contains chain identifiers (the thumbprint of the corresponding public chain key) followed by the message indices within that chain. Such a JWE header may look this this:</p><pre>{<br>  alg: &quot;A256KW&quot;,<br>  enc: &quot;A256CBC-HS512&quot;,<br>  aad: {<br>    v: 1,<br>    dh: {<br>      crv: &quot;P-384&quot;,<br>      kty: &quot;EC&quot;,<br>      x: &quot;mmEa7QxSNTCJMD80hynUq6GJV1Aq8l...&quot;,<br>      y: &quot;wjgt1XqQeGx3XxLJTHjk3zLAYH3Ncr...&quot;<br>    },<br>    n: 0,<br>    pn: 1,<br>    mm: [<br>      &quot;gcNwdlmBw4EZIPbrc4V-uN6t7cesDwSeja-SgdeZxKs&quot;,<br>      0,<br>      1,<br>      &quot;rKlsCL-DMQOUY_jmlXlOqeOmVZbilOO9Z_3CxiwMrOI&quot;,<br>      0<br>    ],<br>    dm: []<br>  }<br>}</pre><p>Note that we include all additional header information that is not part of the <a href="https://tools.ietf.org/html/rfc7516#section-4.1">JOSE specs</a> in the <em>aad (Additional Authenticated Data) </em>attribute. This includes the protocol version <em>v, </em>the JWK chain key <em>dh, </em>the index of the message in the current chain <em>n</em>, and the length of the previous chain <em>pn. </em>If this would be one of the initial messages then we would include an extra <em>init </em>field in the <em>aad</em> structure<em>.</em></p><p>This acknowledgement procedure can however only capture what the other side knows it is missing. Thus we must retain all messages of the current and the previous chain in addition to the messages mentioned in the header since the other side might be missing messages at the end of the previous chain or in the current chain.</p><p>We implement a method called <em>retainStoredMessages</em> that deletes all stored messages but those mentioned in the <em>mm </em>header. We invoke it before <em>DHRatchet </em>so it effectively leaves the stored messages of the previous and current chain alone, see line 7 of this Gist:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d3e667456e0463f6caf37fdec61988f1/href">https://medium.com/media/d3e667456e0463f6caf37fdec61988f1/href</a></iframe><h4>Message discarding</h4><p>If a device A is inactive for more than a month device B would stop encrypting and sending messages for it. Additionally, device B would discard all messages that have been stored so far to avoid bloating the contents of the corresponding pipes connecting A and B. When device B becomes active again we signal the indices of discarded messages in the <em>dm</em> header field of each of the following messages from B to A until those indices are removed from the <em>mm</em> header of A as a reaction to the <em>dm</em> header contents. See also line 9 of Algorithm 2.</p><h4>Multi-device support</h4><p>There is not much to say about multi-device support. As we have only one Double Ratchet session per pair of devices there is not much maintenance going on when sending messages between contacts. We have two kinds of messages at the moment: system messages directly address separate devices, and chat messages that address all devices of a contact or group of contacts. If a message is sent to all devices of a contact it gets encrypted separately for each of the sessions.</p><h3>Future work</h3><h4>Direct JWE encryption to save space</h4><p>Currently we use <em>A256KW </em>encryption algorithm to encrypt all messages feeding the Signal message key as input for the <em>AES-KW </em>operation. We believe that this extra step doesn&#39;t add security to the encryption over using the message key directly to encrypt generate the <em>MAC</em> of the <em>JWK</em>.</p><h3>Acknowledgements</h3><p>We like to thank the team of Open Whisper Systems.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b33470bbc2e4" width="1" height="1" alt=""><hr><p><a href="https://blog.sopher.io/implementing-the-signal-e2ee-protocol-for-sopher-io-b33470bbc2e4">Implementing the Signal E2EE protocol for sopher.io</a> was originally published in <a href="https://blog.sopher.io">Becoming sopher</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Implementing OpenID Connect for sopher.io]]></title>
            <link>https://blog.sopher.io/implementing-openid-connect-for-sopher-io-38d2417a6945?source=rss-ea6716657cf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/38d2417a6945</guid>
            <category><![CDATA[oauth]]></category>
            <category><![CDATA[sopher]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[openid-connect]]></category>
            <category><![CDATA[sso]]></category>
            <dc:creator><![CDATA[Stefan Sechelmann]]></dc:creator>
            <pubDate>Sun, 17 Jul 2016 17:46:42 GMT</pubDate>
            <atom:updated>2016-07-18T10:08:55.597Z</atom:updated>
            <cc:license>https://creativecommons.org/licenses/by-sa/4.0/</cc:license>
            <content:encoded><![CDATA[<p><a href="http://openid.net/connect/">OpenID Connect</a> is an identity layer on top of <a href="http://oauth.net/2/">OAuth 2.0</a>. Its main purpose is to ease Single Sign-On (SSO) implementations across different platforms and devices. It is currently widely deployed as “Login with Google / Facebook / Twitter” buttons on all sorts of web sites.</p><p>At sopher.io, we want sopher identities to be as useful as possible. So we decided to implement the OpenID Connect authorization flow, ultimately offering the “Login with sopher.io” button to third-party web sites.</p><p>OpenID Connect defines three types of flows that can be used by a Relying Party (RP) to perform user authentication at an OpenID Connect Provider (OP):</p><h4>Code Flow</h4><p>The <em>Code Flow</em> authenticates the user at his identity provider, e.g., Google, and redirects back with an <em>authorization code</em>. In a second step this code is used to obtain identity and access tokens. The second step takes place via server-to-server communication with the token end-point at the identity provider.</p><h4>Implicit Flow</h4><p>The <em>Implicit Flow</em> works without server-to-server communication. The identity provider directly redirects with identity and access tokens included in the fragment of the redirect URI. No backend is needed on the client side. At sopher.io we use this flow to connect to cloud storage providers.</p><h4>Hybrid Flow</h4><p>The third flow is a mixture of the Code Flow and the Implicit Flow called the <em>Hybrid Flow.</em></p><p>At sopher.io profiles are hosted by the users themselves. We do not operate any servers to host user data and cannot provide a token end point on our side. This renders the code flow infeasible for us. Additionally we cannot verify users’ identities since we simply do not store anything about them.</p><p>The solution to these problems is actually part of the OpenID Connect specification: The <a href="http://openid.net/specs/openid-connect-core-1_0.html#SelfIssued">Self-Issued Identity Provider</a>. In combination with the Implicit Flow we can offer a fully compliant OpenID Connect implementation for sopher.io accounts.</p><h4>Self-Issued Identity Provider</h4><p>A Self-Issued Identity Provider is an entity that cryptographically asserts its identity. It does so by signing the fingerprint of its public key using a signed OpenID Connect id token including the fingerprint and the public key. At first login the RP stores the fingerprint of the public key as the id of the entity. At subsequent logins the RP validates the identity token using the public key included in the id token. How does sopher.io come into play?</p><p>The <em>registration</em> attribute of the OpenID Connect Self-Issued Identity Provider request is meant to provide information about the RP such as name, logo, or policy links. We use this field to transmit the client id token that a sopher app obtains upon registration. A sopher.io account can verify the identity of the RP as well as inferring name, logo, and origin information.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=38d2417a6945" width="1" height="1" alt=""><hr><p><a href="https://blog.sopher.io/implementing-openid-connect-for-sopher-io-38d2417a6945">Implementing OpenID Connect for sopher.io</a> was originally published in <a href="https://blog.sopher.io">Becoming sopher</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>