feat: Move the crypto APIs to pigeon
This commit is contained in:
parent
271428219a
commit
61de3cd565
@ -81,6 +81,18 @@ public class Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum CipherAlgorithm {
|
||||||
|
AES128GCM_NO_PADDING(0),
|
||||||
|
AES256GCM_NO_PADDING(1),
|
||||||
|
AES256CBC_PKCS7(2);
|
||||||
|
|
||||||
|
final int index;
|
||||||
|
|
||||||
|
private CipherAlgorithm(final int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Generated class from Pigeon that represents data sent in messages. */
|
/** Generated class from Pigeon that represents data sent in messages. */
|
||||||
public static final class NotificationMessageContent {
|
public static final class NotificationMessageContent {
|
||||||
/** The textual body of the message. */
|
/** The textual body of the message. */
|
||||||
@ -904,6 +916,86 @@ public class Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Generated class from Pigeon that represents data sent in messages. */
|
||||||
|
public static final class CryptographyResult {
|
||||||
|
private @NonNull byte[] plaintextHash;
|
||||||
|
|
||||||
|
public @NonNull byte[] getPlaintextHash() {
|
||||||
|
return plaintextHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlaintextHash(@NonNull byte[] setterArg) {
|
||||||
|
if (setterArg == null) {
|
||||||
|
throw new IllegalStateException("Nonnull field \"plaintextHash\" is null.");
|
||||||
|
}
|
||||||
|
this.plaintextHash = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NonNull byte[] ciphertextHash;
|
||||||
|
|
||||||
|
public @NonNull byte[] getCiphertextHash() {
|
||||||
|
return ciphertextHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCiphertextHash(@NonNull byte[] setterArg) {
|
||||||
|
if (setterArg == null) {
|
||||||
|
throw new IllegalStateException("Nonnull field \"ciphertextHash\" is null.");
|
||||||
|
}
|
||||||
|
this.ciphertextHash = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor is non-public to enforce null safety; use Builder. */
|
||||||
|
CryptographyResult() {}
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private @Nullable byte[] plaintextHash;
|
||||||
|
|
||||||
|
public @NonNull Builder setPlaintextHash(@NonNull byte[] setterArg) {
|
||||||
|
this.plaintextHash = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable byte[] ciphertextHash;
|
||||||
|
|
||||||
|
public @NonNull Builder setCiphertextHash(@NonNull byte[] setterArg) {
|
||||||
|
this.ciphertextHash = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull CryptographyResult build() {
|
||||||
|
CryptographyResult pigeonReturn = new CryptographyResult();
|
||||||
|
pigeonReturn.setPlaintextHash(plaintextHash);
|
||||||
|
pigeonReturn.setCiphertextHash(ciphertextHash);
|
||||||
|
return pigeonReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
ArrayList<Object> toList() {
|
||||||
|
ArrayList<Object> toListResult = new ArrayList<Object>(2);
|
||||||
|
toListResult.add(plaintextHash);
|
||||||
|
toListResult.add(ciphertextHash);
|
||||||
|
return toListResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NonNull CryptographyResult fromList(@NonNull ArrayList<Object> list) {
|
||||||
|
CryptographyResult pigeonResult = new CryptographyResult();
|
||||||
|
Object plaintextHash = list.get(0);
|
||||||
|
pigeonResult.setPlaintextHash((byte[]) plaintextHash);
|
||||||
|
Object ciphertextHash = list.get(1);
|
||||||
|
pigeonResult.setCiphertextHash((byte[]) ciphertextHash);
|
||||||
|
return pigeonResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Result<T> {
|
||||||
|
@SuppressWarnings("UnknownNullness")
|
||||||
|
void success(T result);
|
||||||
|
|
||||||
|
void error(@NonNull Throwable error);
|
||||||
|
}
|
||||||
|
|
||||||
private static class MoxplatformApiCodec extends StandardMessageCodec {
|
private static class MoxplatformApiCodec extends StandardMessageCodec {
|
||||||
public static final MoxplatformApiCodec INSTANCE = new MoxplatformApiCodec();
|
public static final MoxplatformApiCodec INSTANCE = new MoxplatformApiCodec();
|
||||||
|
|
||||||
@ -913,16 +1005,18 @@ public class Api {
|
|||||||
protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
|
protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case (byte) 128:
|
case (byte) 128:
|
||||||
return MessagingNotification.fromList((ArrayList<Object>) readValue(buffer));
|
return CryptographyResult.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
case (byte) 129:
|
case (byte) 129:
|
||||||
return NotificationEvent.fromList((ArrayList<Object>) readValue(buffer));
|
return MessagingNotification.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
case (byte) 130:
|
case (byte) 130:
|
||||||
return NotificationI18nData.fromList((ArrayList<Object>) readValue(buffer));
|
return NotificationEvent.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
case (byte) 131:
|
case (byte) 131:
|
||||||
return NotificationMessage.fromList((ArrayList<Object>) readValue(buffer));
|
return NotificationI18nData.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
case (byte) 132:
|
case (byte) 132:
|
||||||
return NotificationMessageContent.fromList((ArrayList<Object>) readValue(buffer));
|
return NotificationMessage.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
case (byte) 133:
|
case (byte) 133:
|
||||||
|
return NotificationMessageContent.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
|
case (byte) 134:
|
||||||
return RegularNotification.fromList((ArrayList<Object>) readValue(buffer));
|
return RegularNotification.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
default:
|
default:
|
||||||
return super.readValueOfType(type, buffer);
|
return super.readValueOfType(type, buffer);
|
||||||
@ -931,23 +1025,26 @@ public class Api {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
|
protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
|
||||||
if (value instanceof MessagingNotification) {
|
if (value instanceof CryptographyResult) {
|
||||||
stream.write(128);
|
stream.write(128);
|
||||||
|
writeValue(stream, ((CryptographyResult) value).toList());
|
||||||
|
} else if (value instanceof MessagingNotification) {
|
||||||
|
stream.write(129);
|
||||||
writeValue(stream, ((MessagingNotification) value).toList());
|
writeValue(stream, ((MessagingNotification) value).toList());
|
||||||
} else if (value instanceof NotificationEvent) {
|
} else if (value instanceof NotificationEvent) {
|
||||||
stream.write(129);
|
stream.write(130);
|
||||||
writeValue(stream, ((NotificationEvent) value).toList());
|
writeValue(stream, ((NotificationEvent) value).toList());
|
||||||
} else if (value instanceof NotificationI18nData) {
|
} else if (value instanceof NotificationI18nData) {
|
||||||
stream.write(130);
|
stream.write(131);
|
||||||
writeValue(stream, ((NotificationI18nData) value).toList());
|
writeValue(stream, ((NotificationI18nData) value).toList());
|
||||||
} else if (value instanceof NotificationMessage) {
|
} else if (value instanceof NotificationMessage) {
|
||||||
stream.write(131);
|
stream.write(132);
|
||||||
writeValue(stream, ((NotificationMessage) value).toList());
|
writeValue(stream, ((NotificationMessage) value).toList());
|
||||||
} else if (value instanceof NotificationMessageContent) {
|
} else if (value instanceof NotificationMessageContent) {
|
||||||
stream.write(132);
|
stream.write(133);
|
||||||
writeValue(stream, ((NotificationMessageContent) value).toList());
|
writeValue(stream, ((NotificationMessageContent) value).toList());
|
||||||
} else if (value instanceof RegularNotification) {
|
} else if (value instanceof RegularNotification) {
|
||||||
stream.write(133);
|
stream.write(134);
|
||||||
writeValue(stream, ((RegularNotification) value).toList());
|
writeValue(stream, ((RegularNotification) value).toList());
|
||||||
} else {
|
} else {
|
||||||
super.writeValue(stream, value);
|
super.writeValue(stream, value);
|
||||||
@ -957,7 +1054,7 @@ public class Api {
|
|||||||
|
|
||||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||||
public interface MoxplatformApi {
|
public interface MoxplatformApi {
|
||||||
|
/** Notification APIs */
|
||||||
void createNotificationChannel(@NonNull String title, @NonNull String description, @NonNull String id, @NonNull Boolean urgent);
|
void createNotificationChannel(@NonNull String title, @NonNull String description, @NonNull String id, @NonNull Boolean urgent);
|
||||||
|
|
||||||
void showMessagingNotification(@NonNull MessagingNotification notification);
|
void showMessagingNotification(@NonNull MessagingNotification notification);
|
||||||
@ -969,13 +1066,19 @@ public class Api {
|
|||||||
void setNotificationSelfAvatar(@NonNull String path);
|
void setNotificationSelfAvatar(@NonNull String path);
|
||||||
|
|
||||||
void setNotificationI18n(@NonNull NotificationI18nData data);
|
void setNotificationI18n(@NonNull NotificationI18nData data);
|
||||||
|
/** Platform APIs */
|
||||||
@NonNull
|
@NonNull
|
||||||
String getPersistentDataPath();
|
String getPersistentDataPath();
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
String getCacheDataPath();
|
String getCacheDataPath();
|
||||||
|
/** Cryptography APIs */
|
||||||
|
void encryptFile(@NonNull String sourcePath, @NonNull String destPath, @NonNull byte[] key, @NonNull byte[] iv, @NonNull CipherAlgorithm algorithm, @NonNull String hashSpec, @NonNull Result<CryptographyResult> result);
|
||||||
|
|
||||||
|
void decryptFile(@NonNull String sourcePath, @NonNull String destPath, @NonNull byte[] key, @NonNull byte[] iv, @NonNull CipherAlgorithm algorithm, @NonNull String hashSpec, @NonNull Result<CryptographyResult> result);
|
||||||
|
|
||||||
|
void hashFile(@NonNull String sourcePath, @NonNull String hashSpec, @NonNull Result<byte[]> result);
|
||||||
|
/** Stubs */
|
||||||
void eventStub(@NonNull NotificationEvent event);
|
void eventStub(@NonNull NotificationEvent event);
|
||||||
|
|
||||||
/** The codec used by MoxplatformApi. */
|
/** The codec used by MoxplatformApi. */
|
||||||
@ -1175,6 +1278,104 @@ public class Api {
|
|||||||
channel.setMessageHandler(null);
|
channel.setMessageHandler(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.encryptFile", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
String sourcePathArg = (String) args.get(0);
|
||||||
|
String destPathArg = (String) args.get(1);
|
||||||
|
byte[] keyArg = (byte[]) args.get(2);
|
||||||
|
byte[] ivArg = (byte[]) args.get(3);
|
||||||
|
CipherAlgorithm algorithmArg = args.get(4) == null ? null : CipherAlgorithm.values()[(int) args.get(4)];
|
||||||
|
String hashSpecArg = (String) args.get(5);
|
||||||
|
Result<CryptographyResult> resultCallback =
|
||||||
|
new Result<CryptographyResult>() {
|
||||||
|
public void success(CryptographyResult result) {
|
||||||
|
wrapped.add(0, result);
|
||||||
|
reply.reply(wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(Throwable error) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(error);
|
||||||
|
reply.reply(wrappedError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
api.encryptFile(sourcePathArg, destPathArg, keyArg, ivArg, algorithmArg, hashSpecArg, resultCallback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.decryptFile", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
String sourcePathArg = (String) args.get(0);
|
||||||
|
String destPathArg = (String) args.get(1);
|
||||||
|
byte[] keyArg = (byte[]) args.get(2);
|
||||||
|
byte[] ivArg = (byte[]) args.get(3);
|
||||||
|
CipherAlgorithm algorithmArg = args.get(4) == null ? null : CipherAlgorithm.values()[(int) args.get(4)];
|
||||||
|
String hashSpecArg = (String) args.get(5);
|
||||||
|
Result<CryptographyResult> resultCallback =
|
||||||
|
new Result<CryptographyResult>() {
|
||||||
|
public void success(CryptographyResult result) {
|
||||||
|
wrapped.add(0, result);
|
||||||
|
reply.reply(wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(Throwable error) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(error);
|
||||||
|
reply.reply(wrappedError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
api.decryptFile(sourcePathArg, destPathArg, keyArg, ivArg, algorithmArg, hashSpecArg, resultCallback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.hashFile", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
String sourcePathArg = (String) args.get(0);
|
||||||
|
String hashSpecArg = (String) args.get(1);
|
||||||
|
Result<byte[]> resultCallback =
|
||||||
|
new Result<byte[]>() {
|
||||||
|
public void success(byte[] result) {
|
||||||
|
wrapped.add(0, result);
|
||||||
|
reply.reply(wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(Throwable error) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(error);
|
||||||
|
reply.reply(wrappedError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
api.hashFile(sourcePathArg, hashSpecArg, resultCallback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
BasicMessageChannel<Object> channel =
|
BasicMessageChannel<Object> channel =
|
||||||
new BasicMessageChannel<>(
|
new BasicMessageChannel<>(
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package me.polynom.moxplatform_android
|
package me.polynom.moxplatform_android
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import me.polynom.moxplatform_android.Api.CipherAlgorithm
|
||||||
|
import me.polynom.moxplatform_android.Api.CryptographyResult
|
||||||
|
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
@ -10,6 +12,7 @@ import javax.crypto.Cipher
|
|||||||
import javax.crypto.CipherOutputStream
|
import javax.crypto.CipherOutputStream
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
// A FileOutputStream that continuously hashes whatever it writes to the file.
|
// A FileOutputStream that continuously hashes whatever it writes to the file.
|
||||||
private class HashedFileOutputStream(name: String, hashAlgorithm: String) : FileOutputStream(name) {
|
private class HashedFileOutputStream(name: String, hashAlgorithm: String) : FileOutputStream(name) {
|
||||||
@ -30,115 +33,126 @@ private class HashedFileOutputStream(name: String, hashAlgorithm: String) : File
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCipherSpecFromInteger(algorithmType: Int): String {
|
fun getCipherSpecFromInteger(algorithm: CipherAlgorithm): String {
|
||||||
return when (algorithmType) {
|
return when (algorithm) {
|
||||||
0 -> "AES_128/GCM/NoPadding"
|
CipherAlgorithm.AES128GCM_NO_PADDING -> "AES_128/GCM/NoPadding"
|
||||||
1 -> "AES_256/GCM/NoPadding"
|
CipherAlgorithm.AES256GCM_NO_PADDING -> "AES_256/GCM/NoPadding"
|
||||||
2 -> "AES_256/CBC/PKCS7PADDING"
|
CipherAlgorithm.AES256CBC_PKCS7 -> "AES_256/CBC/PKCS7PADDING"
|
||||||
else -> ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the hash, specified by @algorithm, of the file at path @srcFile. If an exception
|
// Compute the hash, specified by @algorithm, of the file at path @srcFile. If an exception
|
||||||
// occurs, returns null. If everything went well, returns the raw hash of @srcFile.
|
// occurs, returns null. If everything went well, returns the raw hash of @srcFile.
|
||||||
fun hashFile(srcFile: String, algorithm: String): ByteArray? {
|
fun hashFile(srcFile: String, algorithm: String, result: Api.Result<ByteArray?>) {
|
||||||
val buffer = ByteArray(BUFFER_SIZE)
|
thread(start = true) {
|
||||||
try {
|
val buffer = ByteArray(BUFFER_SIZE)
|
||||||
val digest = MessageDigest.getInstance(algorithm)
|
try {
|
||||||
val fInputStream = FileInputStream(srcFile)
|
val digest = MessageDigest.getInstance(algorithm)
|
||||||
var length: Int
|
val fInputStream = FileInputStream(srcFile)
|
||||||
|
var length: Int
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
length = fInputStream.read()
|
length = fInputStream.read()
|
||||||
if (length <= 0) break
|
if (length <= 0) break
|
||||||
|
|
||||||
// Only update the digest if we read more than 0 bytes
|
// Only update the digest if we read more than 0 bytes
|
||||||
digest.update(buffer, 0, length)
|
digest.update(buffer, 0, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputStream.close()
|
||||||
|
|
||||||
|
result.success(digest.digest())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "[hashFile]: " + e.stackTraceToString())
|
||||||
|
result.success(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fInputStream.close()
|
|
||||||
|
|
||||||
return digest.digest()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "[hashFile]: " + e.stackTraceToString())
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt the plaintext file at @src to @dest using the secret key @key and the IV @iv. The algorithm is chosen using @cipherAlgorithm. The file is additionally
|
// Encrypt the plaintext file at @src to @dest using the secret key @key and the IV @iv. The algorithm is chosen using @cipherAlgorithm. The file is additionally
|
||||||
// hashed before and after encryption using the hash algorithm specified by @hashAlgorithm.
|
// hashed before and after encryption using the hash algorithm specified by @hashAlgorithm.
|
||||||
fun encryptAndHash(src: String, dest: String, key: ByteArray, iv: ByteArray, cipherAlgorithm: String, hashAlgorithm: String): HashMap<String, ByteArray>? {
|
fun encryptAndHash(src: String, dest: String, key: ByteArray, iv: ByteArray, cipherAlgorithm: CipherAlgorithm, hashAlgorithm: String, result: Api.Result<CryptographyResult?>) {
|
||||||
val buffer = ByteArray(BUFFER_SIZE)
|
thread(start = true) {
|
||||||
val secretKey = SecretKeySpec(key, cipherAlgorithm)
|
val cipherSpec = getCipherSpecFromInteger(cipherAlgorithm)
|
||||||
try {
|
val buffer = ByteArray(BUFFER_SIZE)
|
||||||
val digest = MessageDigest.getInstance(hashAlgorithm)
|
val secretKey = SecretKeySpec(key, cipherSpec)
|
||||||
val cipher = Cipher.getInstance(cipherAlgorithm)
|
try {
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
|
val digest = MessageDigest.getInstance(hashAlgorithm)
|
||||||
|
val cipher = Cipher.getInstance(cipherSpec)
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
|
||||||
|
|
||||||
val fileInputStream = FileInputStream(src)
|
val fileInputStream = FileInputStream(src)
|
||||||
val fileOutputStream = HashedFileOutputStream(dest, hashAlgorithm)
|
val fileOutputStream = HashedFileOutputStream(dest, hashAlgorithm)
|
||||||
val cipherOutputStream = CipherOutputStream(fileOutputStream, cipher)
|
val cipherOutputStream = CipherOutputStream(fileOutputStream, cipher)
|
||||||
|
|
||||||
var length: Int
|
var length: Int
|
||||||
while (true) {
|
while (true) {
|
||||||
length = fileInputStream.read(buffer)
|
length = fileInputStream.read(buffer)
|
||||||
if (length <= 0) break
|
if (length <= 0) break
|
||||||
|
|
||||||
digest.update(buffer, 0, length)
|
digest.update(buffer, 0, length)
|
||||||
cipherOutputStream.write(buffer, 0, length)
|
cipherOutputStream.write(buffer, 0, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush and close
|
||||||
|
cipherOutputStream.flush()
|
||||||
|
cipherOutputStream.close()
|
||||||
|
fileInputStream.close()
|
||||||
|
|
||||||
|
result.success(
|
||||||
|
CryptographyResult().apply {
|
||||||
|
plaintextHash = digest.digest()
|
||||||
|
ciphertextHash = fileOutputStream.digest()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "[encryptAndHash]: " + e.stackTraceToString())
|
||||||
|
result.success(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush and close
|
|
||||||
cipherOutputStream.flush()
|
|
||||||
cipherOutputStream.close()
|
|
||||||
fileInputStream.close()
|
|
||||||
|
|
||||||
return hashMapOf(
|
|
||||||
"plaintextHash" to digest.digest(),
|
|
||||||
"ciphertextHash" to fileOutputStream.digest(),
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "[encryptAndHash]: " + e.stackTraceToString())
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt the ciphertext file at @src to @dest using the secret key @key and the IV @iv. The algorithm is chosen using @cipherAlgorithm. The file is additionally
|
// Decrypt the ciphertext file at @src to @dest using the secret key @key and the IV @iv. The algorithm is chosen using @cipherAlgorithm. The file is additionally
|
||||||
// hashed before and after decryption using the hash algorithm specified by @hashAlgorithm.
|
// hashed before and after decryption using the hash algorithm specified by @hashAlgorithm.
|
||||||
fun decryptAndHash(src: String, dest: String, key: ByteArray, iv: ByteArray, cipherAlgorithm: String, hashAlgorithm: String): HashMap<String, ByteArray>? {
|
fun decryptAndHash(src: String, dest: String, key: ByteArray, iv: ByteArray, cipherAlgorithm: CipherAlgorithm, hashAlgorithm: String, result: Api.Result<CryptographyResult?>) {
|
||||||
// Shamelessly stolen from https://github.com/hugo-pcl/native-crypto-flutter/pull/3
|
thread(start = true) {
|
||||||
val buffer = ByteArray(BUFFER_SIZE)
|
val cipherSpec = getCipherSpecFromInteger(cipherAlgorithm)
|
||||||
val secretKey = SecretKeySpec(key, cipherAlgorithm)
|
// Shamelessly stolen from https://github.com/hugo-pcl/native-crypto-flutter/pull/3
|
||||||
try {
|
val buffer = ByteArray(BUFFER_SIZE)
|
||||||
val digest = MessageDigest.getInstance(hashAlgorithm)
|
val secretKey = SecretKeySpec(key, cipherSpec)
|
||||||
val cipher = Cipher.getInstance(cipherAlgorithm)
|
try {
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
|
val digest = MessageDigest.getInstance(hashAlgorithm)
|
||||||
|
val cipher = Cipher.getInstance(cipherSpec)
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
|
||||||
|
|
||||||
val fileInputStream = FileInputStream(src)
|
val fileInputStream = FileInputStream(src)
|
||||||
val fileOutputStream = HashedFileOutputStream(dest, hashAlgorithm)
|
val fileOutputStream = HashedFileOutputStream(dest, hashAlgorithm)
|
||||||
val cipherOutputStream = CipherOutputStream(fileOutputStream, cipher)
|
val cipherOutputStream = CipherOutputStream(fileOutputStream, cipher)
|
||||||
|
|
||||||
// Read, decrypt, and hash until we read 0 bytes
|
// Read, decrypt, and hash until we read 0 bytes
|
||||||
var length: Int
|
var length: Int
|
||||||
while (true) {
|
while (true) {
|
||||||
length = fileInputStream.read(buffer)
|
length = fileInputStream.read(buffer)
|
||||||
if (length <= 0) break
|
if (length <= 0) break
|
||||||
|
|
||||||
digest.update(buffer, 0, length)
|
digest.update(buffer, 0, length)
|
||||||
cipherOutputStream.write(buffer, 0, length)
|
cipherOutputStream.write(buffer, 0, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush
|
||||||
|
cipherOutputStream.flush()
|
||||||
|
cipherOutputStream.close()
|
||||||
|
fileInputStream.close()
|
||||||
|
|
||||||
|
result.success(
|
||||||
|
CryptographyResult().apply {
|
||||||
|
plaintextHash = digest.digest()
|
||||||
|
ciphertextHash = fileOutputStream.digest()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "[hashAndDecrypt]: " + e.stackTraceToString())
|
||||||
|
result.success(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush
|
|
||||||
cipherOutputStream.flush()
|
|
||||||
cipherOutputStream.close()
|
|
||||||
fileInputStream.close()
|
|
||||||
|
|
||||||
return hashMapOf(
|
|
||||||
"plaintextHash" to digest.digest(),
|
|
||||||
"ciphertextHash" to fileOutputStream.digest(),
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "[hashAndDecrypt]: " + e.stackTraceToString())
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -40,7 +40,7 @@ import io.flutter.plugin.common.JSONMethodCodec;
|
|||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
|
|
||||||
public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler, ServiceAware, MoxplatformApi {
|
public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler, ServiceAware, MoxplatformApi {
|
||||||
public static final String entrypointKey = "entrypoint_handle";
|
public static final String entrypointKey = "entrypoint_handle";
|
||||||
public static final String extraDataKey = "extra_data";
|
public static final String extraDataKey = "extra_data";
|
||||||
private static final String autoStartAtBootKey = "auto_start_at_boot";
|
private static final String autoStartAtBootKey = "auto_start_at_boot";
|
||||||
@ -166,53 +166,6 @@ import kotlin.jvm.functions.Function1;
|
|||||||
}
|
}
|
||||||
result.success(true);
|
result.success(true);
|
||||||
break;
|
break;
|
||||||
case "encryptFile":
|
|
||||||
Thread encryptionThread = new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ArrayList args = (ArrayList) call.arguments;
|
|
||||||
String src = (String) args.get(0);
|
|
||||||
String dest = (String) args.get(1);
|
|
||||||
byte[] key = (byte[]) args.get(2);
|
|
||||||
byte[] iv = (byte[]) args.get(3);
|
|
||||||
int algorithm = (int) args.get(4);
|
|
||||||
String hashSpec = (String) args.get(5);
|
|
||||||
|
|
||||||
result.success(encryptAndHash(src, dest, key, iv, getCipherSpecFromInteger(algorithm), hashSpec));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
encryptionThread.start();
|
|
||||||
break;
|
|
||||||
case "decryptFile":
|
|
||||||
Thread decryptionThread = new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ArrayList args = (ArrayList) call.arguments;
|
|
||||||
String src = (String) args.get(0);
|
|
||||||
String dest = (String) args.get(1);
|
|
||||||
byte[] key = (byte[]) args.get(2);
|
|
||||||
byte[] iv = (byte[]) args.get(3);
|
|
||||||
int algorithm = (int) args.get(4);
|
|
||||||
String hashSpec = (String) args.get(5);
|
|
||||||
|
|
||||||
result.success(decryptAndHash(src, dest, key, iv, getCipherSpecFromInteger(algorithm), hashSpec));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
decryptionThread.start();
|
|
||||||
break;
|
|
||||||
case "hashFile":
|
|
||||||
Thread hashingThread = new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ArrayList args = (ArrayList) call.arguments;
|
|
||||||
String src = (String) args.get(0);
|
|
||||||
String hashSpec = (String) args.get(1);
|
|
||||||
|
|
||||||
result.success(hashFile(src, hashSpec));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
hashingThread.start();
|
|
||||||
break;
|
|
||||||
case "recordSentMessage":
|
case "recordSentMessage":
|
||||||
ArrayList rargs = (ArrayList) call.arguments;
|
ArrayList rargs = (ArrayList) call.arguments;
|
||||||
recordSentMessage(context, (String) rargs.get(0), (String) rargs.get(1), (String) rargs.get(2), (int) rargs.get(3));
|
recordSentMessage(context, (String) rargs.get(0), (String) rargs.get(1), (String) rargs.get(2), (int) rargs.get(3));
|
||||||
@ -308,6 +261,37 @@ import kotlin.jvm.functions.Function1;
|
|||||||
return context.getCacheDir().getPath();
|
return context.getCacheDir().getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encryptFile(@NonNull String sourcePath, @NonNull String destPath, @NonNull byte[] key, @NonNull byte[] iv, @NonNull CipherAlgorithm algorithm, @NonNull String hashSpec, @NonNull Api.Result<CryptographyResult> result) {
|
||||||
|
CryptoKt.encryptAndHash(
|
||||||
|
sourcePath,
|
||||||
|
destPath,
|
||||||
|
key,
|
||||||
|
iv,
|
||||||
|
algorithm,
|
||||||
|
hashSpec,
|
||||||
|
result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decryptFile(@NonNull String sourcePath, @NonNull String destPath, @NonNull byte[] key, @NonNull byte[] iv, @NonNull CipherAlgorithm algorithm, @NonNull String hashSpec, @NonNull Api.Result<CryptographyResult> result) {
|
||||||
|
CryptoKt.decryptAndHash(
|
||||||
|
sourcePath,
|
||||||
|
destPath,
|
||||||
|
key,
|
||||||
|
iv,
|
||||||
|
algorithm,
|
||||||
|
hashSpec,
|
||||||
|
result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hashFile(@NonNull String sourcePath, @NonNull String hashSpec, @NonNull Api.Result<byte[]> result) {
|
||||||
|
CryptoKt.hashFile(sourcePath, hashSpec, result);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventStub(@NonNull NotificationEvent event) {
|
public void eventStub(@NonNull NotificationEvent event) {
|
||||||
// Stub to trick pigeon into
|
// Stub to trick pigeon into
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
||||||
|
|
||||||
class AndroidCryptographyImplementation extends CryptographyImplementation {
|
class AndroidCryptographyImplementation extends CryptographyImplementation {
|
||||||
final _methodChannel = const MethodChannel('me.polynom.moxplatform_android');
|
final MoxplatformApi _api = MoxplatformApi();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CryptographyResult?> encryptFile(
|
Future<CryptographyResult?> encryptFile(
|
||||||
@ -13,22 +13,13 @@ class AndroidCryptographyImplementation extends CryptographyImplementation {
|
|||||||
CipherAlgorithm algorithm,
|
CipherAlgorithm algorithm,
|
||||||
String hashSpec,
|
String hashSpec,
|
||||||
) async {
|
) async {
|
||||||
final dynamic resultRaw =
|
return _api.encryptFile(
|
||||||
await _methodChannel.invokeMethod<dynamic>('encryptFile', [
|
|
||||||
sourcePath,
|
sourcePath,
|
||||||
destPath,
|
destPath,
|
||||||
key,
|
key,
|
||||||
iv,
|
iv,
|
||||||
algorithm.value,
|
algorithm,
|
||||||
hashSpec,
|
hashSpec,
|
||||||
]);
|
|
||||||
if (resultRaw == null) return null;
|
|
||||||
|
|
||||||
// ignore: argument_type_not_assignable
|
|
||||||
final result = Map<String, dynamic>.from(resultRaw);
|
|
||||||
return CryptographyResult(
|
|
||||||
result['plaintextHash']! as Uint8List,
|
|
||||||
result['ciphertextHash']! as Uint8List,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,35 +32,18 @@ class AndroidCryptographyImplementation extends CryptographyImplementation {
|
|||||||
CipherAlgorithm algorithm,
|
CipherAlgorithm algorithm,
|
||||||
String hashSpec,
|
String hashSpec,
|
||||||
) async {
|
) async {
|
||||||
final dynamic resultRaw =
|
return _api.decryptFile(
|
||||||
await _methodChannel.invokeMethod<dynamic>('decryptFile', [
|
|
||||||
sourcePath,
|
sourcePath,
|
||||||
destPath,
|
destPath,
|
||||||
key,
|
key,
|
||||||
iv,
|
iv,
|
||||||
algorithm.value,
|
algorithm,
|
||||||
hashSpec,
|
hashSpec,
|
||||||
]);
|
|
||||||
if (resultRaw == null) return null;
|
|
||||||
|
|
||||||
// ignore: argument_type_not_assignable
|
|
||||||
final result = Map<String, dynamic>.from(resultRaw);
|
|
||||||
return CryptographyResult(
|
|
||||||
result['plaintextHash']! as Uint8List,
|
|
||||||
result['ciphertextHash']! as Uint8List,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List?> hashFile(String path, String hashSpec) async {
|
Future<Uint8List?> hashFile(String sourcePath, String hashSpec) async {
|
||||||
final dynamic resultsRaw =
|
return _api.hashFile(sourcePath, hashSpec);
|
||||||
await _methodChannel.invokeMethod<dynamic>('hashFile', [
|
|
||||||
path,
|
|
||||||
hashSpec,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (resultsRaw == null) return null;
|
|
||||||
|
|
||||||
return resultsRaw as Uint8List;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,12 @@ enum NotificationEventType {
|
|||||||
open,
|
open,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CipherAlgorithm {
|
||||||
|
aes128GcmNoPadding,
|
||||||
|
aes256GcmNoPadding,
|
||||||
|
aes256CbcPkcs7,
|
||||||
|
}
|
||||||
|
|
||||||
class NotificationMessageContent {
|
class NotificationMessageContent {
|
||||||
NotificationMessageContent({
|
NotificationMessageContent({
|
||||||
this.body,
|
this.body,
|
||||||
@ -285,28 +291,57 @@ class NotificationI18nData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CryptographyResult {
|
||||||
|
CryptographyResult({
|
||||||
|
required this.plaintextHash,
|
||||||
|
required this.ciphertextHash,
|
||||||
|
});
|
||||||
|
|
||||||
|
Uint8List plaintextHash;
|
||||||
|
|
||||||
|
Uint8List ciphertextHash;
|
||||||
|
|
||||||
|
Object encode() {
|
||||||
|
return <Object?>[
|
||||||
|
plaintextHash,
|
||||||
|
ciphertextHash,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static CryptographyResult decode(Object result) {
|
||||||
|
result as List<Object?>;
|
||||||
|
return CryptographyResult(
|
||||||
|
plaintextHash: result[0]! as Uint8List,
|
||||||
|
ciphertextHash: result[1]! as Uint8List,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _MoxplatformApiCodec extends StandardMessageCodec {
|
class _MoxplatformApiCodec extends StandardMessageCodec {
|
||||||
const _MoxplatformApiCodec();
|
const _MoxplatformApiCodec();
|
||||||
@override
|
@override
|
||||||
void writeValue(WriteBuffer buffer, Object? value) {
|
void writeValue(WriteBuffer buffer, Object? value) {
|
||||||
if (value is MessagingNotification) {
|
if (value is CryptographyResult) {
|
||||||
buffer.putUint8(128);
|
buffer.putUint8(128);
|
||||||
writeValue(buffer, value.encode());
|
writeValue(buffer, value.encode());
|
||||||
} else if (value is NotificationEvent) {
|
} else if (value is MessagingNotification) {
|
||||||
buffer.putUint8(129);
|
buffer.putUint8(129);
|
||||||
writeValue(buffer, value.encode());
|
writeValue(buffer, value.encode());
|
||||||
} else if (value is NotificationI18nData) {
|
} else if (value is NotificationEvent) {
|
||||||
buffer.putUint8(130);
|
buffer.putUint8(130);
|
||||||
writeValue(buffer, value.encode());
|
writeValue(buffer, value.encode());
|
||||||
} else if (value is NotificationMessage) {
|
} else if (value is NotificationI18nData) {
|
||||||
buffer.putUint8(131);
|
buffer.putUint8(131);
|
||||||
writeValue(buffer, value.encode());
|
writeValue(buffer, value.encode());
|
||||||
} else if (value is NotificationMessageContent) {
|
} else if (value is NotificationMessage) {
|
||||||
buffer.putUint8(132);
|
buffer.putUint8(132);
|
||||||
writeValue(buffer, value.encode());
|
writeValue(buffer, value.encode());
|
||||||
} else if (value is RegularNotification) {
|
} else if (value is NotificationMessageContent) {
|
||||||
buffer.putUint8(133);
|
buffer.putUint8(133);
|
||||||
writeValue(buffer, value.encode());
|
writeValue(buffer, value.encode());
|
||||||
|
} else if (value is RegularNotification) {
|
||||||
|
buffer.putUint8(134);
|
||||||
|
writeValue(buffer, value.encode());
|
||||||
} else {
|
} else {
|
||||||
super.writeValue(buffer, value);
|
super.writeValue(buffer, value);
|
||||||
}
|
}
|
||||||
@ -316,16 +351,18 @@ class _MoxplatformApiCodec extends StandardMessageCodec {
|
|||||||
Object? readValueOfType(int type, ReadBuffer buffer) {
|
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 128:
|
case 128:
|
||||||
return MessagingNotification.decode(readValue(buffer)!);
|
return CryptographyResult.decode(readValue(buffer)!);
|
||||||
case 129:
|
case 129:
|
||||||
return NotificationEvent.decode(readValue(buffer)!);
|
return MessagingNotification.decode(readValue(buffer)!);
|
||||||
case 130:
|
case 130:
|
||||||
return NotificationI18nData.decode(readValue(buffer)!);
|
return NotificationEvent.decode(readValue(buffer)!);
|
||||||
case 131:
|
case 131:
|
||||||
return NotificationMessage.decode(readValue(buffer)!);
|
return NotificationI18nData.decode(readValue(buffer)!);
|
||||||
case 132:
|
case 132:
|
||||||
return NotificationMessageContent.decode(readValue(buffer)!);
|
return NotificationMessage.decode(readValue(buffer)!);
|
||||||
case 133:
|
case 133:
|
||||||
|
return NotificationMessageContent.decode(readValue(buffer)!);
|
||||||
|
case 134:
|
||||||
return RegularNotification.decode(readValue(buffer)!);
|
return RegularNotification.decode(readValue(buffer)!);
|
||||||
default:
|
default:
|
||||||
return super.readValueOfType(type, buffer);
|
return super.readValueOfType(type, buffer);
|
||||||
@ -343,6 +380,7 @@ class MoxplatformApi {
|
|||||||
|
|
||||||
static const MessageCodec<Object?> codec = _MoxplatformApiCodec();
|
static const MessageCodec<Object?> codec = _MoxplatformApiCodec();
|
||||||
|
|
||||||
|
/// Notification APIs
|
||||||
Future<void> createNotificationChannel(String arg_title, String arg_description, String arg_id, bool arg_urgent) async {
|
Future<void> createNotificationChannel(String arg_title, String arg_description, String arg_id, bool arg_urgent) async {
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.createNotificationChannel', codec,
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.createNotificationChannel', codec,
|
||||||
@ -475,6 +513,7 @@ class MoxplatformApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Platform APIs
|
||||||
Future<String> getPersistentDataPath() async {
|
Future<String> getPersistentDataPath() async {
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.getPersistentDataPath', codec,
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.getPersistentDataPath', codec,
|
||||||
@ -529,6 +568,74 @@ class MoxplatformApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cryptography APIs
|
||||||
|
Future<CryptographyResult?> encryptFile(String arg_sourcePath, String arg_destPath, Uint8List arg_key, Uint8List arg_iv, CipherAlgorithm arg_algorithm, String arg_hashSpec) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.encryptFile', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_sourcePath, arg_destPath, arg_key, arg_iv, arg_algorithm.index, arg_hashSpec]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as CryptographyResult?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<CryptographyResult?> decryptFile(String arg_sourcePath, String arg_destPath, Uint8List arg_key, Uint8List arg_iv, CipherAlgorithm arg_algorithm, String arg_hashSpec) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.decryptFile', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_sourcePath, arg_destPath, arg_key, arg_iv, arg_algorithm.index, arg_hashSpec]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as CryptographyResult?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List?> hashFile(String arg_sourcePath, String arg_hashSpec) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.hashFile', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_sourcePath, arg_hashSpec]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as Uint8List?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stubs
|
||||||
Future<void> eventStub(NotificationEvent arg_event) async {
|
Future<void> eventStub(NotificationEvent arg_event) async {
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.eventStub', codec,
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.eventStub', codec,
|
||||||
|
@ -1,21 +1,5 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:moxplatform_platform_interface/src/api.g.dart';
|
||||||
enum CipherAlgorithm {
|
|
||||||
aes128GcmNoPadding(0),
|
|
||||||
aes256GcmNoPadding(1),
|
|
||||||
aes256CbcPkcs7(2);
|
|
||||||
|
|
||||||
const CipherAlgorithm(this.value);
|
|
||||||
|
|
||||||
/// The "id" of the algorithm choice.
|
|
||||||
final int value;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CryptographyResult {
|
|
||||||
const CryptographyResult(this.plaintextHash, this.ciphertextHash);
|
|
||||||
final Uint8List plaintextHash;
|
|
||||||
final Uint8List ciphertextHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper around platform-native cryptography APIs
|
/// Wrapper around platform-native cryptography APIs
|
||||||
abstract class CryptographyImplementation {
|
abstract class CryptographyImplementation {
|
||||||
@ -47,9 +31,9 @@ abstract class CryptographyImplementation {
|
|||||||
String hashSpec,
|
String hashSpec,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Hashes the file at [path] using the Hash function with name [hashSpec].
|
/// Hashes the file at [sourcePath] using the Hash function with name [hashSpec].
|
||||||
/// Note that this function runs off-thread as to not block the UI thread.
|
/// Note that this function runs off-thread as to not block the UI thread.
|
||||||
///
|
///
|
||||||
/// Returns the hash of the file.
|
/// Returns the hash of the file.
|
||||||
Future<Uint8List?> hashFile(String path, String hashSpec);
|
Future<Uint8List?> hashFile(String sourcePath, String hashSpec);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:moxplatform_platform_interface/src/api.g.dart';
|
||||||
import 'package:moxplatform_platform_interface/src/crypto.dart';
|
import 'package:moxplatform_platform_interface/src/crypto.dart';
|
||||||
|
|
||||||
class StubCryptographyImplementation extends CryptographyImplementation {
|
class StubCryptographyImplementation extends CryptographyImplementation {
|
||||||
@ -27,7 +28,7 @@ class StubCryptographyImplementation extends CryptographyImplementation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List?> hashFile(String path, String hashSpec) async {
|
Future<Uint8List?> hashFile(String sourcePath, String hashSpec) async {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,15 +149,37 @@ class NotificationI18nData {
|
|||||||
final String you;
|
final String you;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CipherAlgorithm {
|
||||||
|
aes128GcmNoPadding,
|
||||||
|
aes256GcmNoPadding,
|
||||||
|
aes256CbcPkcs7;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CryptographyResult {
|
||||||
|
const CryptographyResult(this.plaintextHash, this.ciphertextHash);
|
||||||
|
final Uint8List plaintextHash;
|
||||||
|
final Uint8List ciphertextHash;
|
||||||
|
}
|
||||||
|
|
||||||
@HostApi()
|
@HostApi()
|
||||||
abstract class MoxplatformApi {
|
abstract class MoxplatformApi {
|
||||||
|
/// Notification APIs
|
||||||
void createNotificationChannel(String title, String description, String id, bool urgent);
|
void createNotificationChannel(String title, String description, String id, bool urgent);
|
||||||
void showMessagingNotification(MessagingNotification notification);
|
void showMessagingNotification(MessagingNotification notification);
|
||||||
void showNotification(RegularNotification notification);
|
void showNotification(RegularNotification notification);
|
||||||
void dismissNotification(int id);
|
void dismissNotification(int id);
|
||||||
void setNotificationSelfAvatar(String path);
|
void setNotificationSelfAvatar(String path);
|
||||||
void setNotificationI18n(NotificationI18nData data);
|
void setNotificationI18n(NotificationI18nData data);
|
||||||
|
|
||||||
|
/// Platform APIs
|
||||||
String getPersistentDataPath();
|
String getPersistentDataPath();
|
||||||
String getCacheDataPath();
|
String getCacheDataPath();
|
||||||
|
|
||||||
|
/// Cryptography APIs
|
||||||
|
@async CryptographyResult? encryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec);
|
||||||
|
@async CryptographyResult? decryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec);
|
||||||
|
@async Uint8List? hashFile(String sourcePath, String hashSpec);
|
||||||
|
|
||||||
|
/// Stubs
|
||||||
void eventStub(NotificationEvent event);
|
void eventStub(NotificationEvent event);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user