forked from oxen-io/session-android
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathAESGCM.kt
71 lines (63 loc) · 2.81 KB
/
AESGCM.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package org.session.libsession.utilities
import androidx.annotation.WorkerThread
import org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK
import org.session.libsignal.utilities.ByteUtil
import org.session.libsignal.utilities.Util
import org.session.libsignal.utilities.Hex
import org.whispersystems.curve25519.Curve25519
import javax.crypto.Cipher
import javax.crypto.Mac
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
@WorkerThread
internal object AESGCM {
internal val gcmTagSize = 128
internal val ivSize = 12
internal data class EncryptionResult(
internal val ciphertext: ByteArray,
internal val symmetricKey: ByteArray,
internal val ephemeralPublicKey: ByteArray
)
/**
* Sync. Don't call from the main thread.
*/
internal fun decrypt(ivAndCiphertext: ByteArray, symmetricKey: ByteArray): ByteArray {
val iv = ivAndCiphertext.sliceArray(0 until ivSize)
val ciphertext = ivAndCiphertext.sliceArray(ivSize until ivAndCiphertext.count())
synchronized(CIPHER_LOCK) {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(gcmTagSize, iv))
return cipher.doFinal(ciphertext)
}
}
/**
* Sync. Don't call from the main thread.
*/
internal fun generateSymmetricKey(x25519PublicKey: ByteArray, x25519PrivateKey: ByteArray): ByteArray {
val ephemeralSharedSecret = Curve25519.getInstance(Curve25519.BEST).calculateAgreement(x25519PublicKey, x25519PrivateKey)
val mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec("LOKI".toByteArray(), "HmacSHA256"))
return mac.doFinal(ephemeralSharedSecret)
}
/**
* Sync. Don't call from the main thread.
*/
internal fun encrypt(plaintext: ByteArray, symmetricKey: ByteArray): ByteArray {
val iv = Util.getSecretBytes(ivSize)
synchronized(CIPHER_LOCK) {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(gcmTagSize, iv))
return ByteUtil.combine(iv, cipher.doFinal(plaintext))
}
}
/**
* Sync. Don't call from the main thread.
*/
internal fun encrypt(plaintext: ByteArray, hexEncodedX25519PublicKey: String): EncryptionResult {
val x25519PublicKey = Hex.fromStringCondensed(hexEncodedX25519PublicKey)
val ephemeralKeyPair = Curve25519.getInstance(Curve25519.BEST).generateKeyPair()
val symmetricKey = generateSymmetricKey(x25519PublicKey, ephemeralKeyPair.privateKey)
val ciphertext = encrypt(plaintext, symmetricKey)
return EncryptionResult(ciphertext, symmetricKey, ephemeralKeyPair.publicKey)
}
}