1
1
package multiprooflabs.tee.security
2
2
3
- import android.content.Context
4
3
import android.content.SharedPreferences
5
4
import android.security.keystore.KeyGenParameterSpec
6
5
import android.security.keystore.KeyInfo
7
6
import android.security.keystore.KeyProperties
8
7
import android.util.Log
9
- import fr.acinq.secp256k1.Secp256k1
10
- import io.ktor.network.tls.extensions.HashAlgorithm
11
8
import multiprooflabs.tee.security.Utils.Companion.fromHexString
12
9
import multiprooflabs.tee.security.Utils.Companion.toCbor
13
10
import multiprooflabs.tee.security.Utils.Companion.toHexString
11
+ import org.bouncycastle.asn1.x9.ECNamedCurveTable
12
+ import org.bouncycastle.crypto.generators.ECKeyPairGenerator
13
+
14
+ import org.bouncycastle.crypto.params.ECDomainParameters
15
+ import org.bouncycastle.crypto.params.ECKeyGenerationParameters
16
+ import org.bouncycastle.crypto.params.ECPrivateKeyParameters
17
+ import org.bouncycastle.crypto.params.ECPublicKeyParameters
14
18
import org.bouncycastle.jcajce.provider.digest.Keccak
19
+ import org.web3j.crypto.ECKeyPair
20
+ import org.web3j.crypto.Sign
21
+ import java.math.BigInteger
15
22
import java.nio.charset.StandardCharsets
16
23
import java.security.Key
17
24
import java.security.KeyPairGenerator
18
25
import java.security.KeyStore
19
- import java.security.MessageDigest
20
26
import java.security.PrivateKey
21
27
import java.security.PublicKey
22
28
import java.security.SecureRandom
@@ -31,12 +37,12 @@ class TrustedExecutor(private val db: SharedPreferences, private val isStrongbox
31
37
private val TAG = this .javaClass.name
32
38
private val ETHEREUM_MSG_PREFIX = byteArrayOf(25 )
33
39
.plus(" Ethereum Signed Message:\n " .toByteArray())
34
- .plus(32 )
35
40
private val androidKeyStore = " AndroidKeyStore"
36
- private val aliasAttestationKey = " multiprooflabs.ecdsa"
37
- private val aliasSecretKey = " multiprooflabs.aes"
38
- private val PREF_KEY_SK = " secretKey"
39
- private val PREF_KEY_PK = " publicKey"
41
+ private val ALIAS_ANDROID_ATTESTATION_KEY = " multiprooflabs.ecdsa"
42
+ private val ALIAS_ANDROID_SECRET_KEY = " multiprooflabs.aes"
43
+ private val PREFIX_SECP256K1_KEY = " multiprooflabs.secp256k1"
44
+ private val PREFERENCES_PRIVATE_KEY = " $PREFIX_SECP256K1_KEY .PrivateKey"
45
+ private val PREFERENCES_PUBLIC_KEY = " $PREFIX_SECP256K1_KEY .PublicKey"
40
46
private val CIPHER_TRANSFORMATION = " AES/GCM/NoPadding"
41
47
42
48
val pubKey = null
@@ -70,7 +76,6 @@ class TrustedExecutor(private val db: SharedPreferences, private val isStrongbox
70
76
}
71
77
72
78
73
- @OptIn(ExperimentalStdlibApi ::class )
74
79
private fun generateSecretKey (alias : String ) {
75
80
val generator = KeyGenerator .getInstance(KeyProperties .KEY_ALGORITHM_AES , androidKeyStore)
76
81
val purposes = KeyProperties .PURPOSE_ENCRYPT .or (KeyProperties .PURPOSE_DECRYPT )
@@ -91,47 +96,74 @@ class TrustedExecutor(private val db: SharedPreferences, private val isStrongbox
91
96
generateSecp256k1Key()
92
97
}
93
98
94
- fun generateSecp256k1Key (secretKeyAlias : String = aliasSecretKey) {
95
- Log .i(TAG , " Generating secp256k1 key..." )
99
+ fun generateSecp256k1Key () {
100
+ val curve = ECNamedCurveTable .getByName(" secp256k1" )
101
+ val domainParams = ECDomainParameters (
102
+ curve.curve,
103
+ curve.g,
104
+ curve.n,
105
+ curve.h,
106
+ curve.seed
107
+ )
96
108
97
- // Generate secp256k1
98
- val secureRandom = SecureRandom ()
99
- val privKey = ByteArray (32 )
100
- secureRandom.nextBytes(privKey)
101
- // Write to shared preferences
102
- val pubKey = Secp256k1 .pubkeyCreate(privKey)
103
- with (db.edit()) {
104
- putString(PREF_KEY_SK , encrypt(privKey, secretKeyAlias).toHexString())
105
- putString(PREF_KEY_PK , pubKey.toHexString())
106
- commit()
107
- }
109
+ val secureRandom = SecureRandom ()
110
+ val keyParams = ECKeyGenerationParameters (domainParams, secureRandom)
111
+ val generator = ECKeyPairGenerator ()
112
+ generator.init (keyParams)
113
+ val keyPair = generator.generateKeyPair()
114
+
115
+ val privateKey = keyPair.private as ECPrivateKeyParameters
116
+ val publicKey = keyPair.public as ECPublicKeyParameters
117
+
118
+ with (db.edit()) {
119
+ val encryptedPrivateKey = encrypt(privateKey.d.toByteArray()).toHexString()
120
+ val clearPublicKey = publicKey.q.getEncoded(false ).toHexString()
121
+ putString(PREFERENCES_PRIVATE_KEY , encryptedPrivateKey)
122
+ putString(PREFERENCES_PUBLIC_KEY , clearPublicKey)
123
+ commit()
124
+ }
125
+ Log .i(TAG ," New secp256k1 key pair stored successfully into app preferences" )
108
126
}
109
127
110
128
fun getSecp256k1PublicKey (): ByteArray {
111
- return db.getString(PREF_KEY_PK , null )!! .fromHexString()
129
+ return db.getString(PREFERENCES_PUBLIC_KEY , null )!! .fromHexString()
112
130
}
113
131
114
- private fun keccak256 (message : ByteArray ): ByteArray {
132
+ fun keccak256 (message : ByteArray ): ByteArray {
115
133
val keccak256 = Keccak .Digest256 ()
116
134
keccak256.update(message)
117
135
return keccak256.digest()
118
136
}
119
- fun signWithSecp256k1PrivateKey (message : ByteArray , secretKeyAlias : String = aliasSecretKey): ByteArray {
120
- val sk = decrypt(db.getString(PREF_KEY_SK , null )!! .fromHexString())
121
-
137
+ fun signWithSecp256k1PrivateKey (commitment : ByteArray ): ByteArray {
138
+ val privateKey = BigInteger (decrypt(db.getString(PREFERENCES_PRIVATE_KEY , null )!! .fromHexString()))
139
+ val publicKey = Sign .publicKeyFromPrivate(privateKey)
140
+ val keyPair = ECKeyPair (privateKey, publicKey)
141
+ val needToHash = false
142
+ val signature = Sign .signMessage(commitment, keyPair, needToHash)
143
+
144
+ return signature.r
145
+ .plus(signature.s)
146
+ .plus(signature.v)
147
+ }
122
148
123
- val hashedMessage = keccak256(ETHEREUM_MSG_PREFIX .plus(keccak256(message)))
124
- return Secp256k1 .sign(hashedMessage, sk)
149
+ fun getEIP191SignedData (data : ByteArray ): ByteArray {
150
+ val dataLength = data.size.toString().toByteArray()
151
+ return keccak256(
152
+ ETHEREUM_MSG_PREFIX
153
+ .plus(dataLength)
154
+ .plus(data)
155
+ )
125
156
}
126
157
127
158
fun getAddress (): ByteArray {
128
159
val pubKey = getSecp256k1PublicKey()
129
- val hash = keccak256(pubKey)
130
- return hash.sliceArray(IntRange (0 , 19 ))
160
+ val pubKeyWithoutPrefix = pubKey.sliceArray(IntRange (1 , pubKey.size - 1 ))
161
+ val hash = keccak256(pubKeyWithoutPrefix)
162
+ return hash.sliceArray(IntRange (hash.size - 20 , hash.size - 1 ))
131
163
}
132
164
133
- fun getSecp256k1Attestation (secretKeyAlias : String = aliasSecretKey ): ByteArray {
134
- val pubKey = db.getString(PREF_KEY_PK , null )!! .fromHexString()
165
+ fun getSecp256k1Attestation (): ByteArray {
166
+ val pubKey = db.getString(PREFERENCES_PUBLIC_KEY , null )!! .fromHexString()
135
167
return signWithAttestationKey(pubKey)
136
168
}
137
169
@@ -140,7 +172,7 @@ class TrustedExecutor(private val db: SharedPreferences, private val isStrongbox
140
172
return ks.getKey(alias, null )
141
173
}
142
174
143
- private fun encrypt (data : ByteArray , alias : String = aliasSecretKey ): ByteArray {
175
+ private fun encrypt (data : ByteArray , alias : String = ALIAS_ANDROID_SECRET_KEY ): ByteArray {
144
176
val key = getSecretKey(alias)
145
177
val cipher = Cipher .getInstance(CIPHER_TRANSFORMATION )
146
178
cipher.init (Cipher .ENCRYPT_MODE , key)
@@ -155,7 +187,7 @@ class TrustedExecutor(private val db: SharedPreferences, private val isStrongbox
155
187
return result
156
188
}
157
189
158
- private fun decrypt (data : ByteArray , alias : String = aliasSecretKey ): ByteArray {
190
+ private fun decrypt (data : ByteArray , alias : String = ALIAS_ANDROID_SECRET_KEY ): ByteArray {
159
191
val key = getSecretKey(alias)
160
192
val iv = ByteArray (12 )
161
193
val encryptedData = ByteArray (data.size - iv.size)
@@ -172,15 +204,15 @@ class TrustedExecutor(private val db: SharedPreferences, private val isStrongbox
172
204
return cipher.doFinal(encryptedData)
173
205
}
174
206
175
- private fun maybeGenerateSecretKey (alias : String = aliasSecretKey ) {
207
+ private fun maybeGenerateSecretKey (alias : String = ALIAS_ANDROID_SECRET_KEY ) {
176
208
val ks = KeyStore .getInstance(androidKeyStore)
177
209
.apply { load(null ) }
178
210
if (! ks.containsAlias(alias)) {
179
211
generateSecretKey(alias)
180
212
}
181
213
}
182
214
183
- private fun maybeGenerateSigningKey (alias : String = aliasAttestationKey ): KeyStore {
215
+ private fun maybeGenerateSigningKey (alias : String = ALIAS_ANDROID_ATTESTATION_KEY ): KeyStore {
184
216
val ks = KeyStore .getInstance(androidKeyStore).apply { load(null ) }
185
217
186
218
if (! ks.containsAlias(alias)) {
@@ -225,9 +257,9 @@ class TrustedExecutor(private val db: SharedPreferences, private val isStrongbox
225
257
return AttestationCertificate (leaf, intermediate, root).toCbor()
226
258
}
227
259
228
- fun getAttestationKeyPublicKey () = getPublicKey(aliasAttestationKey )
229
- fun getCertificateAttestation () = getCertificateAttestation(aliasAttestationKey )
230
- fun signWithAttestationKey (data : ByteArray ) = sign(aliasAttestationKey , data)
260
+ fun getAttestationKeyPublicKey () = getPublicKey(ALIAS_ANDROID_ATTESTATION_KEY )
261
+ fun getCertificateAttestation () = getCertificateAttestation(ALIAS_ANDROID_ATTESTATION_KEY )
262
+ fun signWithAttestationKey (data : ByteArray ) = sign(ALIAS_ANDROID_ATTESTATION_KEY , data)
231
263
232
264
233
265
}
0 commit comments