mirror of
https://github.com/oxen-io/session-android.git
synced 2023-12-14 02:53:01 +01:00
acb40c6133
Added a new logger that persists logs for a longer duration to the user's cache directory. Logs are encrypted. The new logs are sent in addition to the user's logcat output.
139 lines
4.5 KiB
Java
139 lines
4.5 KiB
Java
package org.thoughtcrime.securesms.logging;
|
|
|
|
import android.support.annotation.NonNull;
|
|
|
|
import org.thoughtcrime.securesms.util.Conversions;
|
|
import org.thoughtcrime.securesms.util.Util;
|
|
|
|
import java.io.BufferedInputStream;
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.EOFException;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.PushbackInputStream;
|
|
import java.io.RandomAccessFile;
|
|
import java.security.InvalidAlgorithmParameterException;
|
|
import java.security.InvalidKeyException;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.SecureRandom;
|
|
|
|
import javax.crypto.BadPaddingException;
|
|
import javax.crypto.Cipher;
|
|
import javax.crypto.IllegalBlockSizeException;
|
|
import javax.crypto.NoSuchPaddingException;
|
|
import javax.crypto.ShortBufferException;
|
|
import javax.crypto.spec.IvParameterSpec;
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
|
|
class LogFile {
|
|
|
|
public static class Writer {
|
|
|
|
private final byte[] ivBuffer = new byte[16];
|
|
private final GrowingBuffer ciphertextBuffer = new GrowingBuffer();
|
|
|
|
private final byte[] secret;
|
|
private final File file;
|
|
private final Cipher cipher;
|
|
private final BufferedOutputStream outputStream;
|
|
|
|
Writer(@NonNull byte[] secret, @NonNull File file) throws IOException {
|
|
this.secret = secret;
|
|
this.file = file;
|
|
this.outputStream = new BufferedOutputStream(new FileOutputStream(file, true));
|
|
|
|
try {
|
|
this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
|
throw new AssertionError(e);
|
|
}
|
|
}
|
|
|
|
void writeEntry(@NonNull String entry) throws IOException {
|
|
new SecureRandom().nextBytes(ivBuffer);
|
|
|
|
byte[] plaintext = entry.getBytes();
|
|
try {
|
|
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(secret, "AES"), new IvParameterSpec(ivBuffer));
|
|
|
|
int cipherLength = cipher.getOutputSize(plaintext.length);
|
|
byte[] ciphertext = ciphertextBuffer.get(cipherLength);
|
|
cipherLength = cipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
|
|
|
|
outputStream.write(ivBuffer);
|
|
outputStream.write(Conversions.intToByteArray(cipherLength));
|
|
outputStream.write(ciphertext, 0, cipherLength);
|
|
|
|
outputStream.flush();
|
|
} catch (ShortBufferException | InvalidAlgorithmParameterException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
|
|
throw new AssertionError(e);
|
|
}
|
|
}
|
|
|
|
long getLogSize() {
|
|
return file.length();
|
|
}
|
|
|
|
void close() {
|
|
Util.close(outputStream);
|
|
}
|
|
}
|
|
|
|
static class Reader {
|
|
|
|
private final byte[] ivBuffer = new byte[16];
|
|
private final byte[] intBuffer = new byte[4];
|
|
private final GrowingBuffer ciphertextBuffer = new GrowingBuffer();
|
|
|
|
private final byte[] secret;
|
|
private final Cipher cipher;
|
|
private final BufferedInputStream inputStream;
|
|
|
|
Reader(@NonNull byte[] secret, @NonNull File file) throws IOException {
|
|
this.secret = secret;
|
|
this.inputStream = new BufferedInputStream(new FileInputStream(file));
|
|
|
|
try {
|
|
this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
|
throw new AssertionError(e);
|
|
}
|
|
}
|
|
|
|
String readAll() throws IOException {
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
String entry;
|
|
while ((entry = readEntry()) != null) {
|
|
builder.append(entry).append('\n');
|
|
}
|
|
|
|
return builder.toString();
|
|
}
|
|
|
|
private String readEntry() throws IOException {
|
|
try {
|
|
Util.readFully(inputStream, ivBuffer);
|
|
Util.readFully(inputStream, intBuffer);
|
|
|
|
int length = Conversions.byteArrayToInt(intBuffer);
|
|
byte[] ciphertext = ciphertextBuffer.get(length);
|
|
|
|
Util.readFully(inputStream, ciphertext, length);
|
|
|
|
try {
|
|
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secret, "AES"), new IvParameterSpec(ivBuffer));
|
|
byte[] plaintext = cipher.doFinal(ciphertext, 0, length);
|
|
|
|
return new String(plaintext);
|
|
} catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
|
|
throw new AssertionError(e);
|
|
}
|
|
} catch (EOFException e) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|