Skip to content

Commit 3baba70

Browse files
authored
Merge pull request github#12764 from geoffw0/modernsec
Swift: Modernize the encryption queries
2 parents d6b53ab + 07cae40 commit 3baba70

24 files changed

+776
-384
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* Provides classes and predicates for reasoning about constant password
3+
* vulnerabilities.
4+
*/
5+
6+
import swift
7+
import codeql.swift.dataflow.DataFlow
8+
9+
/**
10+
* A dataflow sink for constant password vulnerabilities. That is,
11+
* a `DataFlow::Node` of something that is used as a password.
12+
*/
13+
abstract class ConstantPasswordSink extends DataFlow::Node { }
14+
15+
/**
16+
* A sanitizer for constant password vulnerabilities.
17+
*/
18+
abstract class ConstantPasswordSanitizer extends DataFlow::Node { }
19+
20+
/**
21+
* A unit class for adding additional taint steps.
22+
*/
23+
class ConstantPasswordAdditionalTaintStep extends Unit {
24+
/**
25+
* Holds if the step from `node1` to `node2` should be considered a taint
26+
* step for paths related to constant password vulnerabilities.
27+
*/
28+
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
29+
}
30+
31+
/**
32+
* A password sink for the CryptoSwift library.
33+
*/
34+
private class DefaultConstantPasswordSink extends ConstantPasswordSink {
35+
DefaultConstantPasswordSink() {
36+
// `password` arg in `init` is a sink
37+
exists(ClassOrStructDecl c, ConstructorDecl f, CallExpr call |
38+
c.getName() = ["HKDF", "PBKDF1", "PBKDF2", "Scrypt"] and
39+
c.getAMember() = f and
40+
call.getStaticTarget() = f and
41+
call.getArgumentWithLabel("password").getExpr() = this.asExpr()
42+
)
43+
}
44+
}
45+
46+
/**
47+
* A password sink for the RNCryptor library.
48+
*/
49+
private class RnCryptorPasswordSink extends ConstantPasswordSink {
50+
RnCryptorPasswordSink() {
51+
// RNCryptor (labelled arguments)
52+
exists(ClassOrStructDecl c, MethodDecl f, CallExpr call |
53+
c.getName() = ["RNCryptor", "RNEncryptor", "RNDecryptor"] and
54+
c.getAMember() = f and
55+
call.getStaticTarget() = f and
56+
call.getArgumentWithLabel(["password", "withPassword", "forPassword"]).getExpr() =
57+
this.asExpr()
58+
)
59+
or
60+
// RNCryptor (unlabelled arguments)
61+
exists(MethodDecl f, CallExpr call |
62+
f.hasQualifiedName("RNCryptor", "keyForPassword(_:salt:settings:)") and
63+
call.getStaticTarget() = f and
64+
call.getArgument(0).getExpr() = this.asExpr()
65+
)
66+
}
67+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Provides a taint tracking configuration to find constant password
3+
* vulnerabilities.
4+
*/
5+
6+
import swift
7+
import codeql.swift.dataflow.DataFlow
8+
import codeql.swift.dataflow.TaintTracking
9+
import codeql.swift.dataflow.FlowSteps
10+
import codeql.swift.security.ConstantPasswordExtensions
11+
12+
/**
13+
* A constant password is created through either a byte array or string literals.
14+
*/
15+
class ConstantPasswordSource extends Expr {
16+
ConstantPasswordSource() {
17+
this = any(ArrayExpr arr | arr.getType().getName() = "Array<UInt8>") or
18+
this instanceof StringLiteralExpr
19+
}
20+
}
21+
22+
/**
23+
* A taint configuration from the source of constants passwords to expressions that use
24+
* them to initialize password-based encryption keys.
25+
*/
26+
module ConstantPasswordConfig implements DataFlow::ConfigSig {
27+
predicate isSource(DataFlow::Node node) { node.asExpr() instanceof ConstantPasswordSource }
28+
29+
predicate isSink(DataFlow::Node node) { node instanceof ConstantPasswordSink }
30+
31+
predicate isBarrier(DataFlow::Node node) { node instanceof ConstantPasswordSanitizer }
32+
33+
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
34+
any(ConstantPasswordAdditionalTaintStep s).step(nodeFrom, nodeTo)
35+
}
36+
}
37+
38+
module ConstantPasswordFlow = TaintTracking::Global<ConstantPasswordConfig>;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Provides classes and predicates for reasoning about use of constant salts
3+
* for password hashing.
4+
*/
5+
6+
import swift
7+
import codeql.swift.dataflow.DataFlow
8+
9+
/**
10+
* A dataflow sink for constant salt vulnerabilities. That is,
11+
* a `DataFlow::Node` of something that is used as a salt.
12+
*/
13+
abstract class ConstantSaltSink extends DataFlow::Node { }
14+
15+
/**
16+
* A sanitizer for constant salt vulnerabilities.
17+
*/
18+
abstract class ConstantSaltSanitizer extends DataFlow::Node { }
19+
20+
/**
21+
* A unit class for adding additional taint steps.
22+
*/
23+
class ConstantSaltAdditionalTaintStep extends Unit {
24+
/**
25+
* Holds if the step from `node1` to `node2` should be considered a taint
26+
* step for paths related to constant salt vulnerabilities.
27+
*/
28+
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
29+
}
30+
31+
/**
32+
* A sink for the CryptoSwift library.
33+
*/
34+
private class CryptoSwiftSaltSink extends ConstantSaltSink {
35+
CryptoSwiftSaltSink() {
36+
// `salt` arg in `init` is a sink
37+
exists(ClassOrStructDecl c, ConstructorDecl f, CallExpr call |
38+
c.getName() = ["HKDF", "PBKDF1", "PBKDF2", "Scrypt"] and
39+
c.getAMember() = f and
40+
call.getStaticTarget() = f and
41+
call.getArgumentWithLabel("salt").getExpr() = this.asExpr()
42+
)
43+
}
44+
}
45+
46+
/**
47+
* A sink for the RNCryptor library.
48+
*/
49+
private class RnCryptorSaltSink extends ConstantSaltSink {
50+
RnCryptorSaltSink() {
51+
exists(ClassOrStructDecl c, MethodDecl f, CallExpr call |
52+
c.getName() = ["RNCryptor", "RNEncryptor", "RNDecryptor"] and
53+
c.getAMember() = f and
54+
call.getStaticTarget() = f and
55+
call.getArgumentWithLabel(["salt", "encryptionSalt", "hmacSalt", "HMACSalt"]).getExpr() =
56+
this.asExpr()
57+
)
58+
}
59+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Provides a taint tracking configuration to find use of constant salts
3+
* for password hashing.
4+
*/
5+
6+
import swift
7+
import codeql.swift.dataflow.DataFlow
8+
import codeql.swift.dataflow.TaintTracking
9+
import codeql.swift.dataflow.FlowSteps
10+
import codeql.swift.security.ConstantSaltExtensions
11+
12+
/**
13+
* A constant salt is created through either a byte array or string literals.
14+
*/
15+
class ConstantSaltSource extends Expr {
16+
ConstantSaltSource() {
17+
this = any(ArrayExpr arr | arr.getType().getName() = "Array<UInt8>") or
18+
this instanceof StringLiteralExpr or
19+
this instanceof NumberLiteralExpr
20+
}
21+
}
22+
23+
/**
24+
* A taint configuration from the source of constants salts to expressions that use
25+
* them to initialize password-based encryption keys.
26+
*/
27+
module ConstantSaltConfig implements DataFlow::ConfigSig {
28+
predicate isSource(DataFlow::Node node) { node.asExpr() instanceof ConstantSaltSource }
29+
30+
predicate isSink(DataFlow::Node node) { node instanceof ConstantSaltSink }
31+
32+
predicate isBarrier(DataFlow::Node node) { node instanceof ConstantSaltSanitizer }
33+
34+
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
35+
any(ConstantSaltAdditionalTaintStep s).step(nodeFrom, nodeTo)
36+
}
37+
}
38+
39+
module ConstantSaltFlow = TaintTracking::Global<ConstantSaltConfig>;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* Provides classes and predicates for reasoning about encryption using the
3+
* ECB encryption mode.
4+
*/
5+
6+
import swift
7+
import codeql.swift.dataflow.DataFlow
8+
9+
/**
10+
* A dataflow source for ECB encryption vulnerabilities. That is,
11+
* a `DataFlow::Node` of something that specifies a block mode
12+
* cipher.
13+
*/
14+
abstract class EcbEncryptionSource extends DataFlow::Node { }
15+
16+
/**
17+
* A dataflow sink for ECB encryption vulnerabilities. That is,
18+
* a `DataFlow::Node` of something that is used as the block mode
19+
* of a cipher.
20+
*/
21+
abstract class EcbEncryptionSink extends DataFlow::Node { }
22+
23+
/**
24+
* A sanitizer for ECB encryption vulnerabilities.
25+
*/
26+
abstract class EcbEncryptionSanitizer extends DataFlow::Node { }
27+
28+
/**
29+
* A unit class for adding additional taint steps.
30+
*/
31+
class EcbEncryptionAdditionalTaintStep extends Unit {
32+
/**
33+
* Holds if the step from `node1` to `node2` should be considered a taint
34+
* step for paths related to ECB encryption vulnerabilities.
35+
*/
36+
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
37+
}
38+
39+
/**
40+
* A block mode for the CryptoSwift library.
41+
*/
42+
private class CryptoSwiftEcb extends EcbEncryptionSource {
43+
CryptoSwiftEcb() {
44+
exists(CallExpr call |
45+
call.getStaticTarget().(MethodDecl).hasQualifiedName("ECB", "init()") and
46+
this.asExpr() = call
47+
)
48+
}
49+
}
50+
51+
/**
52+
* A block mode being used to form a CryptoSwift `AES` cipher.
53+
*/
54+
private class AES extends EcbEncryptionSink {
55+
AES() {
56+
// `blockMode` arg in `AES.init` is a sink
57+
exists(CallExpr call |
58+
call.getStaticTarget()
59+
.(MethodDecl)
60+
.hasQualifiedName("AES", ["init(key:blockMode:)", "init(key:blockMode:padding:)"]) and
61+
call.getArgument(1).getExpr() = this.asExpr()
62+
)
63+
}
64+
}
65+
66+
/**
67+
* A block mode being used to form a CryptoSwift `Blowfish` cipher.
68+
*/
69+
private class Blowfish extends EcbEncryptionSink {
70+
Blowfish() {
71+
// `blockMode` arg in `Blowfish.init` is a sink
72+
exists(CallExpr call |
73+
call.getStaticTarget()
74+
.(MethodDecl)
75+
.hasQualifiedName("Blowfish", "init(key:blockMode:padding:)") and
76+
call.getArgument(1).getExpr() = this.asExpr()
77+
)
78+
}
79+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Provides a taint tracking configuration to find encryption using the
3+
* ECB encryption mode.
4+
*/
5+
6+
import swift
7+
import codeql.swift.dataflow.DataFlow
8+
import codeql.swift.dataflow.TaintTracking
9+
import codeql.swift.security.ECBEncryptionExtensions
10+
11+
/**
12+
* A taint configuration from the constructor of ECB mode to expressions that use
13+
* it to initialize a cipher.
14+
*/
15+
module EcbEncryptionConfig implements DataFlow::ConfigSig {
16+
predicate isSource(DataFlow::Node node) { node instanceof EcbEncryptionSource }
17+
18+
predicate isSink(DataFlow::Node node) { node instanceof EcbEncryptionSink }
19+
20+
predicate isBarrier(DataFlow::Node node) { node instanceof EcbEncryptionSanitizer }
21+
22+
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
23+
any(EcbEncryptionAdditionalTaintStep s).step(nodeFrom, nodeTo)
24+
}
25+
}
26+
27+
module EcbEncryptionFlow = DataFlow::Global<EcbEncryptionConfig>;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Provides classes and predicates for reasoning about hard-coded encryption
3+
* key vulnerabilities.
4+
*/
5+
6+
import swift
7+
import codeql.swift.dataflow.DataFlow
8+
9+
/**
10+
* A dataflow sink for hard-coded encryption key vulnerabilities. That is,
11+
* a `DataFlow::Node` of something that is used as a key.
12+
*/
13+
abstract class HardcodedEncryptionKeySink extends DataFlow::Node { }
14+
15+
/**
16+
* A sanitizer for hard-coded encryption key vulnerabilities.
17+
*/
18+
abstract class HardcodedEncryptionKeySanitizer extends DataFlow::Node { }
19+
20+
/**
21+
* A unit class for adding additional taint steps.
22+
*/
23+
class HardcodedEncryptionKeyAdditionalTaintStep extends Unit {
24+
/**
25+
* Holds if the step from `node1` to `node2` should be considered a taint
26+
* step for paths related to hard-coded encryption key vulnerabilities.
27+
*/
28+
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
29+
}
30+
31+
/**
32+
* A sink for the CryptoSwift library.
33+
*/
34+
private class CryptoSwiftEncryptionKeySink extends HardcodedEncryptionKeySink {
35+
CryptoSwiftEncryptionKeySink() {
36+
// `key` arg in `init` is a sink
37+
exists(CallExpr call, string fName |
38+
call.getStaticTarget()
39+
.(MethodDecl)
40+
.hasQualifiedName([
41+
"AES", "HMAC", "ChaCha20", "CBCMAC", "CMAC", "Poly1305", "Blowfish", "Rabbit"
42+
], fName) and
43+
fName.matches("init(key:%") and
44+
call.getArgument(0).getExpr() = this.asExpr()
45+
)
46+
}
47+
}
48+
49+
/**
50+
* A sink for the RNCryptor library.
51+
*/
52+
private class RnCryptorEncryptionKeySink extends HardcodedEncryptionKeySink {
53+
RnCryptorEncryptionKeySink() {
54+
exists(ClassOrStructDecl c, MethodDecl f, CallExpr call |
55+
c.getFullName() = ["RNCryptor", "RNEncryptor", "RNDecryptor"] and
56+
c.getAMember() = f and
57+
call.getStaticTarget() = f and
58+
call.getArgumentWithLabel(["encryptionKey", "withEncryptionKey"]).getExpr() = this.asExpr()
59+
)
60+
}
61+
}

0 commit comments

Comments
 (0)