cph-cachet / carp.sensing-flutter

CARP Mobile Sensing for Flutter, including mobile sensing framework, data backend support, and the CARP mobile sensing app.
MIT License
80 stars 28 forks source link

Zip encryption not implemented #141

Open koenniem opened 3 years ago

koenniem commented 3 years ago

As the title says, encryption for zip files was never implemented. I'm not sure if this is intentional or not.

The way I see it, there is no method to set a simple password for the zip (though this is a weaker method in general). My suggestion is to encrypt the JSON file in its entirety and then zip it. However, I'm not sure how this interacts with sink.

bardram commented 3 years ago

I need good input on how to do this. What would be the best approach in Flutter to implement a PKI setup?

koenniem commented 3 years ago

From my SO question:

With an asymmetric encryption, such as RSA, only data of limited length can be encrypted. The maximum length corresponds to the key length, i.e. for a 2048 bits key a maximum of 256 bytes. For longer data, symmetric encryption (e.g. AES) must be used, or a combination (hybrid encryption) if the symmetric key is to be encrypted with an asymmetric method, e.g. to send it to other parties. During encryption, binary data is processed and binary data is returned. The zipping can therefore in principle take place before or after the encryption depending on the requirement.

There indeed seems to be a limit in the size of the data when using asymmetric encryption I was not aware of (see here and here). I'm not sure if this implies the ZIP itself (or rather the file) cannot be encrypted (unless it's symmetrically done). If so, you can either encrypt every line individually (although I'm not sure how that impacts performance) or by using a hybrid approach, i.e. generating a symmetric key to encrypt the file and then encrypt that key with a public key.

The hybrid approach seems a bit nicer although it requires an extra key to be sent. On the other hand, encrypting line by line is easy to implement in the current set-up. What are your thought on this?

koenniem commented 3 years ago

I think a hybrid approach could be achieved by using the encrypt package. In flush() in file_data_manager.dart:

/// Flushes data to the file, compress, encrypt, and close it.
void flush(File flushFile, IOSink flushSink) {

 (...)

  // once finished closing the file, then zip and encrypt it
  // Actually, it should be the other way around then
  flushSink.close().then((value) {
    // encrypt the zip file
    if (_fileDataEndPoint.encrypt) {
      // Create a new symmetric key (32 bytes)
      final key = Key.fromLength(32);
      final iv = IV.fromLength(16);

      // TODO: Read in file, encrypt, and save.
      final AESEncrypter = Encrypter(AES(key));
      final encrypted = AESEncrypter(file, iv: iv);

      // Convert public key to PEM and then to RSAPublicKey
      // Assumes [_fileDataEndPoint.publicKey] is a base64 RSA key without headers
      String pk = _fileDataEndPoint.publicKey;
      String pem = '-----BEGIN RSA PUBLIC KEY-----\n$pk'
         '\n-----END RSA PUBLIC KEY-----';
      final pubkey = RSAKeyParser().parse(pem);

      // Encrypt symmetric key with public key
      final RSAEncrypter = Encrypter(RSA(publicKey: pubkey, privateKey: null)); // Not sure if it can be null
      final encryptedKey = RSAEncrypter.encrypt(key); // Save it to somewhere

      addEvent(FileDataManagerEvent(
            FileDataManagerEventTypes.FILE_ENCRYPTED, _finalFilePath));
    }
    if (_fileDataEndPoint.zip) {
      (...)
    }
  });
}

Unfortunately, I don't know enough about dart to implement the reading and writing of the file. Nevertheless, I think this shows the general idea. As long as there aren't too many zips (i.e. not every second or so), I think it should be okay in terms of computational time and battery.

bardram commented 3 years ago

I think this looks like a sound approach and I will look into how this can be implemented.