Generate SSD pinned TLS factory from typed Jerboa

ober

389657b27b8496b79783e8a50aab012d26d2a686

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index c14ca73..54730f5 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -15575,16 +15575,24 @@
                       (java nio file StandardOpenOption)
                       (java security MessageDigest)
                       (java security PublicKey)
+                      (java security KeyStore)
+                      (java security SecureRandom)
                       (java time Instant)
                       (java time OffsetDateTime)
                       (java time format DateTimeFormatter)
                       (java security cert Certificate)
+                      (java security cert CertificateException)
                       (java util zip ZipEntry)
                       (java util zip ZipInputStream)
                       (java util zip ZipOutputStream)
                       (javax net ssl HttpsURLConnection)
+                      (javax net ssl KeyManager)
+                      (javax net ssl SSLContext)
                       (javax net ssl SSLPeerUnverifiedException)
                       (javax net ssl SSLSocketFactory)
+                      (javax net ssl TrustManager)
+                      (javax net ssl TrustManagerFactory)
+                      (javax net ssl X509TrustManager)
                       (androidx documentfile provider DocumentFile)
                       (org json JSONArray)
                       (org json JSONObject))
@@ -15600,6 +15608,7 @@
                 truthStoreLoadRemoteConfigLocal
                 truthStoreRemoteUrlLocal
                 truthStoreOpenPinnedConnectionLocal
+                truthStorePinnedSocketFactoryLocal
                 truthStoreInvalidateTruthIndexLocal
                 truthStoreTruthIndexEntriesLocal
                 truthStoreTruthIndexEntryLocal
@@ -15636,6 +15645,7 @@
         (type BufferedInputStream)
         (type BufferedOutputStream)
         (type Certificate)
+        (type CertificateException)
         (type Char)
         (type ContentResolver)
         (type Context)
@@ -15650,6 +15660,8 @@
         (type LinkOption)
         (type LimitedInputStream)
         (type HttpsURLConnection)
+        (type KeyStore)
+        (type KeyManager)
         (type MessageDigest)
         (type Number)
         (type OffsetDateTime)
@@ -15657,18 +15669,23 @@
         (type Path)
         (type PublicKey)
         (type RemoteConfig)
+        (type SSLContext)
         (type SSLPeerUnverifiedException)
         (type SSLSocketFactory)
+        (type SecureRandom)
         (type SsdSession)
         (type SharedPreferences)
         (type StorageStats)
         (type StandardCopyOption)
         (type StandardOpenOption)
+        (type TrustManager)
+        (type TrustManagerFactory)
         (type TruthIndexEntry)
         (type TruthStore)
         (type Uri)
         (type URL)
         (type URLConnection)
+        (type X509TrustManager)
         (type JSONArray)
         (type JSONObject)
         (type ZipEntry)
@@ -16150,6 +16167,44 @@
         (extern (truthStoreSslPeerUnverifiedException
                   (message : String)) : SSLPeerUnverifiedException
           (kotlin-call SSLPeerUnverifiedException))
+        (extern (truthStoreCertificateException
+                  (message : String)) : CertificateException
+          (kotlin-call CertificateException))
+        (extern (truthStoreTrustManagerFactoryDefaultAlgorithm) : String
+          (kotlin-call TrustManagerFactory getDefaultAlgorithm))
+        (extern (truthStoreTrustManagerFactoryGetInstance
+                  (algorithm : String)) : TrustManagerFactory
+          (kotlin-call TrustManagerFactory getInstance))
+        (extern (truthStoreTrustManagerFactoryInit
+                  (factory : TrustManagerFactory)
+                  (store : (Nullable KeyStore))) : Unit
+          (kotlin-member-call init))
+        (extern (truthStoreNullAsKeyStore
+                  (value : (Nullable Any))) : (Nullable KeyStore)
+          (kotlin-cast KeyStore))
+        (extern (truthStoreTrustManagers
+                  (factory : TrustManagerFactory)) : (Array TrustManager)
+          (kotlin-member-get trustManagers))
+        (extern (truthStoreTrustManagerAsX509
+                  (manager : TrustManager)) : (Nullable X509TrustManager)
+          (kotlin-safe-cast X509TrustManager))
+        (extern (truthStoreTrustManagerArrayOfPinned
+                  (manager : X509TrustManager)) : (Array TrustManager)
+          (kotlin-call arrayOf))
+        (extern (truthStoreSslContextGetInstance
+                  (protocol : String)) : SSLContext
+          (kotlin-call SSLContext getInstance))
+        (extern (truthStoreSecureRandom) : SecureRandom
+          (kotlin-call SecureRandom))
+        (extern (truthStoreSslContextInit
+                  (context : SSLContext)
+                  (keyManagers : (Nullable (Array KeyManager)))
+                  (trustManagers : (Array TrustManager))
+                  (random : SecureRandom)) : Unit
+          (kotlin-member-call init))
+        (extern (truthStoreSslContextSocketFactory
+                  (context : SSLContext)) : SSLSocketFactory
+          (kotlin-member-get socketFactory))
         (extern (truthStoreMaxZipEntryBytes) : Int
           (kotlin-value MAX_ZIP_ENTRY_BYTES))
         (extern (truthStoreMaxHttpTextBytes) : Int
@@ -16709,6 +16764,74 @@
                   connection
                   config)
                 connection))))
+        (def (truthStorePinnedExpectedPinLocal
+               (config : RemoteConfig)) : Bytes
+          (if (remotePinBytesPresent
+                (RemoteConfig-spkiSha256 config))
+            (nullable-get (RemoteConfig-spkiSha256 config))
+            (throw
+              (truthStoreCertificateException
+                "SPKI pin is required for pinned TLS")
+              Bytes)))
+        (def (truthStoreX509TrustManagerSearchStep
+               (search : (Pair Int32 (Nullable X509TrustManager)))
+               (candidate : (Nullable X509TrustManager))) : (Pair Int32 (Nullable X509TrustManager))
+          (if (nullable-null? candidate)
+            search
+            (pair
+              (+ (pair-first search) (int32 1))
+              candidate)))
+        (def (truthStoreSingleX509TrustManagerLocal
+               (managers : (Array TrustManager))) : X509TrustManager
+          (let ((search
+                  (for/fold ((state
+                               (pair
+                                 (int32 0)
+                                 (nullable-none X509TrustManager))))
+                            ((i (in-range
+                                  (int32 0)
+                                  (list-size managers))))
+                    (truthStoreX509TrustManagerSearchStep
+                      state
+                      (truthStoreTrustManagerAsX509
+                        (list-ref managers i))))))
+            (if (= (pair-first search) (int32 1))
+              (nullable-get (pair-second search))
+              (throw
+                (truthStoreCertificateException
+                  "Expected exactly one platform X509 trust manager")
+                X509TrustManager))))
+        (def (truthStoreDefaultTrustManagerFactoryLocal) : TrustManagerFactory
+          (let ((factory
+                  (truthStoreTrustManagerFactoryGetInstance
+                    (truthStoreTrustManagerFactoryDefaultAlgorithm))))
+            (begin
+              (truthStoreTrustManagerFactoryInit
+                factory
+                (truthStoreNullAsKeyStore
+                  (nullable-none Any)))
+              factory)))
+        (def (truthStorePinnedSocketFactoryLocal
+               (config : RemoteConfig)) : SSLSocketFactory
+          (let ((expectedPin
+                  (truthStorePinnedExpectedPinLocal config)))
+            (let ((platform
+                    (truthStoreSingleX509TrustManagerLocal
+                      (truthStoreTrustManagers
+                        (truthStoreDefaultTrustManagerFactoryLocal)))))
+              (let ((pinned
+                      (truthStoreMakePinnedTrustManager
+                        platform
+                        expectedPin)))
+                (let ((context
+                        (truthStoreSslContextGetInstance "TLS")))
+                  (begin
+                    (truthStoreSslContextInit
+                      context
+                      (nullable-none (Array KeyManager))
+                      (truthStoreTrustManagerArrayOfPinned pinned)
+                      (truthStoreSecureRandom))
+                    (truthStoreSslContextSocketFactory context)))))))
         (def (truthStoreReadResponseTextLocal
                (connection : HttpsURLConnection)) : String
           (begin
@@ -18533,17 +18656,8 @@
        "    ): HttpsURLConnection ="
        "        truthStoreOpenPinnedConnectionLocal(this, relative, method, outputBytes, contentType, acceptGzip)"
        ""
-       "    internal fun pinnedSocketFactory(config: RemoteConfig): SSLSocketFactory {"
-       "        if (!remotePinBytesPresent(config.spkiSha256)) throw CertificateException(\"SPKI pin is required for pinned TLS\")"
-       "        val expectedPin = checkNotNull(config.spkiSha256)"
-       "        val managerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())"
-       "        managerFactory.init(null as KeyStore?)"
-       "        val platform = managerFactory.trustManagers.filterIsInstance<X509TrustManager>().single()"
-       "        val pinned = truthStoreMakePinnedTrustManager(platform, expectedPin)"
-       "        return SSLContext.getInstance(\"TLS\").apply {"
-       "            init(null, arrayOf<TrustManager>(pinned), SecureRandom())"
-       "        }.socketFactory"
-       "    }"
+       "    internal fun pinnedSocketFactory(config: RemoteConfig): SSLSocketFactory ="
+       "        truthStorePinnedSocketFactoryLocal(config)"
        ""
        "    fun pullRemoteTruth(session: SsdSession): Int ="
        "        truthStorePullRemoteTruthLocal(this, session)"