From ed61bfc39dedea195f1e61d8f3fc77c07a195729 Mon Sep 17 00:00:00 2001 From: Sandro Montanari Date: Tue, 21 Jan 2025 17:37:35 +0000 Subject: [PATCH] Add support for Base64 encoded signature To simplify debugging, we are planning to publish the log list signature as a Base64 encoded string. It might take a while for the changes to the signature publishing pipeline to reach prod, and we do not know exactly when that will happen. To avoid accidental crashes, I added a fallback path to the verifier code that reverts to use the signature as raw (not Base64 encoded) bytes. I will remove the fallback path once the signature publishing changes are finalized. Bug: 374719543 Test: atest NetworkSecurityUnitTests Change-Id: Ia785b554b840940dc81b47ac9bc2121c0b410ab0 --- .../server/net/ct/SignatureVerifier.java | 23 ++++++++++++++++--- ...CertificateTransparencyDownloaderTest.java | 18 +++++++-------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java b/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java index 96488fc32e..67ef63fb4e 100644 --- a/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java +++ b/networksecurity/service/src/com/android/server/net/ct/SignatureVerifier.java @@ -21,6 +21,7 @@ import android.content.ContentResolver; import android.content.Context; import android.net.Uri; import android.os.Build; +import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -62,10 +63,15 @@ public class SignatureVerifier { } void setPublicKey(String publicKey) throws GeneralSecurityException { + byte[] decodedPublicKey = null; + try { + decodedPublicKey = Base64.getDecoder().decode(publicKey); + } catch (IllegalArgumentException e) { + throw new GeneralSecurityException("Invalid public key base64 encoding", e); + } setPublicKey( KeyFactory.getInstance("RSA") - .generatePublic( - new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey)))); + .generatePublic(new X509EncodedKeySpec(decodedPublicKey))); } @VisibleForTesting @@ -82,10 +88,21 @@ public class SignatureVerifier { verifier.initVerify(mPublicKey.get()); ContentResolver contentResolver = mContext.getContentResolver(); + boolean success = false; try (InputStream fileStream = contentResolver.openInputStream(file); InputStream signatureStream = contentResolver.openInputStream(signature)) { verifier.update(fileStream.readAllBytes()); - return verifier.verify(signatureStream.readAllBytes()); + + byte[] signatureBytes = signatureStream.readAllBytes(); + try { + success = verifier.verify(Base64.getDecoder().decode(signatureBytes)); + } catch (IllegalArgumentException e) { + Log.w("CertificateTransparencyDownloader", "Invalid signature base64 encoding", e); + // TODO: remove the fallback once the signature base64 is published + Log.i("CertificateTransparencyDownloader", "Signature verification as raw bytes"); + success = verifier.verify(signatureBytes); + } } + return success; } } diff --git a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java index 2f57fc92ac..78e7272309 100644 --- a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java +++ b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java @@ -235,8 +235,8 @@ public class CertificateTransparencyDownloaderTest { Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0)) .isEqualTo(1); verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt()); - verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus( - anyInt(), anyInt()); + verify(mLogger, never()) + .logCTLogListUpdateFailedEventWithDownloadStatus(anyInt(), anyInt()); } @Test @@ -309,8 +309,8 @@ public class CertificateTransparencyDownloaderTest { Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0)) .isEqualTo(1); verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt()); - verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus( - anyInt(), anyInt()); + verify(mLogger, never()) + .logCTLogListUpdateFailedEventWithDownloadStatus(anyInt(), anyInt()); } @Test @@ -387,8 +387,8 @@ public class CertificateTransparencyDownloaderTest { Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0)) .isEqualTo(1); verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt()); - verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus( - anyInt(), anyInt()); + verify(mLogger, never()) + .logCTLogListUpdateFailedEventWithDownloadStatus(anyInt(), anyInt()); } @Test @@ -606,8 +606,8 @@ public class CertificateTransparencyDownloaderTest { Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0)) .isEqualTo(1); verify(mLogger, never()).logCTLogListUpdateFailedEvent(anyInt(), anyInt()); - verify(mLogger, never()).logCTLogListUpdateFailedEventWithDownloadStatus( - anyInt(), anyInt()); + verify(mLogger, never()) + .logCTLogListUpdateFailedEventWithDownloadStatus(anyInt(), anyInt()); } @Test @@ -793,7 +793,7 @@ public class CertificateTransparencyDownloaderTest { try (InputStream fileStream = new FileInputStream(file); OutputStream outputStream = new FileOutputStream(signatureFile)) { signer.update(fileStream.readAllBytes()); - outputStream.write(signer.sign()); + outputStream.write(Base64.getEncoder().encode(signer.sign())); } return signatureFile;