@umutcansu/react-native-pinvault 2.8.1 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +3 -3
- package/RNPinVault.podspec +1 -1
- package/android/build.gradle +1 -1
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/ConfigParser.kt +28 -5
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/NativeSecurity.kt +21 -5
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/PinnedFetch.kt +122 -13
- package/ios/Core/ConfigParser.swift +37 -6
- package/ios/Core/NativeSecurity.swift +26 -5
- package/ios/Core/PinnedFetch.swift +68 -7
- package/lib/typescript/src/types.d.ts +6 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/types.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.9.0 — 2026-11-10
|
|
4
|
+
|
|
5
|
+
### Security
|
|
6
|
+
|
|
7
|
+
- **Release builds take `requiredSignatures`, `recoveryPublicKeys` and
|
|
8
|
+
`requiredRecoverySignatures` only from the native security file.** They are
|
|
9
|
+
part of the library's trust-anchor fingerprint, and a changed fingerprint
|
|
10
|
+
resets the replay watermarks; a value JS could add and drop again reset
|
|
11
|
+
them at will. A value the file does not declare is refused
|
|
12
|
+
(`E_INVALID_CONFIG`) unless it is the default (1, no recovery keys). Copy
|
|
13
|
+
what JS passes into `pinvault_security.json`. Debug builds are unchanged.
|
|
14
|
+
- **An endpoint that is absolute in another spelling is refused**: whitespace
|
|
15
|
+
around the value (`" //host/x"`), a backslash anywhere and a bare `scheme:`
|
|
16
|
+
prefix, on Android and iOS.
|
|
17
|
+
- **A vault file's `endpoint` follows that rule too** and may not contain a
|
|
18
|
+
`..` path segment (plain or `%2e`); it was checked for `://` and a leading
|
|
19
|
+
slash only.
|
|
20
|
+
- **Breaking — release builds take `serverScope` and `clientCaPins` only from
|
|
21
|
+
the native security file.** `serverScope` names the library's config store;
|
|
22
|
+
a value JS could set where the file named none moved the store to a new
|
|
23
|
+
namespace without replay watermarks. A value the file does not declare is
|
|
24
|
+
refused (`E_INVALID_CONFIG`). Copy what JS passes into
|
|
25
|
+
`pinvault_security.json`. Debug builds are unchanged.
|
|
26
|
+
- **`PinVault.fetch`: a redirect to another host or port takes only `Accept`,
|
|
27
|
+
`Accept-Language` and `User-Agent` along, and never a body** (a 307/308 that
|
|
28
|
+
would repeat one is returned as the answer). On Android the plugin now
|
|
29
|
+
follows redirects itself (OkHttp kept every header but `Authorization`); on
|
|
30
|
+
iOS the library does.
|
|
31
|
+
- `PinVault.fetch` on iOS: `timeoutMs` is one limit for the whole exchange,
|
|
32
|
+
redirects included, as on Android.
|
|
33
|
+
- `ios.requireUnlockedDeviceAllowFallback`: the library's
|
|
34
|
+
`requireUnlockedDevice()` no longer falls back silently on iOS (it fails
|
|
35
|
+
with `UnlockedDeviceKeyRequiredException`); this is the opt-out, the twin of
|
|
36
|
+
`android.requireUnlockedDeviceAllowFallback`.
|
|
37
|
+
- Through the library: on iOS, numbers a server sends (attestation intervals,
|
|
38
|
+
a certificate's version field, a vault file's version header) can no longer
|
|
39
|
+
overflow and end the app; a blank vault file `signaturePublicKey` no longer
|
|
40
|
+
turns that file's verification off.
|
|
41
|
+
- Depends on PinVault 2.9.0 (Swift package tag `v2.9.0`).
|
|
42
|
+
|
|
43
|
+
## 2.8.2 — 2026-10-21
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- No change to the bridge. Released alongside PinVault 2.8.2, whose
|
|
48
|
+
reference server and dashboard say whose key the backup pin of a host
|
|
49
|
+
added from a URL trusts (the issuing CA's) and take an optional second
|
|
50
|
+
pin of the site instead. Depends on PinVault 2.8.2 (Swift package tag `v2.8.2`).
|
|
51
|
+
|
|
3
52
|
## 2.8.1 — 2026-10-15
|
|
4
53
|
|
|
5
54
|
### Changed
|
package/README.md
CHANGED
|
@@ -29,9 +29,9 @@ npm install @umutcansu/react-native-pinvault
|
|
|
29
29
|
cd ios && pod install
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
The native libraries come with it, at the package's own version: npm 2.
|
|
33
|
-
uses `io.github.umutcansu:pinvault:2.
|
|
34
|
-
package at tag `v2.
|
|
32
|
+
The native libraries come with it, at the package's own version: npm 2.9.0
|
|
33
|
+
uses `io.github.umutcansu:pinvault:2.9.0` from Maven Central and the Swift
|
|
34
|
+
package at tag `v2.9.0`. The New Architecture must stay on (React Native's
|
|
35
35
|
default since 0.76).
|
|
36
36
|
|
|
37
37
|
**iOS.** The pod depends on the PinVault Swift package through React Native's
|
package/RNPinVault.podspec
CHANGED
|
@@ -4,7 +4,7 @@ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
|
4
4
|
|
|
5
5
|
# PinVault for iOS is a Swift package (Package.swift at the repository root,
|
|
6
6
|
# product "PinVault"). React Native's spm_dependency adds it to the Pods project:
|
|
7
|
-
# - consumers: the git URL at the tag of this package's version (v2.
|
|
7
|
+
# - consumers: the git URL at the tag of this package's version (v2.9.0);
|
|
8
8
|
# - development: PINVAULT_IOS_PACKAGE_PATH = a PinVault checkout (the sample's
|
|
9
9
|
# Podfile sets it to this repository), the counterpart of pinvault.localPath.
|
|
10
10
|
pinvault_url = "https://github.com/umutcansu/PinVault.git"
|
package/android/build.gradle
CHANGED
|
@@ -202,12 +202,38 @@ internal object ConfigParser {
|
|
|
202
202
|
*/
|
|
203
203
|
private fun relEndpoint(f: Fields, key: String, max: Int): String? {
|
|
204
204
|
val v = f.string(key, max) ?: return null
|
|
205
|
-
|
|
205
|
+
// The URL parser skips leading whitespace and reads `\` as `/`: " //host/x"
|
|
206
|
+
// and "https:\\host/x" passed a check that only looked at the first character.
|
|
207
|
+
if (v != v.trim() || v.contains('\\') || v.contains("://") || v.startsWith("/") || SCHEME_PREFIX.containsMatchIn(v)) {
|
|
206
208
|
throw BridgeInputException("${f.path}.$key: must be a relative path, not an absolute URL")
|
|
207
209
|
}
|
|
208
210
|
return v
|
|
209
211
|
}
|
|
210
212
|
|
|
213
|
+
/** `scheme:` at the start of a value: an absolute URL however its slashes are written. */
|
|
214
|
+
private val SCHEME_PREFIX = Regex("^[A-Za-z][A-Za-z0-9+.-]*:")
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* A vault file's endpoint: required, held to the same rule as the block's
|
|
218
|
+
* endpoints ([relEndpoint] — it used to be checked for `://` and a leading
|
|
219
|
+
* slash only, so " //host/x" or "https:\\host/x" sent the file request and
|
|
220
|
+
* the file's token to another host), and without a `..` segment: the
|
|
221
|
+
* endpoint is resolved against the block's URL, and `..` would climb out of
|
|
222
|
+
* the path the app's native side declared for that Config API.
|
|
223
|
+
*/
|
|
224
|
+
private fun vaultEndpoint(f: Fields): String {
|
|
225
|
+
val endpoint = relEndpoint(f, "endpoint", 512) ?: throw BridgeInputException("${f.path}.endpoint: required")
|
|
226
|
+
if (hasParentSegment(endpoint)) {
|
|
227
|
+
throw BridgeInputException("${f.path}.endpoint: must not contain a '..' path segment")
|
|
228
|
+
}
|
|
229
|
+
return endpoint
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** True when the path of [endpoint] has a `..` segment, written plainly or as `%2e` (URL parsers read both). */
|
|
233
|
+
internal fun hasParentSegment(endpoint: String): Boolean =
|
|
234
|
+
endpoint.substringBefore('?').substringBefore('#').split('/')
|
|
235
|
+
.any { it.replace("%2e", ".", ignoreCase = true) == ".." }
|
|
236
|
+
|
|
211
237
|
fun hostPin(p: Fields): HostPin {
|
|
212
238
|
val hostname = p.requireString("hostname", 255)
|
|
213
239
|
val pins = p.stringList("sha256", MAX_PINS_PER_HOST, 128) ?: throw BridgeInputException("${p.path}.sha256: required")
|
|
@@ -370,10 +396,7 @@ internal object ConfigParser {
|
|
|
370
396
|
if (!VAULT_KEY.matches(key) || key.all { it == '.' }) {
|
|
371
397
|
throw BridgeInputException("${f.path}.key: must match [A-Za-z0-9._-]{1,64} and not be only dots")
|
|
372
398
|
}
|
|
373
|
-
val endpoint = f
|
|
374
|
-
if (endpoint.contains("://") || endpoint.startsWith("/") || endpoint.startsWith("\\")) {
|
|
375
|
-
throw BridgeInputException("${f.path}.endpoint: must be a relative path, not an absolute URL")
|
|
376
|
-
}
|
|
399
|
+
val endpoint = vaultEndpoint(f)
|
|
377
400
|
val jsSignatureKey = f.string("signaturePublicKey", KEY_LENGTH, multiline = true)
|
|
378
401
|
val updateWithPins = f.bool("updateWithPins")
|
|
379
402
|
val storage = f.enum("storage", StorageStrategy.values())
|
|
@@ -349,15 +349,31 @@ internal object SecurityPolicy {
|
|
|
349
349
|
return nativeValue
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
+
// The signing keys' threshold, the recovery keys and theirs are part of the
|
|
353
|
+
// library's trust-anchor fingerprint, and a changed fingerprint resets the
|
|
354
|
+
// replay watermarks. In a release build they are the file's alone: a value
|
|
355
|
+
// the bundle could add and drop again would reset them at will (a default — 1,
|
|
356
|
+
// no recovery keys — changes nothing and is let through).
|
|
357
|
+
// `serverScope` names the library's config store: another scope is another
|
|
358
|
+
// namespace, which starts with no watermarks at all — a bundle that sets one
|
|
359
|
+
// makes the device accept a replayed old config. `clientCaPins` decide which
|
|
360
|
+
// CA may issue the device's identity. Both follow the same rule.
|
|
361
|
+
fun <T : Any> anchorOnly(key: String, nativeValue: T?, jsValue: T?, isDefault: (T) -> Boolean): T? {
|
|
362
|
+
if (release && nativeValue == null && jsValue != null && !isDefault(jsValue)) throw BridgeInputException(
|
|
363
|
+
"$path.$key: a release build takes it only from the app's native security file (${native.source}); " +
|
|
364
|
+
"declare $key for Config API '$id' there")
|
|
365
|
+
return jsValue
|
|
366
|
+
}
|
|
367
|
+
|
|
352
368
|
return SecurityFields(
|
|
353
369
|
bootstrapPins = pins,
|
|
354
370
|
oneKey = oneKey,
|
|
355
371
|
keys = keys,
|
|
356
|
-
requiredSignatures = one("requiredSignatures", block.requiredSignatures, js.requiredSignatures),
|
|
357
|
-
recoveryKeys = list("recoveryPublicKeys", block.recoveryPublicKeys, js.recoveryKeys),
|
|
358
|
-
requiredRecovery = one("requiredRecoverySignatures", block.requiredRecoverySignatures, js.requiredRecovery),
|
|
359
|
-
serverScope = one("serverScope", block.serverScope, js.serverScope),
|
|
360
|
-
clientCaPins = list("clientCaPins", block.clientCaPins, js.clientCaPins),
|
|
372
|
+
requiredSignatures = one("requiredSignatures", block.requiredSignatures, anchorOnly("requiredSignatures", block.requiredSignatures, js.requiredSignatures) { it == 1 }),
|
|
373
|
+
recoveryKeys = list("recoveryPublicKeys", block.recoveryPublicKeys, anchorOnly("recoveryPublicKeys", block.recoveryPublicKeys, js.recoveryKeys) { it.isEmpty() }),
|
|
374
|
+
requiredRecovery = one("requiredRecoverySignatures", block.requiredRecoverySignatures, anchorOnly("requiredRecoverySignatures", block.requiredRecoverySignatures, js.requiredRecovery) { it == 1 }),
|
|
375
|
+
serverScope = one("serverScope", block.serverScope, anchorOnly("serverScope", block.serverScope, js.serverScope) { false }),
|
|
376
|
+
clientCaPins = list("clientCaPins", block.clientCaPins, anchorOnly("clientCaPins", block.clientCaPins, js.clientCaPins) { it.isEmpty() }),
|
|
361
377
|
// The file is the source of truth for the relaxations too.
|
|
362
378
|
allowUnsigned = block.allowUnsigned,
|
|
363
379
|
allowUnpinned = block.allowUnpinnedConfigApi,
|
|
@@ -8,9 +8,12 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
|
|
8
8
|
import okhttp3.OkHttpClient
|
|
9
9
|
import okhttp3.Request
|
|
10
10
|
import okhttp3.RequestBody.Companion.toRequestBody
|
|
11
|
+
import okhttp3.Response
|
|
11
12
|
import okio.Buffer
|
|
12
13
|
import okio.ByteString.Companion.decodeBase64
|
|
13
14
|
import java.io.IOException
|
|
15
|
+
import java.io.InterruptedIOException
|
|
16
|
+
import java.net.ProtocolException
|
|
14
17
|
import java.util.concurrent.TimeUnit
|
|
15
18
|
|
|
16
19
|
/** A `fetch(url, init)` from JS, checked. */
|
|
@@ -32,7 +35,9 @@ internal class ResponseTooLargeException(limit: Long) : IOException("Response la
|
|
|
32
35
|
* The plugin's own `fetch`: parsed strictly, sent through the pinned client
|
|
33
36
|
* the library hands out (`PinVault.getClient()` or `getClient(settings)`),
|
|
34
37
|
* HTTPS only, bounded in both directions. A redirect from `https` into plain
|
|
35
|
-
* `http` is not followed (the 3xx comes back), as on iOS.
|
|
38
|
+
* `http` is not followed (the 3xx comes back), as on iOS. A redirect to
|
|
39
|
+
* another host or port takes only [CROSS_ORIGIN_HEADERS] along and never a
|
|
40
|
+
* body ([followUp]).
|
|
36
41
|
*/
|
|
37
42
|
internal object PinnedFetch {
|
|
38
43
|
|
|
@@ -50,6 +55,56 @@ internal object PinnedFetch {
|
|
|
50
55
|
"Host", "Connection", "Keep-Alive", "Proxy-Connection", "TE", "Trailer", "Transfer-Encoding", "Upgrade", "Content-Length",
|
|
51
56
|
)
|
|
52
57
|
|
|
58
|
+
/**
|
|
59
|
+
* The only request headers that follow a redirect to another host or port
|
|
60
|
+
* (lower case): what the client accepts and what it is. Everything else
|
|
61
|
+
* the caller set was meant for the host it named.
|
|
62
|
+
*/
|
|
63
|
+
val CROSS_ORIGIN_HEADERS = setOf("accept", "accept-language", "user-agent")
|
|
64
|
+
|
|
65
|
+
/** As OkHttp's own limit. */
|
|
66
|
+
const val MAX_FOLLOW_UPS = 20
|
|
67
|
+
private val REDIRECT_CODES = setOf(300, 301, 302, 303, 307, 308)
|
|
68
|
+
|
|
69
|
+
/** Why a caller may not set request header [name], or null when it may: the library's and the HTTP client's own. */
|
|
70
|
+
fun refusedHeader(name: String): String? = when {
|
|
71
|
+
LIBRARY_HEADERS.any { it.equals(name, ignoreCase = true) } -> "set by PinVault itself (a token host's requests carry it)"
|
|
72
|
+
CLIENT_HEADERS.any { it.equals(name, ignoreCase = true) } -> "set by the HTTP client (host, connection and framing headers)"
|
|
73
|
+
else -> null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* [refusedHeader] for a WebSocket's upgrade request, which is an HTTP
|
|
78
|
+
* request like any other (a caller's `Host` would name another site on the
|
|
79
|
+
* pinned connection), plus the handshake's own `Sec-WebSocket-*` headers.
|
|
80
|
+
*/
|
|
81
|
+
fun refusedSocketHeader(name: String): String? = refusedHeader(name)
|
|
82
|
+
?: if (name.startsWith("Sec-WebSocket-", ignoreCase = true)) "set by the WebSocket handshake" else null
|
|
83
|
+
|
|
84
|
+
const val MAX_SOCKET_PROTOCOLS = 8
|
|
85
|
+
const val MAX_SOCKET_PROTOCOL_LENGTH = 64
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A WebSocket's subprotocols as the caller listed them, checked: the
|
|
89
|
+
* bridge writes `Sec-WebSocket-Protocol` from this list itself (the header
|
|
90
|
+
* is refused from the caller, [refusedSocketHeader]). Each entry is an
|
|
91
|
+
* RFC 7230 token — so no comma, space or control character can add a
|
|
92
|
+
* second value or a second header — of bounded length, and none twice
|
|
93
|
+
* (RFC 6455 §4.1).
|
|
94
|
+
*/
|
|
95
|
+
fun socketProtocols(raw: List<*>?): List<String> {
|
|
96
|
+
if (raw == null) return emptyList()
|
|
97
|
+
if (raw.size > MAX_SOCKET_PROTOCOLS) throw BridgeInputException("protocols: at most $MAX_SOCKET_PROTOCOLS")
|
|
98
|
+
val seen = HashSet<String>()
|
|
99
|
+
return raw.map { entry ->
|
|
100
|
+
if (entry !is String || entry.length > MAX_SOCKET_PROTOCOL_LENGTH || !TOKEN.matches(entry)) {
|
|
101
|
+
throw BridgeInputException("protocols: each must be a token (RFC 7230) of at most $MAX_SOCKET_PROTOCOL_LENGTH characters")
|
|
102
|
+
}
|
|
103
|
+
if (!seen.add(entry)) throw BridgeInputException("protocols: '$entry' is listed twice")
|
|
104
|
+
entry
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
53
108
|
fun parse(json: String): FetchRequest {
|
|
54
109
|
// Base64 inflates by 4/3; leave room for the other fields.
|
|
55
110
|
val f = StrictJson.parseObject(json, "request", maxChars = (MAX_REQUEST_BYTES * 4 / 3 + 64 * 1024).toInt())
|
|
@@ -61,12 +116,7 @@ internal object PinnedFetch {
|
|
|
61
116
|
val headers = Headers.Builder()
|
|
62
117
|
f.stringMap("headers", 128, 256, 8192)?.forEach { (name, value) ->
|
|
63
118
|
if (!TOKEN.matches(name)) throw BridgeInputException("request.headers: '${name.take(40)}' is not a header name")
|
|
64
|
-
|
|
65
|
-
throw BridgeInputException("request.headers.$name: set by PinVault itself (a token host's requests carry it)")
|
|
66
|
-
}
|
|
67
|
-
if (CLIENT_HEADERS.any { it.equals(name, ignoreCase = true) }) {
|
|
68
|
-
throw BridgeInputException("request.headers.$name: set by the HTTP client (host, connection and framing headers)")
|
|
69
|
-
}
|
|
119
|
+
refusedHeader(name)?.let { why -> throw BridgeInputException("request.headers.$name: $why") }
|
|
70
120
|
if (value.any { it == '\r' || it == '\n' || it.code == 0 }) {
|
|
71
121
|
throw BridgeInputException("request.headers.$name: line breaks are not allowed")
|
|
72
122
|
}
|
|
@@ -104,18 +154,77 @@ internal object PinnedFetch {
|
|
|
104
154
|
/** Runs [request] on [client] (a pinned client from the library); blocking. */
|
|
105
155
|
fun execute(client: OkHttpClient, request: FetchRequest): Map<String, Any?> {
|
|
106
156
|
// newBuilder shares the pool, the pinned socket factory and the
|
|
107
|
-
// library's interceptors
|
|
108
|
-
|
|
157
|
+
// library's interceptors. Redirects are followed here, not by OkHttp:
|
|
158
|
+
// OkHttp sends every header but Authorization — and the body of a
|
|
159
|
+
// 307/308 — to whatever host the Location names (see followUp).
|
|
160
|
+
val callClient = client.newBuilder().followRedirects(false).followSslRedirects(false).build()
|
|
109
161
|
val contentType = request.headers["Content-Type"]?.toMediaTypeOrNull()
|
|
110
162
|
val body = when {
|
|
111
163
|
request.body != null -> request.body.toRequestBody(contentType)
|
|
112
164
|
request.method in setOf("POST", "PUT", "PATCH") -> ByteArray(0).toRequestBody(contentType)
|
|
113
165
|
else -> null
|
|
114
166
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
request.timeoutMs?.let {
|
|
118
|
-
|
|
167
|
+
var okRequest = Request.Builder().url(request.url).headers(request.headers).method(request.method, body).build()
|
|
168
|
+
// One time limit for the whole exchange, redirects included, as when OkHttp followed them.
|
|
169
|
+
val deadline = request.timeoutMs?.let { System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(it) }
|
|
170
|
+
var followUps = 0
|
|
171
|
+
while (true) {
|
|
172
|
+
val call = callClient.newCall(okRequest)
|
|
173
|
+
if (deadline != null) {
|
|
174
|
+
val left = deadline - System.nanoTime()
|
|
175
|
+
if (left <= 0) throw InterruptedIOException("timeout")
|
|
176
|
+
call.timeout().timeout(left, TimeUnit.NANOSECONDS)
|
|
177
|
+
}
|
|
178
|
+
val response = call.execute()
|
|
179
|
+
// A client the app told not to follow redirects still does not.
|
|
180
|
+
val next = if (client.followRedirects) followUp(okRequest, response) else null
|
|
181
|
+
if (next == null) return read(response, request)
|
|
182
|
+
response.close()
|
|
183
|
+
followUps++
|
|
184
|
+
if (followUps > MAX_FOLLOW_UPS) throw ProtocolException("Too many follow-up requests: $followUps")
|
|
185
|
+
okRequest = next
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The next request for a 3xx, or null when [response] is the answer.
|
|
191
|
+
* Built from [sent] — the request as this class made it, not as the
|
|
192
|
+
* library's interceptors completed it: what they add (the attestation
|
|
193
|
+
* token of a token host) is added again for the next hop, for its host.
|
|
194
|
+
*
|
|
195
|
+
* To the same host and port everything goes along, as OkHttp does it
|
|
196
|
+
* (301/302/303 turn a POST into a GET; 307/308 repeat method and body).
|
|
197
|
+
* To another host or port only [CROSS_ORIGIN_HEADERS] go along — a fixed
|
|
198
|
+
* list of credential names would miss an app's own session or API-key
|
|
199
|
+
* header — and a body is not sent again: a 307/308 that would repeat it
|
|
200
|
+
* there is the answer. A redirect into plain `http` is never followed.
|
|
201
|
+
*/
|
|
202
|
+
internal fun followUp(sent: Request, response: Response): Request? {
|
|
203
|
+
if (response.code !in REDIRECT_CODES) return null
|
|
204
|
+
val location = response.header("Location") ?: return null
|
|
205
|
+
val target = sent.url.resolve(location) ?: return null
|
|
206
|
+
if (!target.isHttps) return null
|
|
207
|
+
val next = sent.newBuilder().url(target)
|
|
208
|
+
var repeatsBody = false
|
|
209
|
+
if (sent.method != "GET" && sent.method != "HEAD") {
|
|
210
|
+
if (response.code == 307 || response.code == 308) {
|
|
211
|
+
repeatsBody = (sent.body?.contentLength() ?: 0L) != 0L
|
|
212
|
+
} else {
|
|
213
|
+
next.method("GET", null)
|
|
214
|
+
listOf("Transfer-Encoding", "Content-Length", "Content-Type").forEach { next.removeHeader(it) }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
val sameOrigin = target.host == sent.url.host && target.port == sent.url.port
|
|
218
|
+
if (!sameOrigin) {
|
|
219
|
+
if (repeatsBody) return null
|
|
220
|
+
sent.headers.names().filter { it.lowercase() !in CROSS_ORIGIN_HEADERS }.forEach { next.removeHeader(it) }
|
|
221
|
+
}
|
|
222
|
+
return next.build()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Reads [response] under the request's size limit and maps it for the bridge; closes it. */
|
|
226
|
+
private fun read(response: Response, request: FetchRequest): Map<String, Any?> {
|
|
227
|
+
response.use {
|
|
119
228
|
val responseBody = response.body
|
|
120
229
|
val declared = responseBody?.contentLength() ?: -1L
|
|
121
230
|
if (declared > request.maxResponseBytes) throw ResponseTooLargeException(request.maxResponseBytes)
|
|
@@ -99,7 +99,7 @@ public enum ConfigParser {
|
|
|
99
99
|
} else if let max = require.vaultFileMaxOfflineAgeMs {
|
|
100
100
|
builder.vaultFileMaxOfflineAge(max, .milliseconds)
|
|
101
101
|
}
|
|
102
|
-
|
|
102
|
+
let requireUnlocked = try root.bool("requireUnlockedDevice") == true || require.requireUnlockedDevice
|
|
103
103
|
if try root.bool("requireHardwareBackedKeys") == true || require.requireHardwareBackedKeys { builder.requireHardwareBackedKeys() }
|
|
104
104
|
if try root.bool("managedTrustRoots") == true || require.managedTrustRoots { builder.managedTrustRoots() }
|
|
105
105
|
if let v = try root.stringList("expectedSignerSha256", maxItems: 16, maxLength: 128) { builder.expectedSignerSha256(v) }
|
|
@@ -121,7 +121,11 @@ public enum ConfigParser {
|
|
|
121
121
|
// Android-only settings: the Android side reads them; here only their shape is checked.
|
|
122
122
|
_ = try root.object("android")
|
|
123
123
|
var maxRN = ReactNetworking.defaultMaxResponseBytes
|
|
124
|
+
// `requireUnlockedDevice(allowFallback: true)`: off unless the app asks for it here
|
|
125
|
+
// (the Android twin is `android.requireUnlockedDeviceAllowFallback`).
|
|
126
|
+
var allowFallback = false
|
|
124
127
|
if let ios = try root.object("ios") {
|
|
128
|
+
allowFallback = try ios.bool("requireUnlockedDeviceAllowFallback") == true
|
|
125
129
|
if let v = try ios.int64("reactNativeMaxResponseBytes", min: 1, max: ReactNetworking.maxResponseBytesLimit) { maxRN = v }
|
|
126
130
|
try applyResolve(try ios.stringMap("resolve", maxItems: 64, maxKeyLength: 255, maxValueLength: 255),
|
|
127
131
|
native: native, release: release, to: builder)
|
|
@@ -147,6 +151,10 @@ public enum ConfigParser {
|
|
|
147
151
|
if let v = require.expectedTeamIds { builder.expectedTeamId(v) }
|
|
148
152
|
if let v = require.userAuthStrength { builder.userAuthStrength(v) }
|
|
149
153
|
}
|
|
154
|
+
if allowFallback && !requireUnlocked {
|
|
155
|
+
throw BridgeInputError("config.ios.requireUnlockedDeviceAllowFallback: needs requireUnlockedDevice: true")
|
|
156
|
+
}
|
|
157
|
+
if requireUnlocked { builder.requireUnlockedDevice(allowFallback: allowFallback) }
|
|
150
158
|
|
|
151
159
|
try root.finish()
|
|
152
160
|
if let listener { builder.onConnectionEvent(listener) }
|
|
@@ -225,12 +233,38 @@ public enum ConfigParser {
|
|
|
225
233
|
/// is refused (RN-2). Returns nil when the optional field is absent.
|
|
226
234
|
static func relEndpoint(_ f: Fields, _ key: String, maxLength: Int) throws -> String? {
|
|
227
235
|
guard let v = try f.string(key, maxLength: maxLength) else { return nil }
|
|
228
|
-
|
|
236
|
+
// Whitespace around it, a backslash anywhere (parsers read it as `/`) or a
|
|
237
|
+
// `scheme:` prefix: an absolute URL however its slashes are written.
|
|
238
|
+
if v != v.trimmingCharacters(in: .whitespacesAndNewlines) || v.contains("\\") || v.contains("://") || v.hasPrefix("/")
|
|
239
|
+
|| v.range(of: "^[A-Za-z][A-Za-z0-9+.-]*:", options: .regularExpression) != nil {
|
|
229
240
|
throw BridgeInputError("\(f.path).\(key): must be a relative path, not an absolute URL")
|
|
230
241
|
}
|
|
231
242
|
return v
|
|
232
243
|
}
|
|
233
244
|
|
|
245
|
+
/// A vault file's endpoint: required, held to the same rule as the block's
|
|
246
|
+
/// endpoints (`relEndpoint` — it used to be checked for `://` and a leading
|
|
247
|
+
/// slash only, so " //host/x" or "https:\\host/x" sent the file request and
|
|
248
|
+
/// the file's token to another host), and without a `..` segment: the
|
|
249
|
+
/// endpoint is resolved against the block's URL, and `..` would climb out of
|
|
250
|
+
/// the path the app's native side declared for that Config API.
|
|
251
|
+
static func vaultEndpoint(_ f: Fields) throws -> String {
|
|
252
|
+
guard let endpoint = try relEndpoint(f, "endpoint", maxLength: 512) else {
|
|
253
|
+
throw BridgeInputError("\(f.path).endpoint: required")
|
|
254
|
+
}
|
|
255
|
+
if hasParentSegment(endpoint) {
|
|
256
|
+
throw BridgeInputError("\(f.path).endpoint: must not contain a '..' path segment")
|
|
257
|
+
}
|
|
258
|
+
return endpoint
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/// True when the path of `endpoint` has a `..` segment, written plainly or as `%2e` (URL parsers read both).
|
|
262
|
+
static func hasParentSegment(_ endpoint: String) -> Bool {
|
|
263
|
+
let path = endpoint.prefix { $0 != "?" && $0 != "#" }
|
|
264
|
+
return path.split(separator: "/", omittingEmptySubsequences: false)
|
|
265
|
+
.contains { $0.replacingOccurrences(of: "%2e", with: ".", options: .caseInsensitive) == ".." }
|
|
266
|
+
}
|
|
267
|
+
|
|
234
268
|
public static func hostPin(_ p: Fields) throws -> HostPin {
|
|
235
269
|
let hostname = try p.requireString("hostname", maxLength: 255)
|
|
236
270
|
guard let pins = try p.stringList("sha256", maxItems: maxPinsPerHost, maxLength: 128) else {
|
|
@@ -411,10 +445,7 @@ public enum ConfigParser {
|
|
|
411
445
|
|
|
412
446
|
static func addVaultFile(_ builder: PinVaultConfig.Builder, _ f: Fields, _ tokens: VaultTokenStore, native: NativeSecurity?) throws {
|
|
413
447
|
let key = try f.requireString("key", maxLength: 128)
|
|
414
|
-
let endpoint = try f
|
|
415
|
-
if endpoint.contains("://") || endpoint.hasPrefix("/") || endpoint.hasPrefix("\\") {
|
|
416
|
-
throw BridgeInputError("\(f.path).endpoint: must be a relative path, not an absolute URL")
|
|
417
|
-
}
|
|
448
|
+
let endpoint = try vaultEndpoint(f)
|
|
418
449
|
let jsSignatureKey = try f.string("signaturePublicKey", maxLength: keyLength, multiline: true)
|
|
419
450
|
let updateWithPins = try f.bool("updateWithPins")
|
|
420
451
|
let storage = try f.enumValue("storage", StorageStrategy.self)
|
|
@@ -311,11 +311,32 @@ enum SecurityPolicy {
|
|
|
311
311
|
if let j, !NativeSecurity.sameKeys(j, n) { throw fixed(key) }
|
|
312
312
|
return n
|
|
313
313
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
314
|
+
// The signing keys' threshold, the recovery keys and theirs are part of the
|
|
315
|
+
// library's trust-anchor fingerprint, and a changed fingerprint resets the
|
|
316
|
+
// replay watermarks. In a release build they are the file's alone: a value
|
|
317
|
+
// the bundle could add and drop again would reset them at will (a default — 1,
|
|
318
|
+
// no recovery keys — changes nothing and is let through).
|
|
319
|
+
// `serverScope` names the library's config store: another scope is another
|
|
320
|
+
// namespace, which starts with no watermarks at all — a bundle that sets one
|
|
321
|
+
// makes the device accept a replayed old config. `clientCaPins` decide which
|
|
322
|
+
// CA may issue the device's identity. Both follow the same rule.
|
|
323
|
+
func anchorOnly<T>(_ key: String, _ n: T?, _ j: T?, isDefault: (T) -> Bool) throws -> T? {
|
|
324
|
+
if release, n == nil, let j, !isDefault(j) {
|
|
325
|
+
throw BridgeInputError("\(path).\(key): a release build takes it only from the app's native security file (\(native.source)); "
|
|
326
|
+
+ "declare \(key) for Config API '\(id)' there")
|
|
327
|
+
}
|
|
328
|
+
return j
|
|
329
|
+
}
|
|
330
|
+
out.requiredSignatures = try one("requiredSignatures", block.requiredSignatures,
|
|
331
|
+
try anchorOnly("requiredSignatures", block.requiredSignatures, js.requiredSignatures) { $0 == 1 })
|
|
332
|
+
out.recoveryKeys = try list("recoveryPublicKeys", block.recoveryPublicKeys,
|
|
333
|
+
try anchorOnly("recoveryPublicKeys", block.recoveryPublicKeys, js.recoveryKeys) { $0.isEmpty })
|
|
334
|
+
out.requiredRecovery = try one("requiredRecoverySignatures", block.requiredRecoverySignatures,
|
|
335
|
+
try anchorOnly("requiredRecoverySignatures", block.requiredRecoverySignatures, js.requiredRecovery) { $0 == 1 })
|
|
336
|
+
out.serverScope = try one("serverScope", block.serverScope,
|
|
337
|
+
try anchorOnly("serverScope", block.serverScope, js.serverScope) { _ in false })
|
|
338
|
+
out.clientCaPins = try list("clientCaPins", block.clientCaPins,
|
|
339
|
+
try anchorOnly("clientCaPins", block.clientCaPins, js.clientCaPins) { $0.isEmpty })
|
|
319
340
|
// The file is the source of truth for the relaxations too.
|
|
320
341
|
out.allowUnsigned = block.allowUnsigned
|
|
321
342
|
out.allowUnpinned = block.allowUnpinnedConfigApi
|
|
@@ -33,6 +33,69 @@ public enum PinnedFetch {
|
|
|
33
33
|
static let clientHeaders = ["Host", "Connection", "Keep-Alive", "Proxy-Connection", "TE", "Trailer",
|
|
34
34
|
"Transfer-Encoding", "Upgrade", "Content-Length"]
|
|
35
35
|
|
|
36
|
+
/// Why a caller may not set request header `name`, or nil when it may: the library's and the HTTP client's own.
|
|
37
|
+
public static func refusedHeader(_ name: String) -> String? {
|
|
38
|
+
if libraryHeaders.contains(where: { $0.caseInsensitiveCompare(name) == .orderedSame }) {
|
|
39
|
+
return "set by PinVault itself (a token host's requests carry it)"
|
|
40
|
+
}
|
|
41
|
+
if clientHeaders.contains(where: { $0.caseInsensitiveCompare(name) == .orderedSame }) {
|
|
42
|
+
return "set by the HTTP client (host, connection and framing headers)"
|
|
43
|
+
}
|
|
44
|
+
return nil
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// ``refusedHeader(_:)`` for a WebSocket's upgrade request, which is an HTTP
|
|
48
|
+
/// request like any other (a caller's `Host` would name another site on the
|
|
49
|
+
/// pinned connection), plus the handshake's own `Sec-WebSocket-*` headers.
|
|
50
|
+
public static func refusedSocketHeader(_ name: String) -> String? {
|
|
51
|
+
if let why = refusedHeader(name) { return why }
|
|
52
|
+
return name.lowercased().hasPrefix("sec-websocket-") ? "set by the WebSocket handshake" : nil
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public static let maxSocketProtocols = 8
|
|
56
|
+
public static let maxSocketProtocolLength = 64
|
|
57
|
+
|
|
58
|
+
/// A WebSocket's subprotocols as the caller listed them, checked: the
|
|
59
|
+
/// bridge writes `Sec-WebSocket-Protocol` from this list itself (the header
|
|
60
|
+
/// is refused from the caller, ``refusedSocketHeader(_:)``). Each entry is
|
|
61
|
+
/// an RFC 7230 token — so no comma, space or control character can add a
|
|
62
|
+
/// second value or a second header — of bounded length, and none twice
|
|
63
|
+
/// (RFC 6455 §4.1).
|
|
64
|
+
public static func socketProtocols(_ raw: [Any]?) throws -> [String] {
|
|
65
|
+
guard let raw else { return [] }
|
|
66
|
+
if raw.count > maxSocketProtocols { throw BridgeInputError("protocols: at most \(maxSocketProtocols)") }
|
|
67
|
+
var seen = Set<String>()
|
|
68
|
+
return try raw.map { entry in
|
|
69
|
+
guard let name = entry as? String, !name.isEmpty, name.unicodeScalars.count <= maxSocketProtocolLength,
|
|
70
|
+
name.unicodeScalars.allSatisfy({ tokenChars.contains($0) }) else {
|
|
71
|
+
throw BridgeInputError("protocols: each must be a token (RFC 7230) of at most \(maxSocketProtocolLength) characters")
|
|
72
|
+
}
|
|
73
|
+
if !seen.insert(name).inserted { throw BridgeInputError("protocols: '\(name)' is listed twice") }
|
|
74
|
+
return name
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// `operation` under one time limit for the whole exchange, redirects
|
|
79
|
+
/// included (Android: OkHttp's call timeout). A request's own
|
|
80
|
+
/// `timeoutInterval` starts again at every hop and only counts idle time;
|
|
81
|
+
/// `timeoutMs` promises the caller an answer or a failure by then.
|
|
82
|
+
static func withDeadline<T>(_ timeoutMs: Int64?, _ operation: @escaping () async throws -> T) async throws -> T {
|
|
83
|
+
guard let timeoutMs else { return try await operation() }
|
|
84
|
+
return try await withThrowingTaskGroup(of: T?.self) { group in
|
|
85
|
+
group.addTask { try await operation() }
|
|
86
|
+
group.addTask {
|
|
87
|
+
try await Task.sleep(nanoseconds: UInt64(timeoutMs) * 1_000_000)
|
|
88
|
+
return nil
|
|
89
|
+
}
|
|
90
|
+
// Whichever ends first decides; the other is cancelled (a running request with it).
|
|
91
|
+
defer { group.cancelAll() }
|
|
92
|
+
guard let first = try await group.next(), let value = first else {
|
|
93
|
+
throw PinVaultError.io(message: "timeout", cause: URLError(.timedOut))
|
|
94
|
+
}
|
|
95
|
+
return value
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
36
99
|
public static func parse(_ json: String) throws -> FetchRequest {
|
|
37
100
|
let f = try StrictJSON.parseObject(json, path: "request", maxChars: Int(maxRequestBytes * 4 / 3) + 64 * 1024)
|
|
38
101
|
let urlText = try f.requireString("url", maxLength: 8192)
|
|
@@ -47,12 +110,7 @@ public enum PinnedFetch {
|
|
|
47
110
|
guard name.unicodeScalars.allSatisfy({ tokenChars.contains($0) }) else {
|
|
48
111
|
throw BridgeInputError("request.headers: '\(name.prefix(40))' is not a header name")
|
|
49
112
|
}
|
|
50
|
-
if
|
|
51
|
-
throw BridgeInputError("request.headers.\(name): set by PinVault itself (a token host's requests carry it)")
|
|
52
|
-
}
|
|
53
|
-
if clientHeaders.contains(where: { $0.caseInsensitiveCompare(name) == .orderedSame }) {
|
|
54
|
-
throw BridgeInputError("request.headers.\(name): set by the HTTP client (host, connection and framing headers)")
|
|
55
|
-
}
|
|
113
|
+
if let why = refusedHeader(name) { throw BridgeInputError("request.headers.\(name): \(why)") }
|
|
56
114
|
if value.unicodeScalars.contains(where: { $0 == "\r" || $0 == "\n" || $0.value == 0 }) {
|
|
57
115
|
throw BridgeInputError("request.headers.\(name): line breaks are not allowed")
|
|
58
116
|
}
|
|
@@ -105,7 +163,10 @@ public enum PinnedFetch {
|
|
|
105
163
|
/// library stops reading once the body passes `maxResponseBytes` (a
|
|
106
164
|
/// declared `Content-Length` over it is refused before the body is read).
|
|
107
165
|
public static func execute(_ session: PinnedSession, _ r: FetchRequest) async throws -> [String: Any] {
|
|
108
|
-
let
|
|
166
|
+
let request = urlRequest(r)
|
|
167
|
+
let (data, response) = try await withDeadline(r.timeoutMs) {
|
|
168
|
+
try await session.data(for: request, maxResponseBytes: r.maxResponseBytes)
|
|
169
|
+
}
|
|
109
170
|
guard let http = response as? HTTPURLResponse else { throw BridgeInputError("not an HTTP response") }
|
|
110
171
|
return try map(http, data, r)
|
|
111
172
|
}
|
|
@@ -149,6 +149,12 @@ export type PinVaultConfig = {
|
|
|
149
149
|
userAuthStrength?: 'DEVICE_OWNER' | 'BIOMETRIC_CURRENT_SET';
|
|
150
150
|
/** The largest answer React Native's own https request may get (bytes, default 50 MiB, at most 256 MiB). */
|
|
151
151
|
reactNativeMaxResponseBytes?: number;
|
|
152
|
+
/**
|
|
153
|
+
* `requireUnlockedDevice(allowFallback: true)`: when the Keychain refuses a key that
|
|
154
|
+
* works only while the device is unlocked, make it without that requirement instead
|
|
155
|
+
* of failing with `UnlockedDeviceKeyRequiredException` (the default since 2.9.0).
|
|
156
|
+
*/
|
|
157
|
+
requireUnlockedDeviceAllowFallback?: boolean;
|
|
152
158
|
};
|
|
153
159
|
};
|
|
154
160
|
/** A native exception: its class name (`SSLPinningException`, …) and message. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAEjF,qGAAqG;AACrG,MAAM,MAAM,QAAQ,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE1D,4GAA4G;AAC5G,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,kGAAkG;AAClG,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mBAAmB,CAAC,EAAE,QAAQ,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AACnE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,YAAY,CAAC;AAClF,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,CAAC;AACnF,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,qBAAqB,CAAC;IACrC,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,aAAa,CAAC,EAAE,QAAQ,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,GAAG,YAAY,GAAG,aAAa,GAAG,WAAW,CAAC;AAE9F;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,gBAAgB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE3F,yCAAyC;AACzC,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC1E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,QAAQ,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,sBAAsB,CAAC,EAAE,QAAQ,CAAC;IAClC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,2FAA2F;IAC3F,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;;OAKG;IACH,kCAAkC,CAAC,EAAE,OAAO,CAAC;IAC7C,8CAA8C;IAC9C,OAAO,CAAC,EAAE;QACR,qDAAqD;QACrD,kCAAkC,CAAC,EAAE,OAAO,CAAC;QAC7C;;;;WAIG;QACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,uGAAuG;QACvG,wBAAwB,CAAC,EAAE,OAAO,CAAC;QACnC,mGAAmG;QACnG,sBAAsB,CAAC,EAAE,OAAO,CAAC;KAClC,CAAC;IACF,8CAA8C;IAC9C,GAAG,CAAC,EAAE;QACJ,oFAAoF;QACpF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,sFAAsF;QACtF,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC7B,gGAAgG;QAChG,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3B,mFAAmF;QACnF,gBAAgB,CAAC,EAAE,cAAc,GAAG,uBAAuB,CAAC;QAC5D,4GAA4G;QAC5G,2BAA2B,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umutcansu/react-native-pinvault",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "PinVault for React Native: dynamic certificate pinning with signed remote configs, mTLS enrollment and signed vault files, as a thin bridge over the native PinVault libraries",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
package/src/types.ts
CHANGED
|
@@ -157,6 +157,12 @@ export type PinVaultConfig = {
|
|
|
157
157
|
userAuthStrength?: 'DEVICE_OWNER' | 'BIOMETRIC_CURRENT_SET';
|
|
158
158
|
/** The largest answer React Native's own https request may get (bytes, default 50 MiB, at most 256 MiB). */
|
|
159
159
|
reactNativeMaxResponseBytes?: number;
|
|
160
|
+
/**
|
|
161
|
+
* `requireUnlockedDevice(allowFallback: true)`: when the Keychain refuses a key that
|
|
162
|
+
* works only while the device is unlocked, make it without that requirement instead
|
|
163
|
+
* of failing with `UnlockedDeviceKeyRequiredException` (the default since 2.9.0).
|
|
164
|
+
*/
|
|
165
|
+
requireUnlockedDeviceAllowFallback?: boolean;
|
|
160
166
|
};
|
|
161
167
|
};
|
|
162
168
|
|