How does it work?
How do I use it?
Security
How does it work?
Internet Task Sync allows two or more computers to collaborate on a task, as long as both have a connection to the internet and have been given permission to acccess the task.
Once you start or join an sync, any changes made to the task on your computer are immediately transmitted to a central database. Every few seconds, your computer will query that central server, and look for new updates to the task you're working on. If new updates are found, they'll be downlodaed an integrated into what you're doing.
In practical terms, it means a computer back at your SAR hall could be used to start generating assignments while the crew is driving out to the field. Once you arrive, you can connect to the sync and see those assignments. Meanwhile, as you log comms in the field, that planning person back at your hall will see the updates immediately.
This is a marked difference from the network solution currently in place - which works only over a local area network without some serious nerdage.
How do I use it?
Obviously, this will require a newer version of the software. Minimum 6.14.25.
Start an internet sync
The first step in the process is to begin an internet sync. Use any computer that has the current task information on it.
- Open the software and load the current task if it isn't already
- Click on Network > Internet Sharing / Sync
- You will now see a new screen with three options: Do not sync, Join an ongoing sync, and Start a new sync. Choose "Start a new Sync".
- The screen will display a randomly generated encryption key for you. Copy this key, using either of the buttons provided.
- Press "OK" and wait a few moments while the task to date is uploaded to the internet server.
- Provide the encryption key you copied earlier to anyone else you want to join the task.
Join an internet sync
Once a task has been uploaded to the server, anyone else in possession of the encryption key is able to join the task. You can also use this option if you have previously initiated a sync from this computer, but closed the program and are now re-opening it.
- Open the software if it isn't already. If you have a local copy of the task, feel free to load that as well, it will speed up processing.
- Click on Network > Internet Sharing / Sync
- You will now see a new screen with three options: Do not sync, Join an ongoing sync, and Start a new sync. Choose "Join an Ongoing Sync".
- Enter the encryption key you've been provided by whoever set up the sync. If you are loading a file that was already synced, the information should be filled in for you.
- Press "OK" and wait a few moments while the task to date is synchronized the internet server.
How is the information stored?
- 256-bit end to end encryption
- Server is blind to the data
- Server is in Canada
The details
All task information is stored in a central database. This database is located on a server in Canada. Access to the server is restricted to the developer of this software.
The data is encrypted with 256-bit encryption on your device, then transmitted to the database encrypted where it is stored. Even the developers can't access the contents of your task data. Here is a preview of how the data is saved within the database:
- UpdateID this is a randomly generated unique identifier for the individual update/change made to the task. It is assigned on your computer, and serves only to identify that update among all other updates.
- TaskID this is a randomly generated unique identifier for the task itself, and is common for all updates related to that specific task. It is not related to the Task Number, Task Name, or any other identifiers. You may recognize it as the first half of your encryption key.
- CommandName this indicates the type of procedure being performed, either UPSERT to add or make changes, DELETE to remove something, or INITIAL for the initial task information.
- DataEnc this is your encrypted data, containing the item being updated. Again, it is not readable on the server, or anywhere else, without your private encryption key.
- MachineID when the software is first installed on a computer, a unique identifier is randonly generated for that machine, internal to the software. This is used to identify the source of an update. There is no reference on the server of who this machine ID corresponds to, that information is only available on the computer itself.
- ObjectType this identifies the type of item being added/updated/deleted
Summary: What would be learned from the server?
Without the encryption key, someone could tell that an assignment was updated at 06:22 on the 7th of October on the same task, and by the same computer that updated the comms plan item at 04:33. There is no way to determine (a) which task number or (b) which user/group or (c) the contents of the update.
Encryption Code
Here is the code used to encrypt the data
public static class StringCipher
{
// This constant is used to determine the keysize of the encryption algorithm in bits.
// We divide this by 8 within the code below to get the equivalent number of bytes.
private const int Keysize = 256;
// This constant determines the number of iterations for the password bytes generation function.
private const int DerivationIterations = 1000;
public static string Encrypt(string plainText, string passPhrase)
{
// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
// so that the same Salt and IV values can be used when decrypting.
var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
public static string Decrypt(string cipherText, string passPhrase)
{
// Get the complete stream of bytes that represent:
// [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
using (var streamReader = new StreamReader(cryptoStream, Encoding.UTF8))
{
return streamReader.ReadToEnd();
}
}
}
}
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
}