Optional PSEC padding
This commit is contained in:
parent
d6a7d1466d
commit
e77887a51c
@ -19,6 +19,8 @@ object AIRADatabase {
|
||||
external fun clearTemporaryFiles(): Int
|
||||
external fun getIdentityPublicKey(): ByteArray
|
||||
external fun getIdentityFingerprint(): String
|
||||
external fun getUsePadding(): Boolean
|
||||
external fun setUsePadding(usePadding: Boolean): Boolean
|
||||
external fun changeName(newName: String): Boolean
|
||||
external fun changePassword(databaseFolder: String, oldPassword: ByteArray?, newPassword: ByteArray?): Boolean
|
||||
}
|
@ -126,8 +126,6 @@ class ChatActivity : ServiceBoundActivity() {
|
||||
if (this@ChatActivity.sessionId == sessionId) {
|
||||
runOnUiThread {
|
||||
findViewById<ConstraintLayout>(R.id.bottom_panel).visibility = View.GONE
|
||||
binding.buttonSend.setOnClickListener(null)
|
||||
binding.buttonAttach.setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import sushi.hardcore.aira.background_service.AIRAService
|
||||
import sushi.hardcore.aira.databinding.ActivityMainBinding
|
||||
import sushi.hardcore.aira.databinding.ActivitySettingsBinding
|
||||
import sushi.hardcore.aira.utils.StringUtils
|
||||
|
||||
@ -22,20 +22,24 @@ class SettingsActivity: AppCompatActivity() {
|
||||
private lateinit var airaService: AIRAService
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.preferences, rootKey)
|
||||
val identityName = findPreference<EditTextPreference>("identityName")
|
||||
val identityNamePreference = findPreference<EditTextPreference>("identityName")
|
||||
val paddingPreference = findPreference<SwitchPreferenceCompat>("psecPadding")
|
||||
identityNamePreference?.isPersistent = false
|
||||
paddingPreference?.isPersistent = false
|
||||
Intent(activity, AIRAService::class.java).also { serviceIntent ->
|
||||
activity?.bindService(serviceIntent, object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder) {
|
||||
val binder = service as AIRAService.AIRABinder
|
||||
airaService = binder.getService()
|
||||
identityName?.text = airaService.identityName
|
||||
identityNamePreference?.text = airaService.identityName
|
||||
paddingPreference?.isChecked = airaService.usePadding
|
||||
}
|
||||
override fun onServiceDisconnected(name: ComponentName?) {}
|
||||
}, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
identityName?.setOnPreferenceChangeListener { _, newValue ->
|
||||
identityNamePreference?.setOnPreferenceChangeListener { _, newValue ->
|
||||
if (airaService.changeName(newValue as String)) {
|
||||
identityName.text = newValue
|
||||
identityNamePreference.text = newValue
|
||||
}
|
||||
false
|
||||
}
|
||||
@ -107,6 +111,11 @@ class SettingsActivity: AppCompatActivity() {
|
||||
false
|
||||
}
|
||||
}
|
||||
paddingPreference?.setOnPreferenceChangeListener { _, checked ->
|
||||
airaService.usePadding = checked as Boolean
|
||||
AIRADatabase.setUsePadding(checked)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun changePassword(context: Context, isIdentityProtected: Boolean, oldPasswordEditText: EditText, newPassword: ByteArray?) {
|
||||
|
@ -48,6 +48,7 @@ class AIRAService : Service() {
|
||||
private val sendFileTransfers = mutableMapOf<Int, FilesSender>()
|
||||
val receiveFileTransfers = mutableMapOf<Int, FilesReceiver>()
|
||||
lateinit var contacts: HashMap<Int, Contact>
|
||||
var usePadding = true
|
||||
private lateinit var serviceHandler: Handler
|
||||
private lateinit var notificationManager: NotificationManagerCompat
|
||||
private lateinit var nsdManager: NsdManager
|
||||
@ -308,7 +309,7 @@ class AIRAService : Service() {
|
||||
sessionIdByKey[key] = sessionId
|
||||
uiCallbacks?.onNewSession(sessionId, session.ip)
|
||||
if (!isContact(sessionId)) {
|
||||
session.encryptAndSend(Protocol.askName())
|
||||
session.encryptAndSend(Protocol.askName(), usePadding)
|
||||
}
|
||||
} else {
|
||||
session.close()
|
||||
@ -412,7 +413,7 @@ class AIRAService : Service() {
|
||||
}
|
||||
|
||||
private fun sendAndSave(sessionId: Int, msg: ByteArray) {
|
||||
sessions[sessionId]?.encryptAndSend(msg)
|
||||
sessions[sessionId]?.encryptAndSend(msg, usePadding)
|
||||
if (msg[0] == Protocol.MESSAGE) {
|
||||
saveMsg(sessionId, msg)
|
||||
}
|
||||
@ -460,7 +461,7 @@ class AIRAService : Service() {
|
||||
identityName?.let {
|
||||
for (session in sessions.values) {
|
||||
try {
|
||||
session.encryptAndSend(Protocol.tellName(it))
|
||||
session.encryptAndSend(Protocol.tellName(it), usePadding)
|
||||
} catch (e: SocketException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
@ -498,6 +499,7 @@ class AIRAService : Service() {
|
||||
sessionCounter++
|
||||
}
|
||||
}
|
||||
usePadding = AIRADatabase.getUsePadding()
|
||||
}
|
||||
|
||||
private fun encryptNextChunk(session: Session, filesSender: FilesSender) {
|
||||
@ -510,7 +512,7 @@ class AIRAService : Service() {
|
||||
}
|
||||
filesSender.nextChunk = if (read > 0) {
|
||||
filesSender.lastChunkSizes.add(nextChunk.size)
|
||||
session.encrypt(nextChunk)
|
||||
session.encrypt(nextChunk, usePadding)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@ -540,7 +542,7 @@ class AIRAService : Service() {
|
||||
receiveFileTransfers.remove(sessionId)!!.fileTransferNotification.onAborted()
|
||||
}
|
||||
if (outgoing) {
|
||||
session.encryptAndSend(Protocol.abortFilesTransfer())
|
||||
session.encryptAndSend(Protocol.abortFilesTransfer(), usePadding)
|
||||
}
|
||||
}
|
||||
|
||||
@ -574,7 +576,7 @@ class AIRAService : Service() {
|
||||
when (buffer[0]) {
|
||||
Protocol.ASK_NAME -> {
|
||||
identityName?.let { name ->
|
||||
session.encryptAndSend(Protocol.tellName(name))
|
||||
session.encryptAndSend(Protocol.tellName(name), usePadding)
|
||||
}
|
||||
}
|
||||
Protocol.TELL_NAME -> {
|
||||
@ -603,7 +605,7 @@ class AIRAService : Service() {
|
||||
val chunk = buffer.sliceArray(1 until buffer.size)
|
||||
try {
|
||||
outputStream.write(chunk)
|
||||
session.encryptAndSend(Protocol.ackChunk())
|
||||
session.encryptAndSend(Protocol.ackChunk(), usePadding)
|
||||
file.transferred += chunk.size
|
||||
if (file.transferred >= file.fileSize) {
|
||||
outputStream.close()
|
||||
|
@ -166,8 +166,9 @@ class Session(private val socket: SocketChannel, val outgoing: Boolean): Selecta
|
||||
return false
|
||||
}
|
||||
|
||||
private fun randomPad(input: ByteArray): ByteArray {
|
||||
private fun pad(input: ByteArray, usePadding: Boolean): ByteArray {
|
||||
val encodedLen = ByteBuffer.allocate(MESSAGE_LEN_LEN).putInt(input.size).array()
|
||||
return if (usePadding) {
|
||||
val msgLen = input.size + MESSAGE_LEN_LEN
|
||||
var len = 1000
|
||||
while (len < msgLen) {
|
||||
@ -175,7 +176,10 @@ class Session(private val socket: SocketChannel, val outgoing: Boolean): Selecta
|
||||
}
|
||||
val padding = ByteArray(len-msgLen)
|
||||
prng.nextBytes(padding)
|
||||
return encodedLen + input + padding
|
||||
encodedLen + input + padding
|
||||
} else {
|
||||
encodedLen + input
|
||||
}
|
||||
}
|
||||
|
||||
private fun unpad(input: ByteArray): ByteArray {
|
||||
@ -190,8 +194,8 @@ class Session(private val socket: SocketChannel, val outgoing: Boolean): Selecta
|
||||
}
|
||||
}
|
||||
|
||||
fun encrypt(plainText: ByteArray): ByteArray {
|
||||
val padded = randomPad(plainText)
|
||||
fun encrypt(plainText: ByteArray, usePadding: Boolean): ByteArray {
|
||||
val padded = pad(plainText, usePadding)
|
||||
val rawMsgLen = ByteBuffer.allocate(MESSAGE_LEN_LEN).putInt(padded.size).array()
|
||||
val nonce = ivToNonce(applicationKeys.localIv, localCounter)
|
||||
localCounter++
|
||||
@ -200,8 +204,8 @@ class Session(private val socket: SocketChannel, val outgoing: Boolean): Selecta
|
||||
return rawMsgLen+localCipher.doFinal(padded)
|
||||
}
|
||||
|
||||
fun encryptAndSend(plainText: ByteArray) {
|
||||
writeAll(encrypt(plainText))
|
||||
fun encryptAndSend(plainText: ByteArray, usePadding: Boolean) {
|
||||
writeAll(encrypt(plainText, usePadding))
|
||||
}
|
||||
|
||||
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
|
||||
|
@ -20,6 +20,7 @@ impl<'a> DBKeys {
|
||||
pub const KEYPAIR: &'a str = "keypair";
|
||||
pub const SALT: &'a str = "salt";
|
||||
pub const MASTER_KEY: &'a str = "master_key";
|
||||
pub const USE_PADDING: &'a str = "use_padding";
|
||||
}
|
||||
|
||||
fn bool_to_byte(b: bool) -> u8 {
|
||||
@ -44,7 +45,8 @@ struct EncryptedIdentity {
|
||||
name: String,
|
||||
encrypted_keypair: Vec<u8>,
|
||||
salt: Vec<u8>,
|
||||
encrypted_master_key: Vec<u8>
|
||||
encrypted_master_key: Vec<u8>,
|
||||
encrypted_use_padding: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct Contact {
|
||||
@ -59,6 +61,7 @@ pub struct Identity {
|
||||
pub name: String,
|
||||
keypair: Keypair,
|
||||
pub master_key: [u8; crypto::MASTER_KEY_LEN],
|
||||
pub use_padding: bool,
|
||||
database_folder: String,
|
||||
}
|
||||
|
||||
@ -334,6 +337,13 @@ impl Identity {
|
||||
result
|
||||
}
|
||||
|
||||
pub fn set_use_padding(&mut self, use_padding: bool) -> Result<usize, rusqlite::Error> {
|
||||
self.use_padding = use_padding;
|
||||
let db = KeyValueTable::new(&self.get_database_path(), MAIN_TABLE)?;
|
||||
let encrypted_use_padding = crypto::encrypt_data(&[bool_to_byte(use_padding)], &self.master_key).unwrap();
|
||||
db.update(DBKeys::USE_PADDING, &encrypted_use_padding)
|
||||
}
|
||||
|
||||
pub fn zeroize(&mut self){
|
||||
self.master_key.zeroize();
|
||||
self.keypair.secret.zeroize();
|
||||
@ -345,11 +355,13 @@ impl Identity {
|
||||
let encrypted_keypair = db.get(DBKeys::KEYPAIR)?;
|
||||
let salt = db.get(DBKeys::SALT)?;
|
||||
let encrypted_master_key = db.get(DBKeys::MASTER_KEY)?;
|
||||
let encrypted_use_padding = db.get(DBKeys::USE_PADDING)?;
|
||||
Ok(EncryptedIdentity {
|
||||
name: std::str::from_utf8(&name).unwrap().to_owned(),
|
||||
encrypted_keypair,
|
||||
salt,
|
||||
encrypted_master_key,
|
||||
encrypted_use_padding,
|
||||
})
|
||||
}
|
||||
|
||||
@ -375,10 +387,13 @@ impl Identity {
|
||||
};
|
||||
match crypto::decrypt_data(&encrypted_identity.encrypted_keypair, &master_key) {
|
||||
Ok(keypair) => {
|
||||
match crypto::decrypt_data(&encrypted_identity.encrypted_use_padding, &master_key) {
|
||||
Ok(use_padding) => {
|
||||
Ok(Identity{
|
||||
name: encrypted_identity.name,
|
||||
keypair: Keypair::from_bytes(&keypair[..]).unwrap(),
|
||||
master_key: master_key,
|
||||
master_key,
|
||||
use_padding: byte_to_bool(use_padding[0]).unwrap(),
|
||||
database_folder: database_folder,
|
||||
})
|
||||
}
|
||||
@ -388,6 +403,12 @@ impl Identity {
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
print_error!(e);
|
||||
Err(String::from(DATABASE_CORRUPED_ERROR))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e.to_string())
|
||||
}
|
||||
}
|
||||
@ -418,11 +439,13 @@ impl Identity {
|
||||
salt
|
||||
};
|
||||
db.set(DBKeys::SALT, &salt)?;
|
||||
|
||||
let encrypted_use_padding = crypto::encrypt_data(&[bool_to_byte(true)], &master_key).unwrap();
|
||||
db.set(DBKeys::USE_PADDING, &encrypted_use_padding)?;
|
||||
Ok(Identity {
|
||||
name: name.to_owned(),
|
||||
keypair,
|
||||
master_key,
|
||||
use_padding: true,
|
||||
database_folder
|
||||
})
|
||||
}
|
||||
|
@ -28,4 +28,7 @@ impl<'a> KeyValueTable<'a> {
|
||||
pub fn update(&self, key: &str, value: &[u8]) -> Result<usize, Error> {
|
||||
self.db.execute(&format!("UPDATE {} SET value=? WHERE key=\"{}\"", self.table_name, key), params![value])
|
||||
}
|
||||
/*pub fn upsert(&self, key: &str, value: &[u8]) -> Result<usize, Error> {
|
||||
self.db.execute(&format!("INSERT INTO {} (key, value) VALUES(?1, ?2) ON CONFLICT(key) DO UPDATE SET value=?3", self.table_name), params![key, value, value])
|
||||
}*/
|
||||
}
|
@ -344,6 +344,24 @@ pub fn Java_sushi_hardcore_aira_AIRADatabase_changeName(env: JNIEnv, _: JClass,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub fn Java_sushi_hardcore_aira_AIRADatabase_getUsePadding(_: JNIEnv, _: JClass) -> jboolean {
|
||||
bool_to_jboolean(loaded_identity.lock().unwrap().as_mut().unwrap().use_padding)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub fn Java_sushi_hardcore_aira_AIRADatabase_setUsePadding(_: JNIEnv, _: JClass, use_padding: jboolean) -> jboolean {
|
||||
match loaded_identity.lock().unwrap().as_mut().unwrap().set_use_padding(jboolean_to_bool(use_padding)) {
|
||||
Ok(_) => 1,
|
||||
Err(e) => {
|
||||
log_error(e);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub fn Java_sushi_hardcore_aira_AIRADatabase_getIdentityFingerprint(env: JNIEnv, _: JClass) -> jobject {
|
||||
|
5
app/src/main/res/drawable/ic_blur.xml
Normal file
5
app/src/main/res/drawable/ic_blur.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M6,13c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM6,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM6,9c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM3,9.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM6,5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM21,10.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM14,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zM14,3.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM3,13.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM10,20.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM10,3.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM10,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zM10,12.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM18,13c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM18,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM18,9c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM18,5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM21,13.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM14,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM14,20.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM10,8.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM10,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM14,12.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM14,8.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5z"/>
|
||||
</vector>
|
@ -81,4 +81,7 @@
|
||||
<string name="about">About</string>
|
||||
<string name="version">AIRA version</string>
|
||||
<string name="refresh_name">Refresh name</string>
|
||||
<string name="security">Security</string>
|
||||
<string name="use_psec_padding">Use PSEC padding</string>
|
||||
<string name="psec_padding_summary">PSEC padding obfuscates the length of your messages but uses more network bandwidth.</string>
|
||||
</resources>
|
||||
|
@ -28,6 +28,16 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/security">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="psecPadding"
|
||||
android:title="@string/use_psec_padding"
|
||||
android:summary="@string/psec_padding_summary"
|
||||
android:icon="@drawable/ic_blur"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/about">
|
||||
|
||||
<Preference
|
||||
|
Loading…
Reference in New Issue
Block a user