Skip to content

Commit 07e3421

Browse files
committed
Rust: Add shared ConceptsShared.qll, CryptoAlgorithms.qll and CryptoAlgorithmNames.qll to Rust.
1 parent 6c4e0a9 commit 07e3421

File tree

5 files changed

+395
-3
lines changed

5 files changed

+395
-3
lines changed

config/identical-files.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,12 +288,14 @@
288288
"CryptoAlgorithms Python/JS/Ruby": [
289289
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
290290
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll",
291-
"ruby/ql/lib/codeql/ruby/security/CryptoAlgorithms.qll"
291+
"ruby/ql/lib/codeql/ruby/security/CryptoAlgorithms.qll",
292+
"rust/ql/lib/codeql/rust/security/CryptoAlgorithms.qll"
292293
],
293294
"CryptoAlgorithmNames Python/JS/Ruby": [
294295
"javascript/ql/lib/semmle/javascript/security/internal/CryptoAlgorithmNames.qll",
295296
"python/ql/lib/semmle/python/concepts/internal/CryptoAlgorithmNames.qll",
296-
"ruby/ql/lib/codeql/ruby/security/internal/CryptoAlgorithmNames.qll"
297+
"ruby/ql/lib/codeql/ruby/security/internal/CryptoAlgorithmNames.qll",
298+
"rust/ql/lib/codeql/rust/security/internal/CryptoAlgorithmNames.qll"
297299
],
298300
"SensitiveDataHeuristics Python/JS": [
299301
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
@@ -308,7 +310,8 @@
308310
"Concepts Python/Ruby/JS": [
309311
"python/ql/lib/semmle/python/internal/ConceptsShared.qll",
310312
"ruby/ql/lib/codeql/ruby/internal/ConceptsShared.qll",
311-
"javascript/ql/lib/semmle/javascript/internal/ConceptsShared.qll"
313+
"javascript/ql/lib/semmle/javascript/internal/ConceptsShared.qll",
314+
"rust/ql/lib/codeql/rust/internal/ConceptsShared.qll"
312315
],
313316
"ApiGraphModels": [
314317
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll",
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* This file contains imports required for the Rust version of `ConceptsShared.qll`.
3+
* Since they are language-specific, they can't be placed directly in that file, as it is shared between languages.
4+
*/
5+
6+
import codeql.rust.dataflow.DataFlow::DataFlow as DataFlow
7+
import codeql.rust.security.CryptoAlgorithms as CryptoAlgorithms
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/**
2+
* Provides Concepts which are shared across languages.
3+
*
4+
* Each language has a language specific `Concepts.qll` file that can import the
5+
* shared concepts from this file. A language can either re-export the concept directly,
6+
* or can add additional member-predicates that are needed for that language.
7+
*
8+
* Moving forward, `Concepts.qll` will be the staging ground for brand new concepts from
9+
* each language, but we will maintain a discipline of moving those concepts to
10+
* `ConceptsShared.qll` ASAP.
11+
*/
12+
13+
private import ConceptsImports
14+
15+
/**
16+
* Provides models for cryptographic concepts.
17+
*
18+
* Note: The `CryptographicAlgorithm` class currently doesn't take weak keys into
19+
* consideration for the `isWeak` member predicate. So RSA is always considered
20+
* secure, although using a low number of bits will actually make it insecure. We plan
21+
* to improve our libraries in the future to more precisely capture this aspect.
22+
*/
23+
module Cryptography {
24+
class CryptographicAlgorithm = CryptoAlgorithms::CryptographicAlgorithm;
25+
26+
class EncryptionAlgorithm = CryptoAlgorithms::EncryptionAlgorithm;
27+
28+
class HashingAlgorithm = CryptoAlgorithms::HashingAlgorithm;
29+
30+
class PasswordHashingAlgorithm = CryptoAlgorithms::PasswordHashingAlgorithm;
31+
32+
/**
33+
* A data-flow node that is an application of a cryptographic algorithm. For example,
34+
* encryption, decryption, signature-validation.
35+
*
36+
* Extend this class to refine existing API models. If you want to model new APIs,
37+
* extend `CryptographicOperation::Range` instead.
38+
*/
39+
class CryptographicOperation extends DataFlow::Node instanceof CryptographicOperation::Range {
40+
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
41+
CryptographicAlgorithm getAlgorithm() { result = super.getAlgorithm() }
42+
43+
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
44+
DataFlow::Node getInitialization() { result = super.getInitialization() }
45+
46+
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
47+
DataFlow::Node getAnInput() { result = super.getAnInput() }
48+
49+
/**
50+
* Gets the block mode used to perform this cryptographic operation.
51+
*
52+
* This predicate is only expected to have a result if two conditions hold:
53+
* 1. The operation is an encryption operation, i.e. the algorithm used is an `EncryptionAlgorithm`, and
54+
* 2. The algorithm used is a block cipher (not a stream cipher).
55+
*
56+
* If either of these conditions do not hold, then this predicate should have no result.
57+
*/
58+
BlockMode getBlockMode() { result = super.getBlockMode() }
59+
}
60+
61+
/** Provides classes for modeling new applications of a cryptographic algorithms. */
62+
module CryptographicOperation {
63+
/**
64+
* A data-flow node that is an application of a cryptographic algorithm. For example,
65+
* encryption, decryption, signature-validation.
66+
*
67+
* Extend this class to model new APIs. If you want to refine existing API models,
68+
* extend `CryptographicOperation` instead.
69+
*/
70+
abstract class Range extends DataFlow::Node {
71+
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
72+
abstract DataFlow::Node getInitialization();
73+
74+
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
75+
abstract CryptographicAlgorithm getAlgorithm();
76+
77+
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
78+
abstract DataFlow::Node getAnInput();
79+
80+
/**
81+
* Gets the block mode used to perform this cryptographic operation.
82+
*
83+
* This predicate is only expected to have a result if two conditions hold:
84+
* 1. The operation is an encryption operation, i.e. the algorithm used is an `EncryptionAlgorithm`, and
85+
* 2. The algorithm used is a block cipher (not a stream cipher).
86+
*
87+
* If either of these conditions do not hold, then this predicate should have no result.
88+
*/
89+
abstract BlockMode getBlockMode();
90+
}
91+
}
92+
93+
/**
94+
* A cryptographic block cipher mode of operation. This can be used to encrypt
95+
* data of arbitrary length using a block encryption algorithm.
96+
*/
97+
class BlockMode extends string {
98+
BlockMode() {
99+
this =
100+
[
101+
"ECB", "CBC", "GCM", "CCM", "CFB", "OFB", "CTR", "OPENPGP",
102+
"XTS", // https://csrc.nist.gov/publications/detail/sp/800-38e/final
103+
"EAX" // https://en.wikipedia.org/wiki/EAX_mode
104+
]
105+
}
106+
107+
/** Holds if this block mode is considered to be insecure. */
108+
predicate isWeak() { this = "ECB" }
109+
110+
/** Holds if the given string appears to match this block mode. */
111+
bindingset[s]
112+
predicate matchesString(string s) { s.toUpperCase().matches("%" + this + "%") }
113+
}
114+
}
115+
116+
/** Provides classes for modeling HTTP-related APIs. */
117+
module Http {
118+
/** Provides classes for modeling HTTP clients. */
119+
module Client {
120+
/**
121+
* A data-flow node that makes an outgoing HTTP request.
122+
*
123+
* Extend this class to refine existing API models. If you want to model new APIs,
124+
* extend `Http::Client::Request::Range` instead.
125+
*/
126+
class Request extends DataFlow::Node instanceof Request::Range {
127+
/**
128+
* Gets a data-flow node that contributes to the URL of the request.
129+
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
130+
*/
131+
DataFlow::Node getAUrlPart() { result = super.getAUrlPart() }
132+
133+
/** Gets a string that identifies the framework used for this request. */
134+
string getFramework() { result = super.getFramework() }
135+
136+
/**
137+
* Holds if this request is made using a mode that disables SSL/TLS
138+
* certificate validation, where `disablingNode` represents the point at
139+
* which the validation was disabled, and `argumentOrigin` represents the origin
140+
* of the argument that disabled the validation (which could be the same node as
141+
* `disablingNode`).
142+
*/
143+
predicate disablesCertificateValidation(
144+
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
145+
) {
146+
super.disablesCertificateValidation(disablingNode, argumentOrigin)
147+
}
148+
}
149+
150+
/** Provides a class for modeling new HTTP requests. */
151+
module Request {
152+
/**
153+
* A data-flow node that makes an outgoing HTTP request.
154+
*
155+
* Extend this class to model new APIs. If you want to refine existing API models,
156+
* extend `Http::Client::Request` instead.
157+
*/
158+
abstract class Range extends DataFlow::Node {
159+
/**
160+
* Gets a data-flow node that contributes to the URL of the request.
161+
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
162+
*/
163+
abstract DataFlow::Node getAUrlPart();
164+
165+
/** Gets a string that identifies the framework used for this request. */
166+
abstract string getFramework();
167+
168+
/**
169+
* Holds if this request is made using a mode that disables SSL/TLS
170+
* certificate validation, where `disablingNode` represents the point at
171+
* which the validation was disabled, and `argumentOrigin` represents the origin
172+
* of the argument that disabled the validation (which could be the same node as
173+
* `disablingNode`).
174+
*/
175+
abstract predicate disablesCertificateValidation(
176+
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
177+
);
178+
}
179+
}
180+
}
181+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* Provides classes modeling cryptographic algorithms, separated into strong and weak variants.
3+
*
4+
* The classification into strong and weak are based on Wikipedia, OWASP and Google (2021).
5+
*/
6+
7+
private import internal.CryptoAlgorithmNames
8+
9+
/**
10+
* A cryptographic algorithm.
11+
*/
12+
private newtype TCryptographicAlgorithm =
13+
MkHashingAlgorithm(string name, boolean isWeak) {
14+
isStrongHashingAlgorithm(name) and isWeak = false
15+
or
16+
isWeakHashingAlgorithm(name) and isWeak = true
17+
} or
18+
MkEncryptionAlgorithm(string name, boolean isWeak) {
19+
isStrongEncryptionAlgorithm(name) and isWeak = false
20+
or
21+
isWeakEncryptionAlgorithm(name) and isWeak = true
22+
} or
23+
MkPasswordHashingAlgorithm(string name, boolean isWeak) {
24+
isStrongPasswordHashingAlgorithm(name) and isWeak = false
25+
or
26+
isWeakPasswordHashingAlgorithm(name) and isWeak = true
27+
}
28+
29+
/**
30+
* Gets the most specific `CryptographicAlgorithm` that matches the given `name`.
31+
* A matching algorithm is one where the name of the algorithm matches the start of name, with allowances made for different name formats.
32+
* In the case that multiple `CryptographicAlgorithm`s match the given `name`, the algorithm(s) with the longest name will be selected. This is intended to select more specific versions of algorithms when multiple versions could match - for example "SHA3_224" matches against both "SHA3" and "SHA3224", but the latter is a more precise match.
33+
*/
34+
bindingset[name]
35+
private CryptographicAlgorithm getBestAlgorithmForName(string name) {
36+
result =
37+
max(CryptographicAlgorithm algorithm |
38+
algorithm.getName() =
39+
[
40+
name.toUpperCase(), // the full name
41+
name.toUpperCase().regexpCapture("^([\\w]+)(?:-.*)?$", 1), // the name prior to any dashes or spaces
42+
name.toUpperCase().regexpCapture("^([A-Z0-9]+)(?:(-|_).*)?$", 1) // the name prior to any dashes, spaces, or underscores
43+
].regexpReplaceAll("[-_ ]", "") // strip dashes, underscores, and spaces
44+
|
45+
algorithm order by algorithm.getName().length()
46+
)
47+
}
48+
49+
/**
50+
* A cryptographic algorithm.
51+
*/
52+
abstract class CryptographicAlgorithm extends TCryptographicAlgorithm {
53+
/** Gets a textual representation of this element. */
54+
string toString() { result = this.getName() }
55+
56+
/**
57+
* Gets the normalized name of this algorithm (upper-case, no spaces, dashes or underscores).
58+
*/
59+
abstract string getName();
60+
61+
/**
62+
* Holds if the name of this algorithm is the most specific match for `name`.
63+
* This predicate matches quite liberally to account for different ways of formatting algorithm names, e.g. using dashes, underscores, or spaces as separators, including or not including block modes of operation, etc.
64+
*/
65+
bindingset[name]
66+
predicate matchesName(string name) { this = getBestAlgorithmForName(name) }
67+
68+
/**
69+
* Holds if this algorithm is weak.
70+
*/
71+
abstract predicate isWeak();
72+
}
73+
74+
/**
75+
* A hashing algorithm such as `MD5` or `SHA512`.
76+
*/
77+
class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm {
78+
string name;
79+
boolean isWeak;
80+
81+
HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) }
82+
83+
override string getName() { result = name }
84+
85+
override predicate isWeak() { isWeak = true }
86+
}
87+
88+
/**
89+
* An encryption algorithm such as `DES` or `AES512`.
90+
*/
91+
class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm {
92+
string name;
93+
boolean isWeak;
94+
95+
EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) }
96+
97+
override string getName() { result = name }
98+
99+
override predicate isWeak() { isWeak = true }
100+
101+
/** Holds if this algorithm is a stream cipher. */
102+
predicate isStreamCipher() { isStreamCipher(name) }
103+
}
104+
105+
/**
106+
* A password hashing algorithm such as `PBKDF2` or `SCRYPT`.
107+
*/
108+
class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm {
109+
string name;
110+
boolean isWeak;
111+
112+
PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) }
113+
114+
override string getName() { result = name }
115+
116+
override predicate isWeak() { isWeak = true }
117+
}

0 commit comments

Comments
 (0)