Signal Messanger Architecture

Introduction

Signal is an end-to-end encrypted communications application for Android and iOS similar to WhatsApp but open source. It uses the TCP/IP (Internet) to send one-to-one and group messages, which can include text, files, voice notes, images and videos, and make one-to-one voice and video calls. For user identification standard cellular mobile numbers are used.

 

The main system component is the Signal-Server which is maintained by Open Whisper Systems. The source code is available on GitHub (https://github.com/signalapp/Signal-Server) only for audition purposes without providing additional documentation about on premises installations. In the following sections I will provide some information about the system components and their interconnections.

System components

Signal-Server – is the central element of the Signal Messenger ecosystem, being constantly developed by Open Whisper Systems. It manages the registration of new accounts, stores and distributes the public keys of the clients, relays and temporarily stores  the end-to end encrypted messages, enforces rate limits in order to avoid denial of service attacks and provides pre-signed URLs to the mobile client for uploading files. Signal-Server is entirely written in Java; source code is available at the following URL: https://github.com/signalapp/Signal-Server .  An additional PostgreSQL database and Redis NoSQL object store are needed in order to run the Signal-Server. The configuration is stored in a yml file (at a first glance it seems quite complex) and is passed as a command line argument to the java binaries. Before running Signal-Server the Push-Server must be up and running.

Push-Server – acts as a gateway between Signal-Server and Google Cloud Messaging / Apple Push Notification Service. It is entirely written in Java and the configuration is sored in a yml file which must be passed as an command line argument to the java binaries. Source code is available at https://github.com/signalapp/PushServer . Without the push service the App has to be permanently connected to the Signal-Server in order to receive the messages, thus reducing the battery life of the mobile device or the user hast manually check for new messages by opening the App.

STUN/TURN Server –  is a VoIP media traffic NAT traversal server and gateway. It is used for establishing voice and video calls. For this component you can use a standard On-premises or Cloud TURN implementation, such as Linux Coturn (https://github.com/coturn/coturn) which is available as a standard package on the majority of Linux distributions. If you do not need voice/video calls this component can be omitted.

Signal App – is the only visible part to the end user. It  is developed and  maintained by Open Whisper Systems. At the time of writing there is a Android and an iOS version. A desktop (Linux, Windows, Mac ) version is also available but it lacks registration and calling support, being more like an extension of the mobile App for desktop usage.

Twilio – is a cloud communications platform. In this scenario it allows the Signal-Server to make phone calls and send  text messages using its web service APIs. It is used only during the registration phase to confirm the users mobile phone number. Twilio’s services are accessed over HTTP and are billed based on usage.

Amazon S3 (Simple Storage Services) – is a web service offered by Amazon Web Services (AWS). It is used to exchange encrypted files between the Signal clients. In contrast to the text messages which are relayed through the Signal-Server the files are temporarily stored using an external service. The protocol works as follows: the Signal client request an URL from Signal-Server, the server computes a pre-signed URL having a limited validity and sends it to the client, the client uploads the encrypted file to Amazon S3 and then sends the link to its peer. Amazon S3 services are accessed over HTTP and are billed based on usage. If you do not need file transfers this component can be omitted.

Google Cloud Messaging (GCM) – is a mobile notification service developed by Google that enables Signal-Server to send notification data to Signal Apps that target the Android Operating System. GCM is a free service ant it is accessed over HTTP.

Apple Push Notification Service (APNs) – is platform notification service created by Apple Inc. that enables the Signal-Server to send notification data to Signal Apps installed on Apple devices. APNs doesn’t cost anything.

In addition to the end-to-end encryption, the transport between the Signal App and the Signal-Server is secured using TLS and certificate pinning. The Signal-Server implements a API protocol for communicating with the Signal Apps which is available at https://github.com/signalapp/Signal-Server/wiki/API-Protocol (document might be outdated).

Nginx lua plugin for uploads authentication

Sometimes you want to allow your HTTP clients to upload files to a protected location without having to pass data through a secure intermediary proxy node that protect the access credentials. The clients must first request a signed URL from a third-party and then can upload the file using a HTTP POST or PUT, thus reducing latency.

For this example the signed URL has the following structure:

https://<fqdn>/<uri>?e=<timetamp>&s=<signature>

where:

  • <signature>=hmac(<password>, <uri>|<http method>|<expire timestamp>)
  • <timestamp> is the unix time designating the expiration of the URL
  • <password> is a shared secret between the HTTP server and the third party involved.

The authentication scheme is similar to the Amazon AWS signature.

The code is listed bellow (access.lua):

local expire = ngx.var.arg_e
local sign = ngx.var.arg_s
local uri = ngx.var.uri
local method = ngx.var.request_method

local path = ""
local secret = "password"

local hmac = require "resty.hmac"

 

local hmac_sha256 = hmac:new(secret, hmac.ALGOS.SHA256)
   if not hmac_sha256 then
      ngx.log(ngx.STDERR, "failed to create the hmac_sha256 object")
   return
end

 

--ngx.log(ngx.STDERR, "uri= " .. uri .. " method= " .. method .. " expire= " .. expire .. " sign= " .. sign)

if expire ~= nul and sign ~= null and uri:find("upload") and method == "PUT" then

   local digest = hmac_sha256:final(uri..method..expire, true)

   ngx.log(ngx.STDERR, "time="..os.time() .. " inputData= " .. uri.."|"..method.."|"..expire .. " sha256: " .. digest)

   if digest == sign and tonumber(expire) > os.time() then
      ngx.log(ngx.STDERR, "Upload ok")
      return ngx.OK
   end

   elseif method == "GET" then
      return ngx.OK
end

return ngx.exit(403)

And the corresponding nginx server configurations:

location /upload/ {
  lua_code_cache on;
  access_by_lua_file 'sites-available/access.lua';
  root      /var/dav;

  client_body_temp_path /var/dav/temp;
  dav_methods     PUT DELETE;
  create_full_put_path  on;
  dav_access    user:rw group:rw all:rw;
  autoindex     off;
}

The nginx server must be compiled with the openresty/lua-nginx-module.

Compile latest nginx with latest OpenSSL on Raspbian strech

Nginx is a lightweight web server, which can also be used as a reverse proxy, load balancer or HTTP cache. With additional applications it can serve dynamic web pages like those written in PHP.
This tutorial is based on nginx 1.11.10 and openssl 1.1.0e.

First, create a directory:

cd ~
mkdir nginx
cd nginx

Download nginx latest version using wget, then extract the archive:

 wget https://nginx.org/download/nginx-1.11.10.tar.gz
 tar xvf  nginx-1.11.10.tar.gz

Download openssl latest version and extract the archive

 wget https://www.openssl.org/source/openssl-1.1.0e.tar.gz
 tar xvf  openssl-1.1.0e.tar.gz

Switch to the newly created nginx source directory

cd nginx-1.11.10

Start the configuration:

./configure \
--http-client-body-temp-path=/var/run/nginx/body \
--http-fastcgi-temp-path=/var/run/nginx/fastcgi \
--http-proxy-temp-path=/var/run/nginx/proxy \
--http-scgi-temp-path=/var/run/nginx/scgi \
--http-uwsgi-temp-path=/var/run/nginx/uwsgi \
--user=www-data \
--group=www-data \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--with-pcre-jit \
--with-http_v2_module \
--with-debug \
--with-http_stub_status_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_dav_module \
--with-http_gzip_static_module \
--with-http_sub_module \
--with-http_xslt_module \
--with-http_ssl_module \
--with-ld-opt=-lrt \
--with-openssl=../openssl-1.1.0e \
--with-http_v2_module \
--with-http_ssl_module

It takes about 1 minute. You can save the configure command parameters for later usage.

If everything looks good and no errors are reported, you can now start the compilation with the command (assuming your CPU has 4 cores)

 make -j4

Depending on the system load, compiling takes about 10 minutes. If no errors are reported, test the resulting binary:

objs/nginx -V

The following output is expected:

nginx version: nginx/1.11.10
built by gcc 6.3.0 20170124 (Raspbian 6.3.0-5+rpi1) 
built with OpenSSL 1.1.0e  16 Feb 2017
TLS SNI support enabled
configure arguments: --http-client-body-temp-path=/var/run/nginx/body --http-fastcgi-temp-path=/var/run/nginx/fastcgi --http-proxy-temp-path=/var/run/nginx/proxy --http-scgi-temp-path=/var/run/nginx/scgi --http-uwsgi-temp-path=/var/run/nginx/uwsgi --user=www-data --group=www-data --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --with-pcre-jit --with-http_v2_module --with-debug --with-http_stub_status_module --with-http_realip_module --with-http_addition_module --with-http_dav_module --with-http_gzip_static_module --with-http_sub_module --with-http_xslt_module --with-http_ssl_module --with-ld-opt=-lrt --with-openssl=../openssl-1.1.0e --with-http_v2_module --with-http_ssl_module

Finally, install

make install

Running multiple instances of haproxy on CentOS 7.2

Sometimes for security reasons and traffic separation it is necessary to run multiple instances of haproxy on the same system. The only restriction is to use different sockets (IP:port pairs) for each instance.

Steps:

1. Create a new configuration directory under /etc:

mkdir -p /etc/haproxy2
touch /etc/haproxy2/haproxy2.conf

2. Create a symlink for the haproxy binary

ln -s /usr/sbin/haproxy /usr/sbin/haproxy2

Now haproxy can be invoke using haproxy2

3. Create a copy of the initialization script

cp /etc/init.d/haproxy /etc/init.d/haproxy2

Edit the commented lines (4-7) of the haproxy2 script by replacing haproxy with haproxy2 as follows:

#!/bin/sh
#
# chkconfig: - 85 15
# description: HA-Proxy is a TCP/HTTP reverse proxy which is particularly suited \
#              for high availability environments.
# processname: haproxy2
# config: /etc/haproxy2/haproxy2.cfg
# pidfile: /var/run/haproxy2.pid

4. Don’t forget to fill in the required configuration directives into /etc/haproxy2.conf as usually
Adjust the paths accordingly. E.g.

 chroot /var/lib/haproxy2
 stats socket /run/haproxy2/admin.sock mode 660 level admin

5. Test the service using

service haproxy2 start

6. Enable the service to start at boot time

systemctl enable haproxy2

NAT64 on Raspberry Pi

NAT64 [RFC6052, RFC6146] is an IPv6 transition mechanism that enable communication between IPv6 only and IPv4 only hosts using network address translation (NAT). The NAT64 box is a translator between IPv4 and IPv6 protocols; it requires at least one IPv4 public address and an IPv6 subnetwork comprising a 32-bit address space.

Integrating PHP CAPTCHA into Self Service Password

Self Service Password is an open-source free PHP application that allows users to change their password in an LDAP directory. To determine whether the user is human and to prevent brute forcing Self Service Password provides easy integration with Google reCaptcha. Such a solution may not be appropriate for some installations because it requires an Internet connection between the server hosting Self Service Password and Google and also between the client browser and Google. Moreover the validation process is transfered outside the organization boundaries making possible for Google to track every site access.

Secureimage is an open-source free PHP CAPTCHA script for generating complex images and CAPTCHA codes to protect forms from abuse and brute forcing. It can be added with minimal effort into existing forms on the website to determine whether the user is human.  Secureimage does everything from generating the CAPTCHA images to validating the typed code.

Assuming a working Sefl Service Password installation and a webroot /var/www/self-service-password
cd /var/www/self-service-password

wget https://www.phpcaptcha.org/latest.tar.gz

tar -zxvf latest.tar.gz

rm latest.tar.gz

ls

conf  index.php  lang  lib  pages  securimage  style

Having the secureimage folder in place the next step is to add the code to display the captca image inside the form. Edit pages/change.php and add the following at line 249 just below the Google reCaptca code and above the submit input

<tr><td colspan=2>
<img id="captcha" src="securimage/securimage_show.php" alt="CAPTCHA Image" />
</td></tr>

Next, add the following HTML code to create a text input box:

<tr><td colspan=2>
<input type="text" name="captcha_code" size="10" maxlength="6" /> <a href="#" onclick="document.getElementById('captcha').src = 'securimage/securimage_show.php?' + Math.random(); return false">[ Different Image ]</a>
</td></tr>

Up to now you are able to display the Captcha image, to refresh it and to enter the code but no validation will be done. Next we will move onto modifying the PHP code that validates the CAPTCHA code typed in by the user.

First add

require_once("securimage/securimage.php");

to index.php line 31, just above the language section.

Then add

#Check php secureimage
$securimage = new Securimage();

if ( $result === "" ){
if ($securimage->check($_POST['captcha_code']) == false) {
  // the code was incorrect
         $result = "badcaptcha";
         error_log("Bad reCAPTCHA attempt with user $login");
         }
}

to pages/change.php inside the #Check reCAPTCA section at the end of the existing if block.
The call to the check method verifies the generated CAPTCHA code against the code entered by the user. If the code was incorrect, an error message is printed using the Self Service Password functions.

Similarly, it is possible to modify the other existing Self Service Password pages to benefit from the self hosted CAPTCHA verification module.

More info about customizing Secureimage can be found at https://www.phpcaptcha.org/

Based on:

Secureimage v3.6.4

Self Service Password v0.9