diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/utils/EncryptionExtTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/utils/EncryptionExtTest.kt new file mode 100644 index 000000000..258022b24 --- /dev/null +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/utils/EncryptionExtTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ +package org.isoron.uhabits.utils + +import org.isoron.uhabits.* +import org.junit.* +import java.io.* + +class EncryptionExtTest : BaseAndroidTest() { + @Test + fun test_encrypt_decrypt_string() { + val original = "Hello world!" + val key = generateEncryptionKey() + val encrypted = original.encrypt(key) + val decrypted = encrypted.decrypt(key) + assertEquals("Hello world!", decrypted) + } + + @Test + fun test_encrypt_decrypt_file() { + val original = File.createTempFile("file", ".txt") + val writer = PrintWriter(original.outputStream()) + writer.println("hello world") + writer.println("encryption test") + writer.close() + + val key = generateEncryptionKey() + val encrypted = original.encryptToString(key) + + val decrypted = File.createTempFile("file", ".txt") + encrypted.decryptToFile(key, decrypted) + assertEquals("hello world\nencryption test\n", decrypted.readText()) + } +} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/EncryptionExt.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/EncryptionExt.kt new file mode 100644 index 000000000..8beb90c5b --- /dev/null +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/EncryptionExt.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.utils + +import android.util.* +import java.io.* +import java.nio.* +import java.nio.charset.StandardCharsets.* +import javax.crypto.* +import javax.crypto.spec.* + +fun ByteArray.encrypt(key: String): ByteArray { + val keySpec = SecretKeySpec(Base64.decode(key, Base64.DEFAULT), "AES") + val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING") + cipher.init(Cipher.ENCRYPT_MODE, keySpec) + val encrypted = cipher.doFinal(this) + return ByteBuffer + .allocate(16 + encrypted.size) + .put(cipher.iv) + .put(encrypted) + .array() +} + +fun ByteArray.decrypt(key: String): ByteArray { + val buffer = ByteBuffer.wrap(this) + val iv = ByteArray(16) + buffer.get(iv) + val encrypted = ByteArray(buffer.remaining()) + buffer.get(encrypted) + val keySpec = SecretKeySpec(Base64.decode(key, Base64.DEFAULT), "AES") + val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING") + cipher.init(Cipher.DECRYPT_MODE, keySpec, IvParameterSpec(iv)) + return cipher.doFinal(encrypted) +} + +fun String.encrypt(key: String): String { + return Base64.encodeToString(this.toByteArray().encrypt(key), Base64.DEFAULT) +} + +fun String.decrypt(key: String): String { + return String(Base64.decode(this, Base64.DEFAULT).decrypt(key), UTF_8) +} + +fun String.decryptToFile(key: String, output: File) +{ + val outputStream = FileOutputStream(output) + output.writeBytes(Base64.decode(this, Base64.DEFAULT).decrypt(key)) + outputStream.close() +} + +fun File.encryptToString(key: String): String { + val bytes = ByteArray(this.length().toInt()) + val inputStream = FileInputStream(this) + inputStream.read(bytes) + inputStream.close() + return Base64.encodeToString(bytes.encrypt(key), Base64.DEFAULT) +} + +fun generateEncryptionKey(): String { + return try { + val keygen = KeyGenerator.getInstance("AES") + keygen.init(256) + val key = keygen.generateKey() + Base64.encodeToString(key.encoded, Base64.DEFAULT) + } catch (e: Exception) { + throw RuntimeException(e) + } +} \ No newline at end of file