Decoding and encoding class hierarchies in Swift

A while ago I was implementing (for fun) a chat client to work with a chat server used in teaching in a GUI programming course. The client is implemented with Swift and SwiftUI.

In the GUI programming course, students get the server and a sample console client, both implemented in Java. The goal for the students is to both design and implement a GUI chat application. Learning goals are related to GUI design, usability design and implementation of a GUI in a selected language and GUI framework. The server is accessed over TCP and data format is JSON.

One tricky thing was to figure out how the Swift Codable could handle a class structure with a base class Message, having different types of messages as subclasses. For example, ChatMessage (having the sender, message, timestamp, etc.), a ListChannelsMessage (containing the available channels on the server side to join), et cetera.

The Java console client class diagram, containing the same message classes as the Swift app.

The Swift Codable does not directly and simply bend to this kind of a class hierarchy. I searched the net and found a good basis here, but it didn’t fully show how to do both decoding and encoding with a class structure like I had.

So obviously, for fun, I banged my head against the wall until I got it working. I was supposed to write a blog post about that, but haven’t had the time.

But today someone in Mastodon asked the exact question; how to do something like this. A good motivation for me to actually do something about that! I didn’t want to publish the whole Swift Chat client app, since students are still able to start working on this project, and someone might want to select Swift/SwiftUI as the GUI for their app.

So instead, I extracted the code to show how to use Codable and JSONSerialization together to handle a class hierarchy like this to encode and decode objects to JSON and back. The code is now available as a simple example project in GitHub.

I shared the repository to the Mastodon discussion. What was exciting for me is that one of the core persons in developing the Swift language itself favourited my reply! ?

Sadness

Update March 26th: they seem to be up and running again!

I’ve been using weather.willab.fi weather service located here in Oulu, at the VTT research center, for years in many of my courses.

It has been a good demo for teaching many basic computing and networking topics, for example:

  • how to use curl to execute network requests and look at the different HTTP headers the server provides in the response;
  • how HTTP works on top of lower level protocols (TCP), using Wireshark for traffic analysis;
  • localization of simple Android and iOS apps;
  • what is JSON and XML and how they look like with simple weather data;
  • what is a server and what is a client on the Internet;
  • and maybe something else I do not remember now.

But anyways, the service has been down for some time, returning weather data from around December 2022. Now it is even worse; it just displays the nginx server welcome page, and HTTP 404 when you try to get the current weather contents using http://weather.willab.fi/weather.json.

Hopefully they will kick the service up and running soon! Otherwise I need to rewrite many exercises and demo apps to use some other simple service.

Authentication to a server in an Android app

I have been playing around with an Android app as a side hobby for a while now. It is a simple game app, which can load new game boards from a server using HTTP GET. So the data does not contain any personal or business critical data.

But since I like to (at least try to) implement things well and learn more in my toy projects, I decided to protect the game board directory in the server side using .htaccess and to move to using HTTPS. This way the authentication HTTP headers containing the user name and password in the request are encrypted, protecting login info while it is transmitted over the network.

HttpsURLConnection urlConnection = null;
String[] games = null;
try {
  URL serverUrl = new URL(serverAddress + scriptName);
  urlConnection = (HttpsURLConnection) serverUrl.openConnection();
  String authString = getUserNameAndPassword();
  String authEncodedString = Base64.getEncoder().encodeToString(authString.getBytes());
  urlConnection.setRequestProperty("authorization", "Basic " + authEncodedString);
  response = urlConnection.getResponseCode();
  if (response == HttpURLConnection.HTTP_OK) {
// ....

The method getUserNameAndPassword() returns a string username:hashed-password, used in the authentication of the client.

Since using HTTPS, that information is encrypted while transmitted. But what about in the actual application binary? If you store the username and password as plain strings in the code, it would be relatively easy by investigating the binary to dig out this data and use it for unauthorised access to the server. In my app, that wouldn’t be such an issue, since the server side code only supports HTTPS GET to list the available game board files and there is nothing else you could do there.

Anyways, I decided to find a way to try to make it a bit more difficult for anyone to dig out authentication strings from the client binary. I searched for possible solutions, of which this article is a good summary.

In my app, I decided not to use any proxies or cloud services, since all that trouble is not worth it, considering the nature of data my app uses. There is no perfect way to do this since any method has their weaknesses. I picked one simple way to do it to learn how it would actually work in my case.

What I decided to do was this:

  1. First and most obviously, I used long and cryptic username and password values when creating the username and password for .htaccess.
  2. I made sure that the .password file is not in the server directories the HTTP server could read, thus inaccessible from the network using HTTP. This is also something very obvious one should always do.
  3. I took the generated and hashed password, and further obfuscated it and the username using (for example) xor and then rot13. To do this, I implemented a small tool app which takes in the username:hashed-password String and outputs the obfuscated byte array from this step. Xor nor rot13 are not very efficient encryption methods, but still manage to make finding out the passwords a bit more difficult. When doing the xor, I use a byte array, not a String. You could add even more steps here, using other simple additional obfuscation methods, as long as you make sure that you can reverse the process and arrive — using the exact opposite steps — to the original username and password.
  4. I placed the obfuscated username and password bytes in the client app source code not in a String but in a byte array in the client code. So anyone looking for username or password Strings from the code is not going to find it. Obviously the bytes used in xor step need to be in the client code too.
  5. I further divided the byte arrays from step 4 into 2-3 different byte arrays in different places in the code, so that they are not in a continuous area of memory. At this point you could also e.g. reverse bytes in some of the byte arrays to further confuse the possible investigator of the app binary.

So, when the client code is then executed, the divided byte arrays are (some possibly reversed; step #5) combined, then a String is created from them at run time. After this, the obfuscation steps from #3 above are executed in reverse order (e.g. first rot13 then xor), producing the original username and the hashed password. This is then put in the authorization header of the HTTPS request.

Though not perfect, this is in my applications case a good enough security solution. Obviously you should not publicly share which substitution ciphers or other methods (xor, rot13, reversing, …) you use in obfuscation.

Below is the output from the tool to create obfuscated authorisation bytes.

Original string: demousername:hashed-demo-password-from-htpasswd-tool
Rotted: qrzbhfreanzr:unfurq-qrzb-cnffjbeq-sebz-ugcnffjq-gbby
encoded: EBVOVgALBQATDwkTXUFaDhgFFF8QARsFGVcGCxEPEAQCTBRRVhJAAgIRDxUHDUUZDw8VHA==
Take these bytes to a byte array in client app:
{69,66,86,79,86,103,65,76,66,81,65,84,68,119,107,84,88,85,70,97,68,104,103,70,70,70,56,81,65,82,115,70,71,86,99,71,67,120,69,80,69,65,81,67,84,66,82,82,86,104,74,65,65,103,73,82,68,120,85,72,68,85,85,90,68,119,56,86,72,65,61,61}
With this key:
{97,103,52,52,104,109,119,101,114,97,115}