From 814156970553ae9e545df1315ea8917128567767 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Fri, 13 Oct 2023 08:27:34 -0700 Subject: [PATCH] Make mdoc Session Encryption work with all nine KA curves. Change from "Ephemeral Key Curve" to "Session Encryption Curve" in preferences since that's more accurate. Also show which mdoc Session Encryption curve was used in the reader. Fix "Show debugging logs" buttons in both wallet and reader app (currently a no-op). Add new unit tests for COSE_Key encode and decode. Also fix mdoc MAC authentication to make it use the reader public key. Test: New unit tests + all unit tests pass. Test: Manually tested all nine curves with 18013-5 presentations. --- .../android/identity/wallet/MainActivity.kt | 6 +- .../wallet/settings/SettingsFragment.kt | 2 +- .../wallet/settings/SettingsScreen.kt | 14 +- .../wallet/settings/SettingsScreenState.kt | 14 +- .../wallet/settings/SettingsViewModel.kt | 8 +- .../identity/wallet/transfer/Communication.kt | 2 +- .../wallet/transfer/TransferManager.kt | 2 +- .../identity/wallet/util/PreferencesHelper.kt | 2 + .../com/android/mdl/appreader/MainActivity.kt | 2 - .../com/android/mdl/appreader/VerifierApp.kt | 1 + .../fragment/ShowDocumentFragment.kt | 20 ++ .../mdl/appreader/settings/UserPreferences.kt | 2 + .../mdl/appreader/transfer/TransferManager.kt | 7 +- .../DeviceRetrievalHelper.java | 13 - .../deviceretrieval/VerificationHelper.java | 9 +- .../com/android/identity/internal/Util.java | 318 +++++++++++++++--- .../sessionencryption/SessionEncryption.java | 16 +- .../com/android/identity/util/Constants.java | 26 -- .../android/identity/internal/UtilTest.java | 67 ++++ .../SessionEncryptionTest.java | 25 +- 20 files changed, 433 insertions(+), 123 deletions(-) diff --git a/appholder/src/main/java/com/android/identity/wallet/MainActivity.kt b/appholder/src/main/java/com/android/identity/wallet/MainActivity.kt index 91a743463..adbbff931 100644 --- a/appholder/src/main/java/com/android/identity/wallet/MainActivity.kt +++ b/appholder/src/main/java/com/android/identity/wallet/MainActivity.kt @@ -13,9 +13,12 @@ import androidx.navigation.findNavController import androidx.navigation.ui.NavigationUI import androidx.navigation.ui.NavigationUI.setupActionBarWithNavController import androidx.navigation.ui.setupWithNavController +import androidx.preference.PreferenceManager import com.android.identity.mdoc.origininfo.OriginInfo import com.android.identity.mdoc.origininfo.OriginInfoReferrerUrl +import com.android.identity.util.Logger import com.android.identity.wallet.databinding.ActivityMainBinding +import com.android.identity.wallet.util.PreferencesHelper import com.android.identity.wallet.util.log import com.android.identity.wallet.util.logError import com.android.identity.wallet.util.logInfo @@ -46,8 +49,7 @@ class MainActivity : AppCompatActivity() { setupDrawerLayout() setupNfc() onNewIntent(intent) - - Security.addProvider(BouncyCastleProvider()) + Logger.setDebugEnabled(PreferencesHelper.isDebugLoggingEnabled()) } private fun setupNfc() { diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsFragment.kt b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsFragment.kt index 4759688b1..8bbfaa0a8 100644 --- a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsFragment.kt +++ b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsFragment.kt @@ -29,7 +29,7 @@ class SettingsFragment : Fragment() { modifier = Modifier.fillMaxSize(), screenState = state, onAutoCloseChanged = settingsViewModel::onConnectionAutoCloseChanged, - onEphemeralKeyCurveChanged = settingsViewModel::onEphemeralKeyCurveChanged, + onSessionEncryptionCurveChanged = settingsViewModel::onEphemeralKeyCurveChanged, onUseStaticHandoverChanged = settingsViewModel::onUseStaticHandoverChanged, onUseL2CAPChanged = settingsViewModel::onL2CAPChanged, onBLEDataRetrievalModeChanged = settingsViewModel::onBleDataRetrievalChanged, diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreen.kt b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreen.kt index 9f5b3be2a..63c2d212f 100644 --- a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreen.kt +++ b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreen.kt @@ -36,7 +36,7 @@ fun SettingsScreen( modifier: Modifier = Modifier, screenState: SettingsScreenState, onAutoCloseChanged: (Boolean) -> Unit, - onEphemeralKeyCurveChanged: (newValue: SettingsScreenState.EphemeralKeyCurveOption) -> Unit, + onSessionEncryptionCurveChanged: (newValue: SettingsScreenState.SessionEncryptionCurveOption) -> Unit, onUseStaticHandoverChanged: (Boolean) -> Unit, onUseL2CAPChanged: (Boolean) -> Unit, onBLEServiceCacheChanged: (Boolean) -> Unit, @@ -63,9 +63,9 @@ fun SettingsScreen( onCheckedChange = onAutoCloseChanged ) SettingsDropDown( - title = "Ephemeral Key Curve", - description = curveLabelFor(screenState.ephemeralKeyCurveOption.toEcCurve()), - onCurveChanged = onEphemeralKeyCurveChanged + title = "Session Encryption Curve", + description = curveLabelFor(screenState.sessionEncryptionCurveOption.toEcCurve()), + onCurveChanged = onSessionEncryptionCurveChanged ) SettingSectionTitle(title = "NFC Engagement") SettingToggle( @@ -185,7 +185,7 @@ private fun SettingsDropDown( modifier: Modifier = Modifier, title: String, description: String, - onCurveChanged: (selection: SettingsScreenState.EphemeralKeyCurveOption) -> Unit + onCurveChanged: (selection: SettingsScreenState.SessionEncryptionCurveOption) -> Unit ) { var dropDownExpanded by remember { mutableStateOf(false) } val expandDropDown = { dropDownExpanded = true } @@ -212,7 +212,7 @@ private fun SettingsDropDown( tint = MaterialTheme.colorScheme.onSurface ) } - val entries = SettingsScreenState.EphemeralKeyCurveOption.values().toList() + val entries = SettingsScreenState.SessionEncryptionCurveOption.values().toList() DropdownMenu( expanded = dropDownExpanded, onDismissRequest = { dropDownExpanded = false } @@ -246,7 +246,7 @@ private fun SettingsScreenPreview() { modifier = Modifier.fillMaxSize(), screenState = SettingsScreenState(), onAutoCloseChanged = {}, - onEphemeralKeyCurveChanged = {}, + onSessionEncryptionCurveChanged = {}, onUseStaticHandoverChanged = {}, onUseL2CAPChanged = {}, onBLEServiceCacheChanged = {}, diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreenState.kt b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreenState.kt index 82fb2be11..ffeb11890 100644 --- a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreenState.kt +++ b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsScreenState.kt @@ -11,7 +11,7 @@ import kotlinx.parcelize.Parcelize @Immutable data class SettingsScreenState( val autoCloseEnabled: Boolean = true, - val ephemeralKeyCurveOption: EphemeralKeyCurveOption = EphemeralKeyCurveOption.P256, + val sessionEncryptionCurveOption: SessionEncryptionCurveOption = SessionEncryptionCurveOption.P256, val useStaticHandover: Boolean = true, val isL2CAPEnabled: Boolean = false, val isBleClearCacheEnabled: Boolean = false, @@ -55,14 +55,16 @@ data class SettingsScreenState( } @Parcelize - enum class EphemeralKeyCurveOption : Parcelable { + enum class SessionEncryptionCurveOption : Parcelable { P256, P384, P521, BrainPoolP256R1, BrainPoolP320R1, BrainPoolP384R1, - BrainPoolP512R1; + BrainPoolP512R1, + X25519, + X448; fun toEcCurve(): Int { @@ -74,11 +76,13 @@ data class SettingsScreenState( BrainPoolP320R1 -> SecureArea.EC_CURVE_BRAINPOOLP320R1 BrainPoolP384R1 -> SecureArea.EC_CURVE_BRAINPOOLP384R1 BrainPoolP512R1 -> SecureArea.EC_CURVE_BRAINPOOLP512R1 + X25519 -> SecureArea.EC_CURVE_X25519 + X448 -> SecureArea.EC_CURVE_X448 } } companion object { - fun fromEcCurve(@EcCurve curve: Int): EphemeralKeyCurveOption { + fun fromEcCurve(@EcCurve curve: Int): SessionEncryptionCurveOption { return when (curve) { SecureArea.EC_CURVE_P256 -> P256 SecureArea.EC_CURVE_P384 -> P384 @@ -87,6 +91,8 @@ data class SettingsScreenState( SecureArea.EC_CURVE_BRAINPOOLP320R1 -> BrainPoolP320R1 SecureArea.EC_CURVE_BRAINPOOLP384R1 -> BrainPoolP384R1 SecureArea.EC_CURVE_BRAINPOOLP512R1 -> BrainPoolP512R1 + SecureArea.EC_CURVE_X25519 -> X25519 + SecureArea.EC_CURVE_X448 -> X448 else -> throw IllegalStateException("Unknown EcCurve") } } diff --git a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsViewModel.kt b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsViewModel.kt index ae1562667..0526dd243 100644 --- a/appholder/src/main/java/com/android/identity/wallet/settings/SettingsViewModel.kt +++ b/appholder/src/main/java/com/android/identity/wallet/settings/SettingsViewModel.kt @@ -14,7 +14,7 @@ class SettingsViewModel : ViewModel() { fun loadSettings() { val settingsState = SettingsScreenState( autoCloseEnabled = PreferencesHelper.isConnectionAutoCloseEnabled(), - ephemeralKeyCurveOption = SettingsScreenState.EphemeralKeyCurveOption.fromEcCurve( + sessionEncryptionCurveOption = SettingsScreenState.SessionEncryptionCurveOption.fromEcCurve( PreferencesHelper.getEphemeralKeyCurveOption() ), useStaticHandover = PreferencesHelper.shouldUseStaticHandover(), @@ -35,10 +35,10 @@ class SettingsViewModel : ViewModel() { } fun onEphemeralKeyCurveChanged( - ephemeralKeyCurveOption: SettingsScreenState.EphemeralKeyCurveOption + sessionEncryptionCurveOption: SettingsScreenState.SessionEncryptionCurveOption ) { - PreferencesHelper.setEphemeralKeyCurveOption(ephemeralKeyCurveOption.toEcCurve()) - mutableSettingsState.update { it.copy(ephemeralKeyCurveOption = ephemeralKeyCurveOption) } + PreferencesHelper.setEphemeralKeyCurveOption(sessionEncryptionCurveOption.toEcCurve()) + mutableSettingsState.update { it.copy(sessionEncryptionCurveOption = sessionEncryptionCurveOption) } } fun onUseStaticHandoverChanged(newValue: Boolean) { diff --git a/appholder/src/main/java/com/android/identity/wallet/transfer/Communication.kt b/appholder/src/main/java/com/android/identity/wallet/transfer/Communication.kt index b0e7c9827..3b2d04891 100644 --- a/appholder/src/main/java/com/android/identity/wallet/transfer/Communication.kt +++ b/appholder/src/main/java/com/android/identity/wallet/transfer/Communication.kt @@ -14,7 +14,7 @@ class Communication private constructor( ) { private var request: DeviceRequest? = null - private var deviceRetrievalHelper: DeviceRetrievalHelper? = null + var deviceRetrievalHelper: DeviceRetrievalHelper? = null fun setupPresentation(deviceRetrievalHelper: DeviceRetrievalHelper) { this.deviceRetrievalHelper = deviceRetrievalHelper diff --git a/appholder/src/main/java/com/android/identity/wallet/transfer/TransferManager.kt b/appholder/src/main/java/com/android/identity/wallet/transfer/TransferManager.kt index 40f6195f1..a4927771b 100644 --- a/appholder/src/main/java/com/android/identity/wallet/transfer/TransferManager.kt +++ b/appholder/src/main/java/com/android/identity/wallet/transfer/TransferManager.kt @@ -212,7 +212,7 @@ class TransferManager private constructor(private val context: Context) { authKey.secureArea, authKey.alias, keyUnlockData, - authKey.attestation.first().publicKey + communication.deviceRetrievalHelper!!.eReaderKey ) } val data = generator.generate() diff --git a/appholder/src/main/java/com/android/identity/wallet/util/PreferencesHelper.kt b/appholder/src/main/java/com/android/identity/wallet/util/PreferencesHelper.kt index f8323196f..2b17e9b0a 100644 --- a/appholder/src/main/java/com/android/identity/wallet/util/PreferencesHelper.kt +++ b/appholder/src/main/java/com/android/identity/wallet/util/PreferencesHelper.kt @@ -6,6 +6,7 @@ import androidx.core.content.edit import androidx.preference.PreferenceManager import com.android.identity.securearea.SecureArea import com.android.identity.securearea.SecureArea.EcCurve +import com.android.identity.util.Logger import java.io.File object PreferencesHelper { @@ -103,6 +104,7 @@ object PreferencesHelper { fun setDebugLoggingEnabled(enabled: Boolean) { sharedPreferences.edit { putBoolean(DEBUG_LOG, enabled) } + Logger.setDebugEnabled(enabled) } @EcCurve diff --git a/appverifier/src/main/java/com/android/mdl/appreader/MainActivity.kt b/appverifier/src/main/java/com/android/mdl/appreader/MainActivity.kt index 458e66a83..0a7bf4ba5 100644 --- a/appverifier/src/main/java/com/android/mdl/appreader/MainActivity.kt +++ b/appverifier/src/main/java/com/android/mdl/appreader/MainActivity.kt @@ -46,8 +46,6 @@ class MainActivity : AppCompatActivity() { addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) } mPendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) - - Security.addProvider(BouncyCastleProvider()) } private fun setupDrawerLayout() { diff --git a/appverifier/src/main/java/com/android/mdl/appreader/VerifierApp.kt b/appverifier/src/main/java/com/android/mdl/appreader/VerifierApp.kt index 2246c695d..cffc77b47 100644 --- a/appverifier/src/main/java/com/android/mdl/appreader/VerifierApp.kt +++ b/appverifier/src/main/java/com/android/mdl/appreader/VerifierApp.kt @@ -19,6 +19,7 @@ class VerifierApp : Application() { Logger.setLogPrinter(AndroidLogPrinter()) DynamicColors.applyToActivitiesIfAvailable(this) userPreferencesInstance = userPreferences + Logger.setDebugEnabled(userPreferences.isDebugLoggingEnabled()) } companion object { diff --git a/appverifier/src/main/java/com/android/mdl/appreader/fragment/ShowDocumentFragment.kt b/appverifier/src/main/java/com/android/mdl/appreader/fragment/ShowDocumentFragment.kt index f033ec735..d8b3ecc41 100644 --- a/appverifier/src/main/java/com/android/mdl/appreader/fragment/ShowDocumentFragment.kt +++ b/appverifier/src/main/java/com/android/mdl/appreader/fragment/ShowDocumentFragment.kt @@ -17,6 +17,8 @@ import androidx.annotation.AttrRes import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import com.android.identity.mdoc.response.DeviceResponseParser +import com.android.identity.securearea.SecureArea +import com.android.identity.securearea.SecureArea.EcCurve import com.android.mdl.appreader.R import com.android.mdl.appreader.databinding.FragmentShowDocumentBinding import com.android.mdl.appreader.issuerauth.SimpleIssuerTrustStore @@ -155,6 +157,23 @@ class ShowDocumentFragment : Fragment() { binding.btNewRequest.visibility = View.GONE } + private fun curveNameFor(ecCurve: Int): String { + return when (ecCurve) { + SecureArea.EC_CURVE_P256 -> "P-256" + SecureArea.EC_CURVE_P384 -> "P-384" + SecureArea.EC_CURVE_P521 -> "P-521" + SecureArea.EC_CURVE_BRAINPOOLP256R1 -> "BrainpoolP256R1" + SecureArea.EC_CURVE_BRAINPOOLP320R1 -> "BrainpoolP320R1" + SecureArea.EC_CURVE_BRAINPOOLP384R1 -> "BrainpoolP384R1" + SecureArea.EC_CURVE_BRAINPOOLP512R1 -> "BrainpoolP512R1" + SecureArea.EC_CURVE_ED25519 -> "Ed25519" + SecureArea.EC_CURVE_X25519 -> "X25519" + SecureArea.EC_CURVE_ED448 -> "Ed448" + SecureArea.EC_CURVE_X448 -> "X448" + else -> throw IllegalArgumentException("Unknown curve $ecCurve") + } + } + private fun formatTextResult(documents: Collection): String { // Create the trustManager to validate the DS Certificate against the list of known // certificates in the app @@ -177,6 +196,7 @@ class ShowDocumentFragment : Fragment() { sb.append("Number of documents returned: ${documents.size}
") sb.append("Address: " + transferManager.mdocConnectionMethod + "
") + sb.append("Session encryption curve: " + curveNameFor(transferManager.getMdocSessionEncryptionCurve()) + "
") sb.append("
") for (doc in documents) { // Get primary color from theme to use in the HTML formatted document. diff --git a/appverifier/src/main/java/com/android/mdl/appreader/settings/UserPreferences.kt b/appverifier/src/main/java/com/android/mdl/appreader/settings/UserPreferences.kt index a34c79994..8d07f3426 100644 --- a/appverifier/src/main/java/com/android/mdl/appreader/settings/UserPreferences.kt +++ b/appverifier/src/main/java/com/android/mdl/appreader/settings/UserPreferences.kt @@ -2,6 +2,7 @@ package com.android.mdl.appreader.settings import android.content.SharedPreferences import androidx.core.content.edit +import com.android.identity.util.Logger class UserPreferences( private val preferences: SharedPreferences @@ -77,6 +78,7 @@ class UserPreferences( fun setDebugLoggingEnabled(enabled: Boolean) { preferences.edit { putBoolean(LOG_ENABLED, enabled) } + Logger.setDebugEnabled(enabled) } fun getReaderAuthentication(): Int { diff --git a/appverifier/src/main/java/com/android/mdl/appreader/transfer/TransferManager.kt b/appverifier/src/main/java/com/android/mdl/appreader/transfer/TransferManager.kt index ec02a85f5..f4060d71c 100644 --- a/appverifier/src/main/java/com/android/mdl/appreader/transfer/TransferManager.kt +++ b/appverifier/src/main/java/com/android/mdl/appreader/transfer/TransferManager.kt @@ -16,6 +16,7 @@ import com.android.identity.mdoc.request.DeviceRequestGenerator import com.android.identity.mdoc.response.DeviceResponseParser import com.android.identity.android.mdoc.deviceretrieval.VerificationHelper import androidx.preference.PreferenceManager +import com.android.identity.internal.Util import com.android.mdl.appreader.R import com.android.mdl.appreader.document.RequestDocumentList import com.android.mdl.appreader.readercertgen.ReaderCertificateGenerator @@ -351,10 +352,14 @@ class TransferManager private constructor(private val context: Context) { val parser = DeviceResponseParser() parser.setSessionTranscript(v.sessionTranscript) - parser.setEphemeralReaderKey(v.ephemeralReaderKey) + parser.setEphemeralReaderKey(v.eReaderKeyPair.private) parser.setDeviceResponse(rb) return parser.parse() } ?: throw IllegalStateException("Verification is null") } ?: throw IllegalStateException("Response not received") } + + fun getMdocSessionEncryptionCurve(): Int { + return Util.getCurve(verification!!.eReaderKeyPair.public) + } } \ No newline at end of file diff --git a/identity-android/src/main/java/com/android/identity/android/mdoc/deviceretrieval/DeviceRetrievalHelper.java b/identity-android/src/main/java/com/android/identity/android/mdoc/deviceretrieval/DeviceRetrievalHelper.java index 355af848a..c886f2247 100644 --- a/identity-android/src/main/java/com/android/identity/android/mdoc/deviceretrieval/DeviceRetrievalHelper.java +++ b/identity-android/src/main/java/com/android/identity/android/mdoc/deviceretrieval/DeviceRetrievalHelper.java @@ -268,19 +268,6 @@ private OptionalLong ensureSessionEncryption(@NonNull byte[] data) { } } DataItem eReaderKeyDataItem = Util.cborDecode(encodedEReaderKey); - @SecureArea.EcCurve int curve; - try { - curve = Util.coseKeyGetCurve(eReaderKeyDataItem); - } catch (IllegalArgumentException e) { - Logger.w(TAG, "No curve identifier in COSE_Key", e); - return OptionalLong.of(Constants.SESSION_DATA_STATUS_ERROR_SESSION_ENCRYPTION); - } - if (curve != SecureArea.EC_CURVE_P256) { - Logger.w(TAG, - String.format(Locale.US, "Expected curve P-256 (%d) but got %d", - SecureArea.EC_CURVE_P256, curve)); - return OptionalLong.of(Constants.SESSION_DATA_STATUS_ERROR_SESSION_ENCRYPTION); - } try { mEReaderKey = Util.coseKeyDecode(eReaderKeyDataItem); } catch (IllegalArgumentException diff --git a/identity-android/src/main/java/com/android/identity/android/mdoc/deviceretrieval/VerificationHelper.java b/identity-android/src/main/java/com/android/identity/android/mdoc/deviceretrieval/VerificationHelper.java index e66357086..90c71c65e 100644 --- a/identity-android/src/main/java/com/android/identity/android/mdoc/deviceretrieval/VerificationHelper.java +++ b/identity-android/src/main/java/com/android/identity/android/mdoc/deviceretrieval/VerificationHelper.java @@ -727,6 +727,10 @@ private void setDeviceEngagement(@NonNull byte[] deviceEngagement, @NonNull Data EngagementParser.Engagement engagement = engagementParser.parse(); PublicKey eDeviceKey = engagement.getESenderKey(); + // Create reader ephemeral key with key to match device ephemeral key's curve. + int curveToUse = Util.getCurve(eDeviceKey); + mEphemeralKeyPair = Util.createEphemeralKeyPair(curveToUse); + byte[] encodedEReaderKeyPub = Util.cborEncode(Util.cborBuildCoseKey(mEphemeralKeyPair.getPublic())); mEncodedSessionTranscript = Util.cborEncode(new CborBuilder() .addArray() @@ -1103,8 +1107,8 @@ byte[] getSessionTranscript() { * @return the ephemeral key used by the reader for session encryption. */ public @NonNull - PrivateKey getEphemeralReaderKey() { - return mEphemeralKeyPair.getPrivate(); + KeyPair getEReaderKeyPair() { + return mEphemeralKeyPair; } /** @@ -1264,7 +1268,6 @@ public Builder(@NonNull Context context, mHelper.mContext = context; mHelper.mListener = listener; mHelper.mListenerExecutor = executor; - mHelper.mEphemeralKeyPair = Util.createEphemeralKeyPair(SecureArea.EC_CURVE_P256); } /** diff --git a/identity/src/main/java/com/android/identity/internal/Util.java b/identity/src/main/java/com/android/identity/internal/Util.java index f54766acd..e50df999d 100644 --- a/identity/src/main/java/com/android/identity/internal/Util.java +++ b/identity/src/main/java/com/android/identity/internal/Util.java @@ -31,6 +31,10 @@ import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERSequenceGenerator; +import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPublicKey; +import org.bouncycastle.jcajce.provider.asymmetric.edec.BCXDHPublicKey; +import org.bouncycastle.jcajce.spec.XDHParameterSpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.BigIntegers; import java.io.ByteArrayInputStream; @@ -60,6 +64,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.interfaces.ECKey; import java.security.interfaces.ECPublicKey; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; @@ -68,6 +73,7 @@ import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidParameterSpecException; +import java.security.spec.X509EncodedKeySpec; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.ParseException; @@ -121,11 +127,12 @@ public class Util { private static final long COSE_ALG_HMAC_256_256 = 5; private static final long CBOR_SEMANTIC_TAG_ENCODED_CBOR = 24; private static final long COSE_KEY_KTY = 1; + private static final long COSE_KEY_TYPE_OKP = 1; private static final long COSE_KEY_TYPE_EC2 = 2; - private static final long COSE_KEY_EC2_CRV = -1; - private static final long COSE_KEY_EC2_X = -2; - private static final long COSE_KEY_EC2_Y = -3; - private static final long COSE_KEY_EC2_CRV_P256 = 1; + private static final long COSE_KEY_PARAM_CRV = -1; + private static final long COSE_KEY_PARAM_X = -2; + private static final long COSE_KEY_PARAM_Y = -3; + private static final long COSE_KEY_PARAM_CRV_P256 = 1; // Not called. private Util() { @@ -1011,20 +1018,144 @@ byte[] sec1EncodeFieldElementAsOctetString(int octetStringSize, BigInteger field return BigIntegers.asUnsignedByteArray(octetStringSize, fieldValue); } + public static + @SecureArea.EcCurve int getCurve(@NonNull PublicKey publicKey) { + String curveName; + try { + if (publicKey instanceof BCXDHPublicKey) { + curveName = ((BCXDHPublicKey) publicKey).getAlgorithm(); + } else if (publicKey instanceof BCEdDSAPublicKey) { + curveName = ((BCEdDSAPublicKey) publicKey).getAlgorithm(); + } else if (publicKey instanceof ECKey) { + AlgorithmParameters params = AlgorithmParameters.getInstance("EC", + new BouncyCastleProvider()); + params.init(((ECKey) publicKey).getParams()); + curveName = params.getParameterSpec(ECGenParameterSpec.class).getName(); + } else { + throw new IllegalArgumentException( + "No support for PublicKey of class " + publicKey.getClass().getName()); + } + } catch (InvalidParameterSpecException | NoSuchAlgorithmException e) { + throw new IllegalArgumentException(e); + } + switch (curveName) { + case "secp256r1": + case "prime256v1": + case "1.2.840.10045.3.1.7": + return SecureArea.EC_CURVE_P256; + case "1.3.132.0.34": + case "secp384r1": + return SecureArea.EC_CURVE_P384; + case "1.3.132.0.35": + case "secp521r1": + return SecureArea.EC_CURVE_P521; + case "1.3.36.3.3.2.8.1.1.7": + case "brainpoolP256r1": + return SecureArea.EC_CURVE_BRAINPOOLP256R1; + case "1.3.36.3.3.2.8.1.1.9": + case "brainpoolP320r1": + return SecureArea.EC_CURVE_BRAINPOOLP320R1; + case "1.3.36.3.3.2.8.1.1.11": + case "brainpoolP384r1": + return SecureArea.EC_CURVE_BRAINPOOLP384R1; + case "1.3.36.3.3.2.8.1.1.13": + case "brainpoolP512r1": + return SecureArea.EC_CURVE_BRAINPOOLP512R1; + case "X448": + return SecureArea.EC_CURVE_X448; + case "X25519": + return SecureArea.EC_CURVE_X25519; + case "Ed448": + return SecureArea.EC_CURVE_ED448; + case "Ed25519": + return SecureArea.EC_CURVE_ED25519; + default: + throw new IllegalArgumentException("Unknown curve name '" + curveName + "'"); + } + } + + private static + int getCurveBitSize(@SecureArea.EcCurve int curve) { + switch (curve) { + case SecureArea.EC_CURVE_P256: return 256; + case SecureArea.EC_CURVE_P384: return 384; + case SecureArea.EC_CURVE_P521: return 521; + case SecureArea.EC_CURVE_BRAINPOOLP256R1: return 256; + case SecureArea.EC_CURVE_BRAINPOOLP320R1: return 320; + case SecureArea.EC_CURVE_BRAINPOOLP384R1: return 384; + case SecureArea.EC_CURVE_BRAINPOOLP512R1: return 512; + case SecureArea.EC_CURVE_X25519: return 256; + case SecureArea.EC_CURVE_ED25519: return 256; + case SecureArea.EC_CURVE_X448: return 448; + case SecureArea.EC_CURVE_ED448: return 448; + default: + throw new IllegalArgumentException("Unknown curve with id " + curve); + } + } + + private static + @NonNull String getCurveNameSECG(@SecureArea.EcCurve int curve) { + switch (curve) { + case SecureArea.EC_CURVE_P256: return "secp256r1"; + case SecureArea.EC_CURVE_P384: return "secp384r1"; + case SecureArea.EC_CURVE_P521: return "secp521r1"; + case SecureArea.EC_CURVE_BRAINPOOLP256R1: return "brainpoolP256r1"; + case SecureArea.EC_CURVE_BRAINPOOLP320R1: return "brainpoolP320r1"; + case SecureArea.EC_CURVE_BRAINPOOLP384R1: return "brainpoolP384r1"; + case SecureArea.EC_CURVE_BRAINPOOLP512R1: return "brainpoolP512r1"; + default: + throw new IllegalArgumentException("Unknown curve with id " + curve); + } + } + public static @NonNull DataItem cborBuildCoseKey(@NonNull PublicKey key) { - ECPublicKey ecKey = (ECPublicKey) key; - ECPoint w = ecKey.getW(); - byte[] x = sec1EncodeFieldElementAsOctetString(32, w.getAffineX()); - byte[] y = sec1EncodeFieldElementAsOctetString(32, w.getAffineY()); - DataItem item = new CborBuilder() - .addMap() - .put(COSE_KEY_KTY, COSE_KEY_TYPE_EC2) - .put(COSE_KEY_EC2_CRV, COSE_KEY_EC2_CRV_P256) - .put(COSE_KEY_EC2_X, x) - .put(COSE_KEY_EC2_Y, y) - .end() - .build().get(0); + @SecureArea.EcCurve int coseCurveIdentifier = getCurve(key); + int keySizeBits = getCurveBitSize(coseCurveIdentifier); + byte[] x; + byte[] y; + DataItem item; + switch (coseCurveIdentifier) { + case SecureArea.EC_CURVE_ED25519: + case SecureArea.EC_CURVE_ED448: + x = ((BCEdDSAPublicKey) key).getPointEncoding(); + item = new CborBuilder() + .addMap() + .put(COSE_KEY_KTY, COSE_KEY_TYPE_OKP) + .put(COSE_KEY_PARAM_CRV, coseCurveIdentifier) + .put(COSE_KEY_PARAM_X, x) + .end() + .build().get(0); + break; + + case SecureArea.EC_CURVE_X25519: + case SecureArea.EC_CURVE_X448: + x = ((BCXDHPublicKey) key).getUEncoding(); + item = new CborBuilder() + .addMap() + .put(COSE_KEY_KTY, COSE_KEY_TYPE_OKP) + .put(COSE_KEY_PARAM_CRV, coseCurveIdentifier) + .put(COSE_KEY_PARAM_X, x) + .end() + .build().get(0); + break; + + default: + ECPublicKey ecKey = (ECPublicKey) key; + ECPoint w = ecKey.getW(); + keySizeBits = getCurveBitSize(coseCurveIdentifier); + x = sec1EncodeFieldElementAsOctetString((keySizeBits + 7)/8, w.getAffineX()); + y = sec1EncodeFieldElementAsOctetString((keySizeBits + 7)/8, w.getAffineY()); + item = new CborBuilder() + .addMap() + .put(COSE_KEY_KTY, COSE_KEY_TYPE_EC2) + .put(COSE_KEY_PARAM_CRV, coseCurveIdentifier) + .put(COSE_KEY_PARAM_X, x) + .put(COSE_KEY_PARAM_Y, y) + .end() + .build().get(0); + break; + } return item; } @@ -1039,9 +1170,9 @@ DataItem cborBuildCoseKeyWithMalformedYPoint(@NonNull PublicKey key) { DataItem item = new CborBuilder() .addMap() .put(COSE_KEY_KTY, COSE_KEY_TYPE_EC2) - .put(COSE_KEY_EC2_CRV, COSE_KEY_EC2_CRV_P256) - .put(COSE_KEY_EC2_X, x) - .put(COSE_KEY_EC2_Y, y) + .put(COSE_KEY_PARAM_CRV, COSE_KEY_PARAM_CRV_P256) + .put(COSE_KEY_PARAM_X, x) + .put(COSE_KEY_PARAM_Y, y) .end() .build().get(0); return item; @@ -1165,35 +1296,36 @@ DataItem cborMapExtract(@NonNull DataItem map, @NonNull String key) { public static @SecureArea.EcCurve int coseKeyGetCurve(@NonNull DataItem coseKey) { - return (int) cborMapExtractNumber(coseKey, COSE_KEY_EC2_CRV); + return (int) cborMapExtractNumber(coseKey, COSE_KEY_PARAM_CRV); } - public static @NonNull - PublicKey coseKeyDecode(@NonNull DataItem coseKey) { - long kty = cborMapExtractNumber(coseKey, COSE_KEY_KTY); - if (kty != COSE_KEY_TYPE_EC2) { - throw new IllegalArgumentException("Expected COSE_KEY_TYPE_EC2, got " + kty); - } - long crv = cborMapExtractNumber(coseKey, COSE_KEY_EC2_CRV); - if (crv != COSE_KEY_EC2_CRV_P256) { - throw new IllegalArgumentException("Expected COSE_KEY_EC2_CRV_P256, got " + crv); - } - byte[] encodedX = cborMapExtractByteString(coseKey, COSE_KEY_EC2_X); - byte[] encodedY = cborMapExtractByteString(coseKey, COSE_KEY_EC2_Y); + private static @NonNull + PublicKey coseKeyDecodeEc2(@NonNull DataItem coseKey) { + long crv = cborMapExtractNumber(coseKey, COSE_KEY_PARAM_CRV); + + int keySizeOctets = (getCurveBitSize((int) crv) + 7) / 8; + String curveName = getCurveNameSECG((int) crv); - if (encodedX.length != 32) { - Logger.w(TAG, "Expected 32 bytes for X in COSE_Key, found " + encodedX.length); + byte[] encodedX = cborMapExtractByteString(coseKey, COSE_KEY_PARAM_X); + byte[] encodedY = cborMapExtractByteString(coseKey, COSE_KEY_PARAM_Y); + + if (encodedX.length != keySizeOctets) { + Logger.w(TAG, + String.format(Locale.US, "Expected %d bytes for X in COSE_Key, found %d", + keySizeOctets, encodedX.length)); } - if (encodedY.length != 32) { - Logger.w(TAG, "Expected 32 bytes for Y in COSE_Key, found " + encodedY.length); + if (encodedY.length != keySizeOctets) { + Logger.w(TAG, + String.format(Locale.US, "Expected %d bytes for Y in COSE_Key, found %d", + keySizeOctets, encodedY.length)); } BigInteger x = new BigInteger(1, encodedX); BigInteger y = new BigInteger(1, encodedY); try { - AlgorithmParameters params = AlgorithmParameters.getInstance("EC"); - params.init(new ECGenParameterSpec("secp256r1")); + AlgorithmParameters params = AlgorithmParameters.getInstance("EC", new BouncyCastleProvider()); + params.init(new ECGenParameterSpec(curveName)); ECParameterSpec ecParameters = params.getParameterSpec(ECParameterSpec.class); ECPoint ecPoint = new ECPoint(x, y); @@ -1203,12 +1335,88 @@ PublicKey coseKeyDecode(@NonNull DataItem coseKey) { return ecPublicKey; } catch (NoSuchAlgorithmException - | InvalidParameterSpecException - | InvalidKeySpecException e) { + | InvalidParameterSpecException + | InvalidKeySpecException e) { + throw new IllegalStateException("Unexpected error", e); + } + } + + private static @NonNull + PublicKey coseKeyDecodeOkp(@NonNull DataItem coseKey) { + long crv = cborMapExtractNumber(coseKey, COSE_KEY_PARAM_CRV); + + int keySizeOctets = (getCurveBitSize((int) crv) + 7) / 8; + + byte[] encodedX = cborMapExtractByteString(coseKey, COSE_KEY_PARAM_X); + + if (encodedX.length != keySizeOctets) { + Logger.w(TAG, + String.format(Locale.US, "Expected %d bytes for X in COSE_Key, found %d", + keySizeOctets, encodedX.length)); + } + + try { + // Unfortunately we need to create an X509 encoded version of the public + // key material, for simplicity we just use prefixes. + byte[] prefix; + KeyFactory kf; + switch ((int) crv) { + case SecureArea.EC_CURVE_ED448: + kf = KeyFactory.getInstance("EdDSA", new BouncyCastleProvider()); + prefix = ED448_X509_ENCODED_PREFIX; + break; + + case SecureArea.EC_CURVE_ED25519: + kf = KeyFactory.getInstance("EdDSA", new BouncyCastleProvider()); + prefix = ED25519_X509_ENCODED_PREFIX; + break; + + case SecureArea.EC_CURVE_X25519: + kf = KeyFactory.getInstance("XDH", new BouncyCastleProvider()); + prefix = X25519_X509_ENCODED_PREFIX; + break; + + case SecureArea.EC_CURVE_X448: + kf = KeyFactory.getInstance("XDH", new BouncyCastleProvider()); + prefix = X448_X509_ENCODED_PREFIX; + break; + + default: + throw new IllegalArgumentException("Unsupported curve with id " + crv); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write(prefix); + baos.write(encodedX); + return kf.generatePublic(new X509EncodedKeySpec(baos.toByteArray())); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException e) { throw new IllegalStateException("Unexpected error", e); } } + private static final byte[] ED25519_X509_ENCODED_PREFIX = + new byte[] {0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00}; + + private static final byte[] X25519_X509_ENCODED_PREFIX = + new byte[] {0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x03, 0x21, 0x00}; + + private static final byte[] ED448_X509_ENCODED_PREFIX = + new byte[] {0x30, 0x43, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x71, 0x03, 0x3a, 0x00}; + + private static final byte[] X448_X509_ENCODED_PREFIX = + new byte[] {0x30, 0x42, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6f, 0x03, 0x39, 0x00}; + + public static @NonNull + PublicKey coseKeyDecode(@NonNull DataItem coseKey) { + long kty = cborMapExtractNumber(coseKey, COSE_KEY_KTY); + if (kty == COSE_KEY_TYPE_EC2) { + return coseKeyDecodeEc2(coseKey); + } else if (kty == COSE_KEY_TYPE_OKP) { + return coseKeyDecodeOkp(coseKey); + } else { + throw new IllegalArgumentException("Expected COSE_KEY_TYPE_EC2 or COSE_KEY_TYPE_OKP, got " + kty); + } + } + public static @NonNull SecretKey calcEMacKeyForReader( @NonNull PublicKey authenticationPublicKey, @@ -1970,14 +2178,38 @@ UUID uuidFromBytes(@NonNull byte[] bytes) { case SecureArea.EC_CURVE_BRAINPOOLP512R1: stdName = "brainpoolP512r1"; break; + case SecureArea.EC_CURVE_X25519: + stdName = "X25519"; + break; + case SecureArea.EC_CURVE_ED25519: + stdName = "Ed25519"; + break; + case SecureArea.EC_CURVE_X448: + stdName = "X448"; + break; + case SecureArea.EC_CURVE_ED448: + stdName = "Ed448"; + break; default: throw new IllegalArgumentException("curve provided is not one of the supported curves"); } try { - KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); - ECGenParameterSpec ecSpec = new ECGenParameterSpec(stdName); - kpg.initialize(ecSpec); + KeyPairGenerator kpg; + if (stdName.equals("X25519")) { + kpg = KeyPairGenerator.getInstance("X25519", new BouncyCastleProvider()); + kpg.initialize(new XDHParameterSpec(XDHParameterSpec.X25519)); + } else if (stdName.equals("Ed25519")) { + kpg = KeyPairGenerator.getInstance("Ed25519", new BouncyCastleProvider()); + } else if (stdName.equals("X448")) { + kpg = KeyPairGenerator.getInstance("X448", new BouncyCastleProvider()); + kpg.initialize(new XDHParameterSpec(XDHParameterSpec.X448)); + } else if (stdName.equals("Ed448")) { + kpg = KeyPairGenerator.getInstance("Ed448", new BouncyCastleProvider()); + } else { + kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider()); + kpg.initialize(new ECGenParameterSpec(stdName)); + } KeyPair keyPair = kpg.generateKeyPair(); return keyPair; } catch (NoSuchAlgorithmException diff --git a/identity/src/main/java/com/android/identity/mdoc/sessionencryption/SessionEncryption.java b/identity/src/main/java/com/android/identity/mdoc/sessionencryption/SessionEncryption.java index fccaf23f7..a6e67d7c0 100644 --- a/identity/src/main/java/com/android/identity/mdoc/sessionencryption/SessionEncryption.java +++ b/identity/src/main/java/com/android/identity/mdoc/sessionencryption/SessionEncryption.java @@ -23,6 +23,9 @@ import androidx.annotation.Nullable; import com.android.identity.internal.Util; +import com.android.identity.securearea.SecureArea; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.io.ByteArrayInputStream; import java.lang.annotation.Retention; @@ -110,7 +113,18 @@ public SessionEncryption(@Role int role, SecretKeySpec deviceSK, readerSK; try { - KeyAgreement ka = KeyAgreement.getInstance("ECDH"); + KeyAgreement ka; + switch (Util.getCurve(remotePublicKey)) { + case SecureArea.EC_CURVE_X25519: + ka = KeyAgreement.getInstance("X25519", new BouncyCastleProvider()); + break; + case SecureArea.EC_CURVE_X448: + ka = KeyAgreement.getInstance("X448", new BouncyCastleProvider()); + break; + default: + ka = KeyAgreement.getInstance("ECDH", new BouncyCastleProvider()); + break; + } ka.init(mESelfKeyPrivate); ka.doPhase(remotePublicKey, true); byte[] sharedSecret = ka.generateSecret(); diff --git a/identity/src/main/java/com/android/identity/util/Constants.java b/identity/src/main/java/com/android/identity/util/Constants.java index 9c967ea26..04885cbd0 100644 --- a/identity/src/main/java/com/android/identity/util/Constants.java +++ b/identity/src/main/java/com/android/identity/util/Constants.java @@ -100,32 +100,6 @@ public class Constants { */ public static final long SESSION_DATA_STATUS_SESSION_TERMINATION = 20; - /** The curve identifier for P-256 */ - public static final int EC_CURVE_P256 = 1; - - /** The curve identifier for P-384 */ - public static final int EC_CURVE_P384 = 2; - - /** The curve identifier for P-521 */ - public static final int EC_CURVE_P521 = 3; - - /** The curve identifier for brainpoolP256r1 */ - public static final int EC_CURVE_BRAINPOOLP256R1 = -65537; - - /** The curve identifier for brainpoolP320r1 */ - public static final int EC_CURVE_BRAINPOOLP320R1 = -65538; - - /** The curve identifier for brainpoolP384r1 */ - public static final int EC_CURVE_BRAINPOOLP384R1 = -65539; - - /** The curve identifier for brainpoolP512r1 */ - public static final int EC_CURVE_BRAINPOOLP512R1 = -65540; - - /** The curve identifier for Ed25519 */ - public static final int EC_CURVE_ED25519 = 6; - - /** The curve identifier for Ed448 */ - public static final int EC_CURVE_ED448 = 7; /** * The status code used for session data exchange. diff --git a/identity/src/test/java/com/android/identity/internal/UtilTest.java b/identity/src/test/java/com/android/identity/internal/UtilTest.java index d29bfc3f3..a23b0b1ef 100644 --- a/identity/src/test/java/com/android/identity/internal/UtilTest.java +++ b/identity/src/test/java/com/android/identity/internal/UtilTest.java @@ -25,11 +25,13 @@ import com.android.identity.TestUtilities; +import com.android.identity.securearea.SecureArea; import com.android.identity.util.Timestamp; import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECParameterSpec; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -39,6 +41,7 @@ import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.PublicKey; import java.security.Security; import java.security.Signature; import java.security.cert.X509Certificate; @@ -998,4 +1001,68 @@ public void testMdocVersionCompare() { assertTrue(Util.mdocVersionCompare("1.0", "1.1") < 0); assertTrue(Util.mdocVersionCompare("1.1", "1.0") > 0); } + + // Checks that our COSE_Key encode and decode functions work as expected + // for an EC key of a given curve. + static private void testCoseKey(@SecureArea.EcCurve int curve) { + KeyPair kp = Util.createEphemeralKeyPair(curve); + DataItem coseKey = Util.cborBuildCoseKey(kp.getPublic()); + PublicKey decoded = Util.coseKeyDecode(coseKey); + Assert.assertEquals(kp.getPublic(), decoded); + } + + @Test + public void testCoseKeyP256() { + testCoseKey(SecureArea.EC_CURVE_P256); + } + + @Test + public void testCoseKeyP384() { + testCoseKey(SecureArea.EC_CURVE_P384); + } + + @Test + public void testCoseKeyP521() { + testCoseKey(SecureArea.EC_CURVE_P521); + } + + @Test + public void testCoseKeyBrainpool256() { + testCoseKey(SecureArea.EC_CURVE_BRAINPOOLP256R1); + } + + @Test + public void testCoseKeyBrainpool320() { + testCoseKey(SecureArea.EC_CURVE_BRAINPOOLP320R1); + } + + @Test + public void testCoseKeyBrainpool384() { + testCoseKey(SecureArea.EC_CURVE_BRAINPOOLP384R1); + } + + @Test + public void testCoseKeyBrainpool521() { + testCoseKey(SecureArea.EC_CURVE_BRAINPOOLP512R1); + } + + @Test + public void testCoseKeyX25519() { + testCoseKey(SecureArea.EC_CURVE_X25519); + } + + @Test + public void testCoseKeyX448() { + testCoseKey(SecureArea.EC_CURVE_X448); + } + + @Test + public void testCoseKeyEd25519() { + testCoseKey(SecureArea.EC_CURVE_ED25519); + } + + @Test + public void testCoseKeyEd448() { + testCoseKey(SecureArea.EC_CURVE_ED448); + } } diff --git a/identity/src/test/java/com/android/identity/mdoc/sessionencryption/SessionEncryptionTest.java b/identity/src/test/java/com/android/identity/mdoc/sessionencryption/SessionEncryptionTest.java index d57df52a7..95e3529ce 100644 --- a/identity/src/test/java/com/android/identity/mdoc/sessionencryption/SessionEncryptionTest.java +++ b/identity/src/test/java/com/android/identity/mdoc/sessionencryption/SessionEncryptionTest.java @@ -221,49 +221,46 @@ private void testCurve(@SecureArea.EcCurve int curve) { @Test public void testP256() { - testCurve(Constants.EC_CURVE_P256); + testCurve(SecureArea.EC_CURVE_P256); } @Test public void testP384() { - testCurve(Constants.EC_CURVE_P384); + testCurve(SecureArea.EC_CURVE_P384); } @Test public void testP521() { - testCurve(Constants.EC_CURVE_P521); + testCurve(SecureArea.EC_CURVE_P521); } @Test public void testBrainpool256() { - testCurve(Constants.EC_CURVE_BRAINPOOLP256R1); + testCurve(SecureArea.EC_CURVE_BRAINPOOLP256R1); } @Test public void testBrainpool320() { - testCurve(Constants.EC_CURVE_BRAINPOOLP320R1); + testCurve(SecureArea.EC_CURVE_BRAINPOOLP320R1); } @Test public void testBrainpool384() { - testCurve(Constants.EC_CURVE_BRAINPOOLP384R1); + testCurve(SecureArea.EC_CURVE_BRAINPOOLP384R1); } @Test public void testBrainpool521() { - testCurve(Constants.EC_CURVE_BRAINPOOLP512R1); + testCurve(SecureArea.EC_CURVE_BRAINPOOLP512R1); } - // TODO: remove @Ignore once ED25519 / ED448 is no longer buggy - @Ignore @Test - public void testEd25519() { - testCurve(Constants.EC_CURVE_ED25519); + public void testX25519() { + testCurve(SecureArea.EC_CURVE_X25519); } - @Ignore @Test - public void testEd448() { - testCurve(Constants.EC_CURVE_ED448); + public void testX448() { + testCurve(SecureArea.EC_CURVE_X448); } }