fix(lotw): support more PBES2 variants, classify import errors for users

- Pkcs12Reader: AES-192/AES-128/DES-EDE3-CBC ciphers, PBKDF2
  SHA1/SHA256/SHA384/SHA512 PRFs, keyLength defaulting per cipher,
  error messages reduced to algorithm names
- LoTWKeyMaterial: map BadPadding -> wrong password, IllegalState
  (unsupported algorithm) -> format error with algorithm name,
  IllegalArgumentException -> invalid file
- Import dialog: user-facing messages with algorithm name embedded,
  no more raw OpenSSL/BoringSSL error strings
- Tests: AES-192, DES-EDE3 fixtures, wrong-password BadPadding check
This commit is contained in:
atsunatsu committed 2026-09-27 01:20:07 +08:00
1 parent 474762cbd3
commit 52b733a450
12 files changed
+158 -28

No files matched your search

@@ -92,6 +92,7 @@ fun LoTWUploadConfigDialog(
stationMeta: LoTWStationMeta?,
busy: Boolean,
error: LoTWUploadError?,
errorDetail: String = "",
onDismiss: () -> Unit,
onImport: (ByteArray, CharArray) -> Unit,
onRemove: () -> Unit,
@@ -162,15 +163,20 @@ fun LoTWUploadConfigDialog(
)
error?.let {
Text(
text = stringResource(
when (it) {
LoTWUploadError.PASSWORD -> R.string.prefs_lotw_upload_error_password
LoTWUploadError.EXPIRED -> R.string.prefs_lotw_upload_error_expired
LoTWUploadError.INVALID_FILE -> R.string.prefs_lotw_upload_error_invalid
LoTWUploadError.FORMAT -> R.string.prefs_lotw_upload_error_format
LoTWUploadError.UNKNOWN -> R.string.prefs_lotw_upload_error_unknown
}
),
text = when {
it == LoTWUploadError.FORMAT && errorDetail.isNotBlank() ->
stringResource(R.string.prefs_lotw_upload_error_format_alg, errorDetail)
it == LoTWUploadError.FORMAT -> stringResource(R.string.prefs_lotw_upload_error_format)
else -> stringResource(
when (it) {
LoTWUploadError.PASSWORD -> R.string.prefs_lotw_upload_error_password
LoTWUploadError.EXPIRED -> R.string.prefs_lotw_upload_error_expired
LoTWUploadError.INVALID_FILE -> R.string.prefs_lotw_upload_error_invalid
LoTWUploadError.UNKNOWN -> R.string.prefs_lotw_upload_error_unknown
LoTWUploadError.FORMAT -> R.string.prefs_lotw_upload_error_format
}
)
},
color = MaterialTheme.colorScheme.error,
fontSize = 12.sp
)
@@ -282,6 +282,7 @@ private fun SettingsScreen(uiState: SettingsState, onAction: (SettingsAction) ->
stationMeta = uiState.lotwStationMeta,
busy = uiState.lotwUploadBusy,
error = uiState.lotwUploadError,
errorDetail = uiState.lotwUploadErrorDetail,
onDismiss = { dialogs.lotwUpload = false },
onImport = { bytes, password -> onAction(SettingsAction.ImportLoTWCertificate(bytes, password)) },
onRemove = { onAction(SettingsAction.RemoveLoTWCertificate) },
@@ -79,6 +79,8 @@ data class SettingsState(
val lotwUploadBusy: Boolean = false,
/** Last certificate import outcome; shown inside the upload config dialog. */
val lotwUploadError: LoTWUploadError? = null,
/** Parser detail for FORMAT errors (e.g. the unsupported algorithm name). */
val lotwUploadErrorDetail: String = "",
/** One-click logbook upload: prepared preview awaiting confirmation. */
val logbookPreview: com.rtbishop.look4sat.core.domain.repository.LoTWUploadPreview? = null,
val logbookUploadBusy: Boolean = false,
@@ -423,9 +423,9 @@ class SettingsViewModel(
com.rtbishop.look4sat.core.domain.repository.LoTWProblem.CERTIFICATE_FORMAT -> LoTWUploadError.FORMAT
else -> LoTWUploadError.INVALID_FILE
}
_uiState.update { it.copy(lotwUploadBusy = false, lotwUploadError = error) }
_uiState.update { it.copy(lotwUploadBusy = false, lotwUploadError = error, lotwUploadErrorDetail = e.detail) }
} catch (_: Exception) {
_uiState.update { it.copy(lotwUploadBusy = false, lotwUploadError = LoTWUploadError.UNKNOWN) }
_uiState.update { it.copy(lotwUploadBusy = false, lotwUploadError = LoTWUploadError.UNKNOWN, lotwUploadErrorDetail = "") }
}
}
}