Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make mdoc Session Encryption work with all nine KA curves. #384

Merged
merged 1 commit into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -46,8 +49,7 @@ class MainActivity : AppCompatActivity() {
setupDrawerLayout()
setupNfc()
onNewIntent(intent)

Security.addProvider(BouncyCastleProvider())
Logger.setDebugEnabled(PreferencesHelper.isDebugLoggingEnabled())
}

private fun setupNfc() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(
Expand Down Expand Up @@ -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 }
Expand All @@ -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 }
Expand Down Expand Up @@ -246,7 +246,7 @@ private fun SettingsScreenPreview() {
modifier = Modifier.fillMaxSize(),
screenState = SettingsScreenState(),
onAutoCloseChanged = {},
onEphemeralKeyCurveChanged = {},
onSessionEncryptionCurveChanged = {},
onUseStaticHandoverChanged = {},
onUseL2CAPChanged = {},
onBLEServiceCacheChanged = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {

Expand All @@ -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
Expand All @@ -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")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -103,6 +104,7 @@ object PreferencesHelper {

fun setDebugLoggingEnabled(enabled: Boolean) {
sharedPreferences.edit { putBoolean(DEBUG_LOG, enabled) }
Logger.setDebugEnabled(enabled)
}

@EcCurve
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class VerifierApp : Application() {
Logger.setLogPrinter(AndroidLogPrinter())
DynamicColors.applyToActivitiesIfAvailable(this)
userPreferencesInstance = userPreferences
Logger.setDebugEnabled(userPreferences.isDebugLoggingEnabled())
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<DeviceResponseParser.Document>): String {
// Create the trustManager to validate the DS Certificate against the list of known
// certificates in the app
Expand All @@ -177,6 +196,7 @@ class ShowDocumentFragment : Fragment() {

sb.append("Number of documents returned: <b>${documents.size}</b><br>")
sb.append("Address: <b>" + transferManager.mdocConnectionMethod + "</b><br>")
sb.append("Session encryption curve: <b>" + curveNameFor(transferManager.getMdocSessionEncryptionCurve()) + "</b><br>")
sb.append("<br>")
for (doc in documents) {
// Get primary color from theme to use in the HTML formatted document.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -77,6 +78,7 @@ class UserPreferences(

fun setDebugLoggingEnabled(enabled: Boolean) {
preferences.edit { putBoolean(LOG_ENABLED, enabled) }
Logger.setDebugEnabled(enabled)
}

fun getReaderAuthentication(): Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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);
}

/**
Expand Down
Loading
Loading