Skip to content

Commit 98b7659

Browse files
committed
Swift: Add a special case sink for weak sensitive data hashing sinks that are calls through a metatype.
1 parent d9c0b9c commit 98b7659

4 files changed

Lines changed: 107 additions & 17 deletions

File tree

swift/ql/lib/codeql/swift/security/WeakSensitiveDataHashingExtensions.qll

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,21 @@ private class DefaultWeakSenitiveDataHashingSink extends WeakSensitiveDataHashin
7676

7777
override string getAlgorithm() { result = algorithm }
7878
}
79+
80+
/**
81+
* A sink for weak sensitive data hashing through a call with a metatype qualifier.
82+
*/
83+
private class WeakSenitiveDataHashingMetatypeSink extends WeakSensitiveDataHashingSink {
84+
string algorithm;
85+
86+
WeakSenitiveDataHashingMetatypeSink() {
87+
exists(CallExpr c |
88+
c.getAnArgument().getExpr() = this.asExpr() and
89+
algorithm = ["MD5", "SHA1"] and
90+
c.getQualifier().getType().getFullName() = "Insecure." + algorithm + ".Type" and
91+
c.getStaticTarget().getName() = ["hash(data:)", "update(data:)", "update(bufferPointer:)"]
92+
)
93+
}
94+
95+
override string getAlgorithm() { result = algorithm }
96+
}

swift/ql/test/query-tests/Security/CWE-328/WeakPasswordHashing.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
edges
22
nodes
3+
| testCryptoKit.swift:84:47:84:47 | passwd | semmle.label | passwd |
4+
| testCryptoKit.swift:90:36:90:36 | passwd | semmle.label | passwd |
5+
| testCryptoKit.swift:96:44:96:44 | passwd | semmle.label | passwd |
36
| testCryptoKit.swift:168:32:168:32 | passwd | semmle.label | passwd |
47
| testCryptoKit.swift:177:32:177:32 | passwd | semmle.label | passwd |
58
| testCryptoKit.swift:186:32:186:32 | passwd | semmle.label | passwd |
@@ -31,6 +34,9 @@ nodes
3134
| testCryptoSwift.swift:231:9:231:9 | passwd | semmle.label | passwd |
3235
subpaths
3336
#select
37+
| testCryptoKit.swift:84:47:84:47 | passwd | testCryptoKit.swift:84:47:84:47 | passwd | testCryptoKit.swift:84:47:84:47 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:84:47:84:47 | passwd | password (passwd) |
38+
| testCryptoKit.swift:90:36:90:36 | passwd | testCryptoKit.swift:90:36:90:36 | passwd | testCryptoKit.swift:90:36:90:36 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:90:36:90:36 | passwd | password (passwd) |
39+
| testCryptoKit.swift:96:44:96:44 | passwd | testCryptoKit.swift:96:44:96:44 | passwd | testCryptoKit.swift:96:44:96:44 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:96:44:96:44 | passwd | password (passwd) |
3440
| testCryptoKit.swift:168:32:168:32 | passwd | testCryptoKit.swift:168:32:168:32 | passwd | testCryptoKit.swift:168:32:168:32 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:168:32:168:32 | passwd | password (passwd) |
3541
| testCryptoKit.swift:177:32:177:32 | passwd | testCryptoKit.swift:177:32:177:32 | passwd | testCryptoKit.swift:177:32:177:32 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:177:32:177:32 | passwd | password (passwd) |
3642
| testCryptoKit.swift:186:32:186:32 | passwd | testCryptoKit.swift:186:32:186:32 | passwd | testCryptoKit.swift:186:32:186:32 | passwd | Insecure hashing algorithm (SHA256) depends on $@. | testCryptoKit.swift:186:32:186:32 | passwd | password (passwd) |

swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,63 @@
11
edges
2+
| testCryptoKit.swift:224:18:224:38 | call to Data.init(_:) | testCryptoKit.swift:225:44:225:44 | value1 | provenance | |
3+
| testCryptoKit.swift:224:23:224:23 | cardNumber | testCryptoKit.swift:224:23:224:34 | .utf8 | provenance | |
4+
| testCryptoKit.swift:224:23:224:34 | .utf8 | testCryptoKit.swift:224:18:224:38 | call to Data.init(_:) | provenance | |
5+
| testCryptoKit.swift:227:18:227:38 | call to Data.init(_:) | testCryptoKit.swift:229:39:229:39 | value2 | provenance | |
6+
| testCryptoKit.swift:227:23:227:23 | cardNumber | testCryptoKit.swift:227:23:227:34 | .utf8 | provenance | |
7+
| testCryptoKit.swift:227:23:227:34 | .utf8 | testCryptoKit.swift:227:18:227:38 | call to Data.init(_:) | provenance | |
8+
| testCryptoKit.swift:231:18:231:38 | call to Data.init(_:) | testCryptoKit.swift:232:51:232:51 | value3 | provenance | |
9+
| testCryptoKit.swift:231:23:231:23 | cardNumber | testCryptoKit.swift:231:23:231:34 | .utf8 | provenance | |
10+
| testCryptoKit.swift:231:23:231:34 | .utf8 | testCryptoKit.swift:231:18:231:38 | call to Data.init(_:) | provenance | |
11+
| testCryptoKit.swift:234:18:234:38 | call to Data.init(_:) | testCryptoKit.swift:235:26:235:26 | value4 | provenance | |
12+
| testCryptoKit.swift:234:23:234:23 | cardNumber | testCryptoKit.swift:234:23:234:34 | .utf8 | provenance | |
13+
| testCryptoKit.swift:234:23:234:34 | .utf8 | testCryptoKit.swift:234:18:234:38 | call to Data.init(_:) | provenance | |
14+
| testCryptoKit.swift:235:26:235:26 | value4 | testCryptoKit.swift:244:20:244:27 | value | provenance | |
15+
| testCryptoKit.swift:237:18:237:38 | call to Data.init(_:) | testCryptoKit.swift:238:53:238:53 | value5 | provenance | |
16+
| testCryptoKit.swift:237:23:237:23 | cardNumber | testCryptoKit.swift:237:23:237:34 | .utf8 | provenance | |
17+
| testCryptoKit.swift:237:23:237:34 | .utf8 | testCryptoKit.swift:237:18:237:38 | call to Data.init(_:) | provenance | |
18+
| testCryptoKit.swift:238:53:238:53 | value5 | testCryptoKit.swift:248:47:248:54 | value | provenance | |
19+
| testCryptoKit.swift:244:20:244:27 | value | testCryptoKit.swift:245:43:245:43 | value | provenance | |
20+
| testCryptoKit.swift:248:47:248:54 | value | testCryptoKit.swift:249:37:249:37 | value | provenance | |
221
nodes
22+
| testCryptoKit.swift:85:43:85:43 | cert | semmle.label | cert |
23+
| testCryptoKit.swift:87:43:87:43 | account_no | semmle.label | account_no |
24+
| testCryptoKit.swift:88:43:88:43 | credit_card_no | semmle.label | credit_card_no |
25+
| testCryptoKit.swift:91:36:91:36 | cert | semmle.label | cert |
26+
| testCryptoKit.swift:93:36:93:36 | account_no | semmle.label | account_no |
27+
| testCryptoKit.swift:94:36:94:36 | credit_card_no | semmle.label | credit_card_no |
28+
| testCryptoKit.swift:97:44:97:44 | cert | semmle.label | cert |
29+
| testCryptoKit.swift:99:44:99:44 | account_no | semmle.label | account_no |
30+
| testCryptoKit.swift:100:44:100:44 | credit_card_no | semmle.label | credit_card_no |
331
| testCryptoKit.swift:169:32:169:32 | cert | semmle.label | cert |
432
| testCryptoKit.swift:171:32:171:32 | account_no | semmle.label | account_no |
533
| testCryptoKit.swift:172:32:172:32 | credit_card_no | semmle.label | credit_card_no |
634
| testCryptoKit.swift:178:32:178:32 | cert | semmle.label | cert |
735
| testCryptoKit.swift:180:32:180:32 | account_no | semmle.label | account_no |
836
| testCryptoKit.swift:181:32:181:32 | credit_card_no | semmle.label | credit_card_no |
37+
| testCryptoKit.swift:224:18:224:38 | call to Data.init(_:) | semmle.label | call to Data.init(_:) |
38+
| testCryptoKit.swift:224:23:224:23 | cardNumber | semmle.label | cardNumber |
39+
| testCryptoKit.swift:224:23:224:34 | .utf8 | semmle.label | .utf8 |
40+
| testCryptoKit.swift:225:44:225:44 | value1 | semmle.label | value1 |
41+
| testCryptoKit.swift:227:18:227:38 | call to Data.init(_:) | semmle.label | call to Data.init(_:) |
42+
| testCryptoKit.swift:227:23:227:23 | cardNumber | semmle.label | cardNumber |
43+
| testCryptoKit.swift:227:23:227:34 | .utf8 | semmle.label | .utf8 |
44+
| testCryptoKit.swift:229:39:229:39 | value2 | semmle.label | value2 |
45+
| testCryptoKit.swift:231:18:231:38 | call to Data.init(_:) | semmle.label | call to Data.init(_:) |
46+
| testCryptoKit.swift:231:23:231:23 | cardNumber | semmle.label | cardNumber |
47+
| testCryptoKit.swift:231:23:231:34 | .utf8 | semmle.label | .utf8 |
48+
| testCryptoKit.swift:232:51:232:51 | value3 | semmle.label | value3 |
49+
| testCryptoKit.swift:234:18:234:38 | call to Data.init(_:) | semmle.label | call to Data.init(_:) |
50+
| testCryptoKit.swift:234:23:234:23 | cardNumber | semmle.label | cardNumber |
51+
| testCryptoKit.swift:234:23:234:34 | .utf8 | semmle.label | .utf8 |
52+
| testCryptoKit.swift:235:26:235:26 | value4 | semmle.label | value4 |
53+
| testCryptoKit.swift:237:18:237:38 | call to Data.init(_:) | semmle.label | call to Data.init(_:) |
54+
| testCryptoKit.swift:237:23:237:23 | cardNumber | semmle.label | cardNumber |
55+
| testCryptoKit.swift:237:23:237:34 | .utf8 | semmle.label | .utf8 |
56+
| testCryptoKit.swift:238:53:238:53 | value5 | semmle.label | value5 |
57+
| testCryptoKit.swift:244:20:244:27 | value | semmle.label | value |
58+
| testCryptoKit.swift:245:43:245:43 | value | semmle.label | value |
59+
| testCryptoKit.swift:248:47:248:54 | value | semmle.label | value |
60+
| testCryptoKit.swift:249:37:249:37 | value | semmle.label | value |
961
| testCryptoSwift.swift:153:30:153:30 | phoneNumberArray | semmle.label | phoneNumberArray |
1062
| testCryptoSwift.swift:156:31:156:31 | phoneNumberArray | semmle.label | phoneNumberArray |
1163
| testCryptoSwift.swift:166:20:166:20 | phoneNumberArray | semmle.label | phoneNumberArray |
@@ -18,12 +70,26 @@ nodes
1870
| testCryptoSwift.swift:221:9:221:9 | creditCardNumber | semmle.label | creditCardNumber |
1971
subpaths
2072
#select
73+
| testCryptoKit.swift:85:43:85:43 | cert | testCryptoKit.swift:85:43:85:43 | cert | testCryptoKit.swift:85:43:85:43 | cert | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:85:43:85:43 | cert | sensitive data (credential cert) |
74+
| testCryptoKit.swift:87:43:87:43 | account_no | testCryptoKit.swift:87:43:87:43 | account_no | testCryptoKit.swift:87:43:87:43 | account_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:87:43:87:43 | account_no | sensitive data (private information account_no) |
75+
| testCryptoKit.swift:88:43:88:43 | credit_card_no | testCryptoKit.swift:88:43:88:43 | credit_card_no | testCryptoKit.swift:88:43:88:43 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:88:43:88:43 | credit_card_no | sensitive data (private information credit_card_no) |
76+
| testCryptoKit.swift:91:36:91:36 | cert | testCryptoKit.swift:91:36:91:36 | cert | testCryptoKit.swift:91:36:91:36 | cert | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:91:36:91:36 | cert | sensitive data (credential cert) |
77+
| testCryptoKit.swift:93:36:93:36 | account_no | testCryptoKit.swift:93:36:93:36 | account_no | testCryptoKit.swift:93:36:93:36 | account_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:93:36:93:36 | account_no | sensitive data (private information account_no) |
78+
| testCryptoKit.swift:94:36:94:36 | credit_card_no | testCryptoKit.swift:94:36:94:36 | credit_card_no | testCryptoKit.swift:94:36:94:36 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:94:36:94:36 | credit_card_no | sensitive data (private information credit_card_no) |
79+
| testCryptoKit.swift:97:44:97:44 | cert | testCryptoKit.swift:97:44:97:44 | cert | testCryptoKit.swift:97:44:97:44 | cert | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:97:44:97:44 | cert | sensitive data (credential cert) |
80+
| testCryptoKit.swift:99:44:99:44 | account_no | testCryptoKit.swift:99:44:99:44 | account_no | testCryptoKit.swift:99:44:99:44 | account_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:99:44:99:44 | account_no | sensitive data (private information account_no) |
81+
| testCryptoKit.swift:100:44:100:44 | credit_card_no | testCryptoKit.swift:100:44:100:44 | credit_card_no | testCryptoKit.swift:100:44:100:44 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:100:44:100:44 | credit_card_no | sensitive data (private information credit_card_no) |
2182
| testCryptoKit.swift:169:32:169:32 | cert | testCryptoKit.swift:169:32:169:32 | cert | testCryptoKit.swift:169:32:169:32 | cert | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:169:32:169:32 | cert | sensitive data (credential cert) |
2283
| testCryptoKit.swift:171:32:171:32 | account_no | testCryptoKit.swift:171:32:171:32 | account_no | testCryptoKit.swift:171:32:171:32 | account_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:171:32:171:32 | account_no | sensitive data (private information account_no) |
2384
| testCryptoKit.swift:172:32:172:32 | credit_card_no | testCryptoKit.swift:172:32:172:32 | credit_card_no | testCryptoKit.swift:172:32:172:32 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:172:32:172:32 | credit_card_no | sensitive data (private information credit_card_no) |
2485
| testCryptoKit.swift:178:32:178:32 | cert | testCryptoKit.swift:178:32:178:32 | cert | testCryptoKit.swift:178:32:178:32 | cert | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:178:32:178:32 | cert | sensitive data (credential cert) |
2586
| testCryptoKit.swift:180:32:180:32 | account_no | testCryptoKit.swift:180:32:180:32 | account_no | testCryptoKit.swift:180:32:180:32 | account_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:180:32:180:32 | account_no | sensitive data (private information account_no) |
2687
| testCryptoKit.swift:181:32:181:32 | credit_card_no | testCryptoKit.swift:181:32:181:32 | credit_card_no | testCryptoKit.swift:181:32:181:32 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:181:32:181:32 | credit_card_no | sensitive data (private information credit_card_no) |
88+
| testCryptoKit.swift:225:44:225:44 | value1 | testCryptoKit.swift:224:23:224:23 | cardNumber | testCryptoKit.swift:225:44:225:44 | value1 | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:224:23:224:23 | cardNumber | sensitive data (private information cardNumber) |
89+
| testCryptoKit.swift:229:39:229:39 | value2 | testCryptoKit.swift:227:23:227:23 | cardNumber | testCryptoKit.swift:229:39:229:39 | value2 | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:227:23:227:23 | cardNumber | sensitive data (private information cardNumber) |
90+
| testCryptoKit.swift:232:51:232:51 | value3 | testCryptoKit.swift:231:23:231:23 | cardNumber | testCryptoKit.swift:232:51:232:51 | value3 | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:231:23:231:23 | cardNumber | sensitive data (private information cardNumber) |
91+
| testCryptoKit.swift:245:43:245:43 | value | testCryptoKit.swift:234:23:234:23 | cardNumber | testCryptoKit.swift:245:43:245:43 | value | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:234:23:234:23 | cardNumber | sensitive data (private information cardNumber) |
92+
| testCryptoKit.swift:249:37:249:37 | value | testCryptoKit.swift:237:23:237:23 | cardNumber | testCryptoKit.swift:249:37:249:37 | value | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:237:23:237:23 | cardNumber | sensitive data (private information cardNumber) |
2793
| testCryptoSwift.swift:153:30:153:30 | phoneNumberArray | testCryptoSwift.swift:153:30:153:30 | phoneNumberArray | testCryptoSwift.swift:153:30:153:30 | phoneNumberArray | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:153:30:153:30 | phoneNumberArray | sensitive data (private information phoneNumberArray) |
2894
| testCryptoSwift.swift:156:31:156:31 | phoneNumberArray | testCryptoSwift.swift:156:31:156:31 | phoneNumberArray | testCryptoSwift.swift:156:31:156:31 | phoneNumberArray | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:156:31:156:31 | phoneNumberArray | sensitive data (private information phoneNumberArray) |
2995
| testCryptoSwift.swift:166:20:166:20 | phoneNumberArray | testCryptoSwift.swift:166:20:166:20 | phoneNumberArray | testCryptoSwift.swift:166:20:166:20 | phoneNumberArray | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:166:20:166:20 | phoneNumberArray | sensitive data (private information phoneNumberArray) |

swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,23 @@ enum Insecure {
8181
// --- tests ---
8282

8383
func testHashMethods(passwd : UnsafeRawBufferPointer, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) {
84-
var hash = Crypto.Insecure.MD5.hash(data: passwd) // BAD [NOT DETECTED]
85-
hash = Crypto.Insecure.MD5.hash(data: cert) // BAD [NOT DETECTED]
84+
var hash = Crypto.Insecure.MD5.hash(data: passwd) // BAD
85+
hash = Crypto.Insecure.MD5.hash(data: cert) // BAD
8686
hash = Crypto.Insecure.MD5.hash(data: encrypted_passwd) // GOOD (not sensitive)
87-
hash = Crypto.Insecure.MD5.hash(data: account_no) // BAD [NOT DETECTED]
88-
hash = Crypto.Insecure.MD5.hash(data: credit_card_no) // BAD [NOT DETECTED]
87+
hash = Crypto.Insecure.MD5.hash(data: account_no) // BAD
88+
hash = Crypto.Insecure.MD5.hash(data: credit_card_no) // BAD
8989

90-
hash = Insecure.MD5.hash(data: passwd) // BAD [NOT DETECTED]
91-
hash = Insecure.MD5.hash(data: cert) // BAD [NOT DETECTED]
90+
hash = Insecure.MD5.hash(data: passwd) // BAD
91+
hash = Insecure.MD5.hash(data: cert) // BAD
9292
hash = Insecure.MD5.hash(data: encrypted_passwd) // GOOD (not sensitive)
93-
hash = Insecure.MD5.hash(data: account_no) // BAD [NOT DETECTED]
94-
hash = Insecure.MD5.hash(data: credit_card_no) // BAD [NOT DETECTED]
93+
hash = Insecure.MD5.hash(data: account_no) // BAD
94+
hash = Insecure.MD5.hash(data: credit_card_no) // BAD
9595

96-
hash = Crypto.Insecure.SHA1.hash(data: passwd) // BAD [NOT DETECTED]
97-
hash = Crypto.Insecure.SHA1.hash(data: cert) // BAD [NOT DETECTED]
96+
hash = Crypto.Insecure.SHA1.hash(data: passwd) // BAD
97+
hash = Crypto.Insecure.SHA1.hash(data: cert) // BAD
9898
hash = Crypto.Insecure.SHA1.hash(data: encrypted_passwd) // GOOD (not sensitive)
99-
hash = Crypto.Insecure.SHA1.hash(data: account_no) // BAD [NOT DETECTED]
100-
hash = Crypto.Insecure.SHA1.hash(data: credit_card_no) // BAD [NOT DETECTED]
99+
hash = Crypto.Insecure.SHA1.hash(data: account_no) // BAD
100+
hash = Crypto.Insecure.SHA1.hash(data: credit_card_no) // BAD
101101

102102
hash = Crypto.SHA256.hash(data: passwd) // BAD, not a computationally expensive hash [NOT DETECTED]
103103
hash = Crypto.SHA256.hash(data: cert) // GOOD, computationally expensive hash not required
@@ -222,14 +222,14 @@ func testBadExample(passwordString: String) {
222222

223223
func testWithFlowAndMetatypes(cardNumber: String) {
224224
let value1 = Data(cardNumber.utf8);
225-
let _digest1 = Insecure.MD5.hash(data: value1); // BAD [NOT DETECTED]
225+
let _digest1 = Insecure.MD5.hash(data: value1); // BAD
226226

227227
let value2 = Data(cardNumber.utf8);
228228
let hasher2 = Insecure.MD5.self; // metatype
229-
let _digest2 = hasher2.hash(data: value2); // BAD [NOT DETECTED]
229+
let _digest2 = hasher2.hash(data: value2); // BAD
230230

231231
let value3 = Data(cardNumber.utf8);
232-
let _digest3 = (Insecure.MD5.self).hash(data: value3); // BAD [NOT DETECTED]
232+
let _digest3 = (Insecure.MD5.self).hash(data: value3); // BAD
233233

234234
let value4 = Data(cardNumber.utf8);
235235
testReceiver1(value: value4);
@@ -242,11 +242,11 @@ func testWithFlowAndMetatypes(cardNumber: String) {
242242
}
243243

244244
func testReceiver1(value: Data) {
245-
let _digest = Insecure.MD5.hash(data: value); // BAD [NOT DETECTED]
245+
let _digest = Insecure.MD5.hash(data: value); // BAD
246246
}
247247

248248
func testReceiver2(hasher: Insecure.MD5.Type, value: Data) {
249-
let _digest = hasher.hash(data: value); // BAD [NOT DETECTED]
249+
let _digest = hasher.hash(data: value); // BAD
250250
}
251251

252252
func testReceiver3<H: HashFunction>(hasher: H.Type, value: Data) {

0 commit comments

Comments
 (0)