Skip to content

Commit c184569

Browse files
committed
Skip credential decryption when the IV is missing
decryptAPIRestCredentials bailed only when the ciphertext columns were empty; a row with ciphertext but a blank IV would reach decrypt(ciphertext, "") and throw on read. Also short-circuit when either IV is empty, treating the malformed row as having no credentials.
1 parent 5445bc5 commit c184569

2 files changed

Lines changed: 25 additions & 3 deletions

File tree

libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/SiteSqlUtils.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -708,14 +708,17 @@ class SiteSqlUtils
708708
return this.map { it.decryptAPIRestCredentials() }
709709
}
710710

711-
@Suppress("ReturnCount")
711+
@Suppress("ReturnCount", "ComplexCondition")
712712
private fun SiteModel.decryptAPIRestCredentials(): SiteModel {
713713
// If already decrypted, do nothing
714714
if (!apiRestUsernamePlain.isNullOrEmpty() && !apiRestPasswordPlain.isNullOrEmpty()) {
715715
return this
716716
}
717-
// If the encrypted credentials are empty, there's nothing to decrypt
718-
if (apiRestUsernameEncrypted.isNullOrEmpty() || apiRestPasswordEncrypted.isNullOrEmpty()) {
717+
// If the encrypted credentials — or the IVs required to decrypt them — are empty, there's nothing to
718+
// safely decrypt. The ciphertext/IV pairs are always written together, so a row missing any one is
719+
// treated as having no decryptable credentials rather than risking a decrypt failure on a blank IV.
720+
if (apiRestUsernameEncrypted.isNullOrEmpty() || apiRestPasswordEncrypted.isNullOrEmpty() ||
721+
apiRestUsernameIV.isNullOrEmpty() || apiRestPasswordIV.isNullOrEmpty()) {
719722
return this
720723
}
721724
apiRestUsernamePlain = encryptionUtils.decrypt(apiRestUsernameEncrypted, apiRestUsernameIV)

libs/fluxc/src/test/java/org/wordpress/android/fluxc/persistence/SiteSqlUtilsTest.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,25 @@ class SiteSqlUtilsTest {
191191
assertThat(stored.apiRestPasswordIV).isEmpty()
192192
}
193193

194+
@Test
195+
fun `reading a site with credential ciphertext but a blank IV does not attempt decryption`() {
196+
// A row carrying credential ciphertext without its IV is malformed — decrypting it would fail. The
197+
// read path must treat it as having no decryptable credentials instead of throwing. Both ciphertexts
198+
// are present here so only the blank-IV guard (not the empty-ciphertext one) can short-circuit.
199+
WellSql.insert(SiteModel().apply {
200+
url = "https://example.test"
201+
apiRestUsernameEncrypted = "enc_user"
202+
apiRestUsernameIV = ""
203+
apiRestPasswordEncrypted = "enc_pass"
204+
apiRestPasswordIV = "iv_pass"
205+
}).execute()
206+
207+
val stored = siteSqlUtils.getSites().single()
208+
209+
assertThat(stored.apiRestUsernamePlain).isNullOrEmpty()
210+
assertThat(stored.apiRestPasswordPlain).isNullOrEmpty()
211+
}
212+
194213
@Test
195214
fun `updateXmlRpcUrl writes the column and leaves other fields alone`() {
196215
WellSql.insert(SiteModel().apply {

0 commit comments

Comments
 (0)