11package com.example.sw0b_001.data
22
33import android.content.Context
4+ import android.security.KeyStoreException
5+ import android.security.keystore.KeyGenParameterSpec
6+ import android.security.keystore.KeyProperties
47import android.util.Base64
58import androidx.core.content.edit
9+ import androidx.datastore.core.IOException
610import com.afkanerd.smswithoutborders.libsignal_doubleratchet.KeystoreHelpers
711import com.afkanerd.smswithoutborders.libsignal_doubleratchet.SecurityCurve25519
812import com.afkanerd.smswithoutborders.libsignal_doubleratchet.SecurityRSA
13+ import com.example.sw0b_001.extensions.context.generateSecureRandom
14+ import com.example.sw0b_001.extensions.context.settingsGetDbPassword
15+ import com.example.sw0b_001.extensions.context.settingsSetDbPassword
16+ import java.security.InvalidAlgorithmParameterException
17+ import java.security.InvalidKeyException
18+ import java.security.KeyStore
19+ import java.security.NoSuchAlgorithmException
20+ import java.security.NoSuchProviderException
21+ import java.security.UnrecoverableEntryException
22+ import javax.crypto.BadPaddingException
23+ import javax.crypto.Cipher
24+ import javax.crypto.IllegalBlockSizeException
25+ import javax.crypto.KeyGenerator
26+ import javax.crypto.NoSuchPaddingException
27+ import javax.crypto.SecretKey
28+ import javax.crypto.spec.GCMParameterSpec
29+ import javax.security.cert.CertificateException
930
1031object Cryptography {
1132 val HYBRID_KEYS_FILE = " com.afkanerd.relaysms.HYBRID_KEYS_FILE"
@@ -77,5 +98,106 @@ object Cryptography {
7798 val libSigCurve25519 = SecurityCurve25519 (privateKey)
7899 return libSigCurve25519.calculateSharedSecret(publicKey)
79100 }
101+ @Throws(
102+ KeyStoreException ::class ,
103+ NoSuchAlgorithmException ::class ,
104+ NoSuchProviderException ::class ,
105+ InvalidAlgorithmParameterException ::class
106+ )
107+ fun createAndStoreSecretKey (keystoreAlias : String ) {
108+ val keyGenerator = KeyGenerator .getInstance(
109+ KeyProperties .KEY_ALGORITHM_AES ,
110+ " AndroidKeyStore"
111+ )
112+
113+ val params = KeyGenParameterSpec .Builder (
114+ keystoreAlias,
115+ KeyProperties .PURPOSE_ENCRYPT or KeyProperties .PURPOSE_DECRYPT
116+ )
117+ .setKeySize(256 )
118+ .setBlockModes(KeyProperties .BLOCK_MODE_GCM )
119+ .setEncryptionPaddings(KeyProperties .ENCRYPTION_PADDING_NONE )
120+ .setRandomizedEncryptionRequired(true )
121+ .build()
122+
123+ keyGenerator.init (params)
124+ keyGenerator.generateKey()
125+ }
126+
127+ @Throws(
128+ KeyStoreException ::class ,
129+ UnrecoverableEntryException ::class ,
130+ NoSuchAlgorithmException ::class ,
131+ CertificateException ::class ,
132+ IOException ::class ,
133+ NoSuchPaddingException ::class ,
134+ InvalidKeyException ::class ,
135+ IllegalBlockSizeException ::class ,
136+ BadPaddingException ::class
137+ )
138+ private fun encryptWithKeyStore (data : ByteArray , keystoreAlias : String ): ByteArray {
139+ if (! KeystoreHelpers .isAvailableInKeystore(keystoreAlias))
140+ createAndStoreSecretKey(keystoreAlias)
141+
142+ // Initialize KeyStore
143+ val keyStore: KeyStore = KeyStore .getInstance(" AndroidKeyStore" )
144+ keyStore.load(null )
145+ // Retrieve the key with alias androidKeyStoreAlias created before
146+ val keyEntry: KeyStore .SecretKeyEntry =
147+ keyStore.getEntry(keystoreAlias, null ) as KeyStore .SecretKeyEntry
148+ val key: SecretKey = keyEntry.secretKey
149+ // Use the secret key at your convenience
150+ val cipher: Cipher = Cipher .getInstance(" AES/GCM/NoPadding" )
151+ cipher.init (Cipher .ENCRYPT_MODE , key)
152+ return cipher.iv + cipher.doFinal(data)
153+ }
154+
155+
156+ @Throws(
157+ KeyStoreException ::class ,
158+ UnrecoverableEntryException ::class ,
159+ NoSuchAlgorithmException ::class ,
160+ CertificateException ::class ,
161+ IOException ::class ,
162+ NoSuchPaddingException ::class ,
163+ InvalidKeyException ::class ,
164+ IllegalBlockSizeException ::class ,
165+ BadPaddingException ::class
166+ )
167+ private fun decryptWithKeyStore (data : ByteArray , keystoreAlias : String ): ByteArray? {
168+ val ivSize = 12 // GCM standard
169+ val iv = data.copyOfRange(0 , ivSize)
170+ val data = data.copyOfRange(ivSize, data.size)
171+
172+ // Initialize KeyStore
173+ val keyStore: KeyStore = KeyStore .getInstance(" AndroidKeyStore" )
174+ keyStore.load(null )
175+ // Retrieve the key with alias androidKeyStoreAlias created before
176+ val keyEntry: KeyStore .SecretKeyEntry =
177+ keyStore.getEntry(keystoreAlias, null ) as KeyStore .SecretKeyEntry
178+ val key: SecretKey = keyEntry.secretKey
179+ // Use the secret key at your convenience
180+ val cipher: Cipher = Cipher .getInstance(" AES/GCM/NoPadding" )
181+ val spec = GCMParameterSpec (128 , iv) // 128-bit auth tag
182+ cipher.init (Cipher .DECRYPT_MODE , key, spec)
183+ return cipher.doFinal(data)
184+ }
185+
186+ @JvmStatic
187+ fun getDatabasePassword (context : Context , keystoreAlias : String ) : ByteArray {
188+ val password = context.settingsGetDbPassword
189+ if (password == null ) {
190+ val password = context.generateSecureRandom()
191+ val encryptedPassword = encryptWithKeyStore(
192+ password,
193+ keystoreAlias
194+ )
195+ context.settingsSetDbPassword(encryptedPassword)
196+ return password
197+ } else {
198+ return decryptWithKeyStore(password, keystoreAlias) ? :
199+ throw Exception (" Failed to decrypt database keystore" )
200+ }
201+ }
80202
81203}
0 commit comments