Corona-Warn-App API

curl https://svc90.main.px.t-online.de/version/v1/diagnosis-keys/country/DE/date # Liste publizierter Bundles

[„2020-06-23″,“2020-06-24″,“2020-06-25″,“2020-06-26″,“2020-06-27″,“2020-06-28“]

curl https://svc90.main.px.t-online.de/version/v1/diagnosis-keys/country/DE/date/2020-06-23/hour

[8,13,17]

curl https://svc90.main.px.t-online.de/version/v1/diagnosis-keys/country/DE/date/2020-06-23 --output diagnosis_keys_2020-06-23.zip

Eine Zip-Datei mit export.bin und export.sig

The Corona Warn App (CWA)

There are two repositories on GitHub one for Android and the other for iOS licensed under the Apache License, Version 2.0:

FAQ on what you need to know about the app provided by the German government can be found on golem.de

The current developers are from SAP and T-Systems.

Code Reviews

iOS

At the moment I found a code review for IOS on twitter.

Architecture

Architecture_Overview_v1

Android

For the time being I downloaded the Android sources. Compiled them and started the emulator for „Pixel 2 API R“. I am mainly a Java-Developer so I do not know if I will be able to properly be able to review the Kotlin source-code. If not there will be no more updates on this article. Otherwise I will do my best hopefully to keep this article up to date.

Kritik

Primäre Kritik die ich habe ist an der Technologie und (noch) nicht am Code. Bluetooth wurde *niemals* *nie* *nicht* entwickelt für die Messung von Entfernungen. Ein Gerät welches 2 m entfernt ist kann 20 m weit entfernt sein und umgekehrt. Dieser Umstand lässt sich in mehreren Security-Sites einfach per Suchmaschine finden. Ebenfalls kann man die Spezifikation in der Wikipedia ansehen.

Kosten

Anfangskosten beziehungsweise Entwicklungskosten für die Bereitstellung inklusive Zusatzkosten sind 20 Millionen €. Dazu kommen vorerst noch einmal 45 M € für den Betrieb. Da sind wir schon bei 65 M €. Warum ist dies Kritik? Weil genau zu dem Zeitpunkt an dem begonnen wird die Regeln zu lockern die App da ist. Fantastisch. „Congratulations and celebrations“! Natürlich kann man nicht schneller Entwickeln und die App wurde in verdammt kurzer Zeit entwickelt. Das ist alles klar! Wie immer sind die Entscheidungsträger schuld, die immer alles vorvorgestern haben wollen. Und das obwohl sie gestern noch nicht wussten, dass sie es haben wollen werden.

Screenshots and description of the app.

Weblinks

Podcasts

ISIN Check

International Securities Identification Number (ISIN) see Double-Add-Double Implentation

/**
* Calculates the Double-Add-Double from String src (length 11 chars) 
* alternatively checks validity (length 12 chars, result 0 if valid else undefinied.
*/
public static int calculateCheckISIN(String src)
{
  int  result = 0;
  int  whatLength = (src.length() == 12)? 1 : 2;
  int  current;
  for(int i = src.length() - 1; i >= 0; i--)
  {
    current = src.charAt(i);
    if(current > '9')
    {
      // It is a char
      current -= ('A' - 10);
      result += (3-whatLength)*(current/10) + whatLength*current + (whatLength-1)*(current%10)/5;
    }
    else
    {
      // It is a numeric character
      current -= '0';
      result += whatLength*current + (whatLength-1)*(current/5);
      whatLength = 3 - whatLength;
    }
  }
  result %= 10;
  result = (10 - result%10) % 10;
  return result;
}

HTTP header parsing

If you already established a URLConnection (HttpURLConnection or HttpsURLConnection) you can get the content or headers and parse them yourself.

But why not use the method getHeaderFields provided by URLConnection?  All you need is to understand Java Collections because it returns a Map. Have a look at the Trail: Collections (by Josh Bloch).

You might test argue why not use getDate provided by URLConnection. Good point! I have not looked into that issue enough to tell you for sure it returns the desired long in any case. So for now, in my idiosyncrasy, I try to handle the header Date field on my own.

Enjoy the code using generics:

Map<String,List<String>> map = urlConnection.getHeaderFields();
Set<Entry<String,List<String>>> set = map.entrySet();
Iterator<Entry<String,List<String>>> iterator = set.iterator();
boolean cookie;
boolean headerDate;
while (iterator.hasNext())
{
  Entry<String,List<String>> entry = iterator.next();
  String key = entry.getKey();
  if(key!=null)
  {
    cookie = key.equals("Set-Cookie");
    headerDate = key.equals("Date");
    if(cookie || headerDate)
    {
      List<String> list = entry.getValue();
      Iterator<String> listIterator = list.iterator();
      while (listIterator.hasNext())
      {
        String listElement = listIterator.next();
        if(headerDate)
        {
        ...
        }
        else if(cookie)
        {
        ...
        }
      }
    }
  }
}

Using for loops

Things have been improved a bit. No iterator needed anymore.

String url = "https://example.com";
Map<String,List<String>> headerFields;
try {
  URLConnection connection = new URL(url).openConnection();
  headerFields = connection.getHeaderFields();
  Set<String> keyStrings = headerFields.keySet();
  for (String key : keyStrings) {
    System.out.print(key + ": ");
    for (String value : headerFields.get(key)) {
      System.out.print(value + " ");
    }
    System.out.print("\n");
  }
}
catch (IOException ioe) {
  System.err.println(ioe);
}

Java 8


Now let us look at how you can do it today. Yeah! We may use Lambda funtions. First you get a Stream of the Lists of Strings. Next step is to use the Collectors to create the actual String from the List joined by spaces.

headerFields = connection.getHeaderFields();
headerFields.forEach((k, v) -> {
  if(k != null && k.equals("Set-Cookie")) {
  System.out.println(k + ": " + v.stream()
    .collect(Collectors.joining(" ")));
  }
});

Typo3 routing

You are creating a site with typo3? You have no intention to have multiple domains even in future? You have no intention of using extensions? Just use the core extension SimulateStaticDocuments and enable it in your typoscript template with:

config.simulateStaticDocuments = 1

It works fine.
But seriously you do not want to do that!

RealUrl

The standard configuration is just as simple as I describe it on my site.
Now do not be silly to expect it to take over the routes of your self made extension. Extensions like tt_news have documentation how to configure RealUrl properly.
If an extension has no preparation for the routes with RealUrl it is just a shit extension do not blame Typo3 for this.

Typo3 URI problem

What is the synopsis?
I used mc_googlesitemap (version 0.4.2; which seemed buggy). Google Webmaster Tools accepted the URI in the sitemap. I quick fixed the problem in the extension mc_googlesitemap (URI still persists in google site search).

You could ask for my fix but I would be rather you use another extension! Anyhow I understand if you have a problem. I am sure you know how to contact me if you are not a bot.

What was wrong?

http://www.DOMAIN.TLD/http://www.DOMAIN.TLD/ does not cause an error.
http://www.DOMAIN.TLD/http://www.DOMAIN.TLD/http://www.DOMAIN.TLD/ does.

it’s even this which triggers it: http://www.DOMAIN.TLD/X://X

And this shows a blank page:

http://www.DOMAIN.TLD/X:/X
http://www.DOMAIN.TLD/X:X
http://www.DOMAIN.TLD/X:

What is my typo 3 version?
It is 4.5.2 and my realurl setup is at pastebin.com

What is my temporary workaround?
For now I put this in my .htaccess and I know it does not cover all cases mentioned above:

RewriteCond %{REQUEST_URI} ^(/http://.*|/https://.*)
RewriteRule ^(.*)$ http://%{HTTP_HOST}/page-not-found.html [R=301,L,E=nolog:1]