Skip to content

Commit 2a8f720

Browse files
committed
Python: Port cryptodome models to use API graphs
1 parent 1eabfbd commit 2a8f720

File tree

1 file changed

+27
-280
lines changed

1 file changed

+27
-280
lines changed

python/ql/src/semmle/python/frameworks/Cryptodome.qll

Lines changed: 27 additions & 280 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
private import python
99
private import semmle.python.dataflow.new.DataFlow
1010
private import semmle.python.Concepts
11+
private import semmle.python.ApiGraphs
1112

1213
/**
1314
* Provides models for
@@ -16,286 +17,25 @@ private import semmle.python.Concepts
1617
* See https://pycryptodome.readthedocs.io/en/latest/
1718
*/
1819
private module CryptodomeModel {
19-
// ---------------------------------------------------------------------------
20-
// Cryptodome
21-
// ---------------------------------------------------------------------------
22-
/** Gets a reference to the `Cryptodome`/`Crypto` module. */
23-
private DataFlow::Node cryptodome(DataFlow::TypeTracker t) {
24-
t.start() and
25-
result = DataFlow::importNode(["Cryptodome", "Crypto"])
26-
or
27-
exists(DataFlow::TypeTracker t2 | result = cryptodome(t2).track(t2, t))
28-
}
29-
30-
/** Gets a reference to the `Cryptodome`/`Crypto` module. */
31-
DataFlow::Node cryptodome() { result = cryptodome(DataFlow::TypeTracker::end()) }
32-
33-
/** Provides models for the `Cryptodome`/`Crypto` module. */
34-
module Cryptodome {
35-
/**
36-
* Gets a reference to the attribute `attr_name` of the `Cryptodome`/`Crypto` module.
37-
* WARNING: Only holds for a few predefined attributes.
38-
*/
39-
private DataFlow::Node cryptodome_attr(DataFlow::TypeTracker t, string attr_name) {
40-
attr_name in ["PublicKey"] and
41-
(
42-
t.start() and
43-
result = DataFlow::importNode(["Cryptodome", "Crypto"] + "." + attr_name)
44-
or
45-
t.startInAttr(attr_name) and
46-
result = cryptodome()
47-
)
48-
or
49-
// Due to bad performance when using normal setup with `cryptodome_attr(t2, attr_name).track(t2, t)`
50-
// we have inlined that code and forced a join
51-
exists(DataFlow::TypeTracker t2 |
52-
exists(DataFlow::StepSummary summary |
53-
cryptodome_attr_first_join(t2, attr_name, result, summary) and
54-
t = t2.append(summary)
55-
)
56-
)
57-
}
58-
59-
pragma[nomagic]
60-
private predicate cryptodome_attr_first_join(
61-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
62-
) {
63-
DataFlow::StepSummary::step(cryptodome_attr(t2, attr_name), res, summary)
64-
}
65-
66-
/**
67-
* Gets a reference to the attribute `attr_name` of the `Cryptodome`/`Crypto` module.
68-
* WARNING: Only holds for a few predefined attributes.
69-
*/
70-
private DataFlow::Node cryptodome_attr(string attr_name) {
71-
result = cryptodome_attr(DataFlow::TypeTracker::end(), attr_name)
72-
}
73-
74-
// -------------------------------------------------------------------------
75-
// Cryptodome.PublicKey
76-
// -------------------------------------------------------------------------
77-
/** Gets a reference to the `Cryptodome.PublicKey`/`Crypto.PublicKey` module. */
78-
DataFlow::Node publicKey() { result = cryptodome_attr("PublicKey") }
79-
80-
/** Provides models for the `Cryptodome.PublicKey`/`Crypto.PublicKey` module */
81-
module PublicKey {
82-
/**
83-
* Gets a reference to the attribute `attr_name` of the `Cryptodome.PublicKey`/`Crypto.PublicKey` module.
84-
* WARNING: Only holds for a few predefined attributes.
85-
*/
86-
private DataFlow::Node publicKey_attr(DataFlow::TypeTracker t, string attr_name) {
87-
attr_name in ["RSA", "DSA", "ECC"] and
88-
(
89-
t.start() and
90-
result = DataFlow::importNode(["Cryptodome", "Crypto"] + ".PublicKey" + "." + attr_name)
91-
or
92-
t.startInAttr(attr_name) and
93-
result = publicKey()
94-
)
95-
or
96-
// Due to bad performance when using normal setup with `publicKey_attr(t2, attr_name).track(t2, t)`
97-
// we have inlined that code and forced a join
98-
exists(DataFlow::TypeTracker t2 |
99-
exists(DataFlow::StepSummary summary |
100-
publicKey_attr_first_join(t2, attr_name, result, summary) and
101-
t = t2.append(summary)
102-
)
103-
)
104-
}
105-
106-
pragma[nomagic]
107-
private predicate publicKey_attr_first_join(
108-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
109-
DataFlow::StepSummary summary
110-
) {
111-
DataFlow::StepSummary::step(publicKey_attr(t2, attr_name), res, summary)
112-
}
113-
114-
/**
115-
* Gets a reference to the attribute `attr_name` of the `Cryptodome.PublicKey`/`Crypto.PublicKey` module.
116-
* WARNING: Only holds for a few predefined attributes.
117-
*/
118-
private DataFlow::Node publicKey_attr(string attr_name) {
119-
result = publicKey_attr(DataFlow::TypeTracker::end(), attr_name)
120-
}
121-
122-
// -------------------------------------------------------------------------
123-
// Cryptodome.PublicKey.RSA
124-
// -------------------------------------------------------------------------
125-
/** Gets a reference to the `Cryptodome.PublicKey.RSA`/`Crypto.PublicKey.RSA` module. */
126-
DataFlow::Node rsa() { result = publicKey_attr("RSA") }
127-
128-
/** Provides models for the `Cryptodome.PublicKey.RSA`/`Crypto.PublicKey.RSA` module */
129-
module RSA {
130-
/**
131-
* Gets a reference to the attribute `attr_name` of the `Cryptodome.PublicKey.RSA`/`Crypto.PublicKey.RSA` module.
132-
* WARNING: Only holds for a few predefined attributes.
133-
*/
134-
private DataFlow::Node rsa_attr(DataFlow::TypeTracker t, string attr_name) {
135-
attr_name in ["generate"] and
136-
(
137-
t.start() and
138-
result =
139-
DataFlow::importNode(["Cryptodome", "Crypto"] + ".PublicKey.RSA" + "." + attr_name)
140-
or
141-
t.startInAttr(attr_name) and
142-
result = rsa()
143-
)
144-
or
145-
// Due to bad performance when using normal setup with `rsa_attr(t2, attr_name).track(t2, t)`
146-
// we have inlined that code and forced a join
147-
exists(DataFlow::TypeTracker t2 |
148-
exists(DataFlow::StepSummary summary |
149-
rsa_attr_first_join(t2, attr_name, result, summary) and
150-
t = t2.append(summary)
151-
)
152-
)
153-
}
154-
155-
pragma[nomagic]
156-
private predicate rsa_attr_first_join(
157-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
158-
DataFlow::StepSummary summary
159-
) {
160-
DataFlow::StepSummary::step(rsa_attr(t2, attr_name), res, summary)
161-
}
162-
163-
/**
164-
* Gets a reference to the attribute `attr_name` of the `Cryptodome.PublicKey.RSA`/`Crypto.PublicKey.RSA` module.
165-
* WARNING: Only holds for a few predefined attributes.
166-
*/
167-
private DataFlow::Node rsa_attr(string attr_name) {
168-
result = rsa_attr(DataFlow::TypeTracker::end(), attr_name)
169-
}
170-
171-
/** Gets a reference to the `Cryptodome.PublicKey.RSA.generate`/`Crypto.PublicKey.RSA.generate` function. */
172-
DataFlow::Node generate() { result = rsa_attr("generate") }
173-
}
174-
175-
// -------------------------------------------------------------------------
176-
// Cryptodome.PublicKey.DSA
177-
// -------------------------------------------------------------------------
178-
/** Gets a reference to the `Cryptodome.PublicKey.DSA`/`Crypto.PublicKey.DSA` module. */
179-
DataFlow::Node dsa() { result = publicKey_attr("DSA") }
180-
181-
/** Provides models for the `Cryptodome.PublicKey.DSA`/`Crypto.PublicKey.DSA` module */
182-
module DSA {
183-
/**
184-
* Gets a reference to the attribute `attr_name` of the `Cryptodome.PublicKey.DSA`/`Crypto.PublicKey.DSA` module.
185-
* WARNING: Only holds for a few predefined attributes.
186-
*/
187-
private DataFlow::Node dsa_attr(DataFlow::TypeTracker t, string attr_name) {
188-
attr_name in ["generate"] and
189-
(
190-
t.start() and
191-
result =
192-
DataFlow::importNode(["Cryptodome", "Crypto"] + ".PublicKey.DSA" + "." + attr_name)
193-
or
194-
t.startInAttr(attr_name) and
195-
result = dsa()
196-
)
197-
or
198-
// Due to bad performance when using normal setup with `dsa_attr(t2, attr_name).track(t2, t)`
199-
// we have inlined that code and forced a join
200-
exists(DataFlow::TypeTracker t2 |
201-
exists(DataFlow::StepSummary summary |
202-
dsa_attr_first_join(t2, attr_name, result, summary) and
203-
t = t2.append(summary)
204-
)
205-
)
206-
}
207-
208-
pragma[nomagic]
209-
private predicate dsa_attr_first_join(
210-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
211-
DataFlow::StepSummary summary
212-
) {
213-
DataFlow::StepSummary::step(dsa_attr(t2, attr_name), res, summary)
214-
}
215-
216-
/**
217-
* Gets a reference to the attribute `attr_name` of the `Cryptodome.PublicKey.DSA`/`Crypto.PublicKey.DSA` module.
218-
* WARNING: Only holds for a few predefined attributes.
219-
*/
220-
private DataFlow::Node dsa_attr(string attr_name) {
221-
result = dsa_attr(DataFlow::TypeTracker::end(), attr_name)
222-
}
223-
224-
/** Gets a reference to the `Cryptodome.PublicKey.DSA.generate`/`Crypto.PublicKey.DSA.generate` function. */
225-
DataFlow::Node generate() { result = dsa_attr("generate") }
226-
}
227-
228-
// -------------------------------------------------------------------------
229-
// Cryptodome.PublicKey.ECC
230-
// -------------------------------------------------------------------------
231-
/** Gets a reference to the `Cryptodome.PublicKey.ECC`/`Crypto.PublicKey.ECC` module. */
232-
DataFlow::Node ecc() { result = publicKey_attr("ECC") }
233-
234-
/** Provides models for the `Cryptodome.PublicKey.ECC`/`Crypto.PublicKey.ECC` module */
235-
module ECC {
236-
/**
237-
* Gets a reference to the attribute `attr_name` of the `Cryptodome.PublicKey.ECC`/`Crypto.PublicKey.ECC` module.
238-
* WARNING: Only holds for a few predefined attributes.
239-
*/
240-
private DataFlow::Node ecc_attr(DataFlow::TypeTracker t, string attr_name) {
241-
attr_name in ["generate"] and
242-
(
243-
t.start() and
244-
result =
245-
DataFlow::importNode(["Cryptodome", "Crypto"] + ".PublicKey.ECC" + "." + attr_name)
246-
or
247-
t.startInAttr(attr_name) and
248-
result = ecc()
249-
)
250-
or
251-
// Due to bad performance when using normal setup with `ecc_attr(t2, attr_name).track(t2, t)`
252-
// we have inlined that code and forced a join
253-
exists(DataFlow::TypeTracker t2 |
254-
exists(DataFlow::StepSummary summary |
255-
ecc_attr_first_join(t2, attr_name, result, summary) and
256-
t = t2.append(summary)
257-
)
258-
)
259-
}
260-
261-
pragma[nomagic]
262-
private predicate ecc_attr_first_join(
263-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
264-
DataFlow::StepSummary summary
265-
) {
266-
DataFlow::StepSummary::step(ecc_attr(t2, attr_name), res, summary)
267-
}
268-
269-
/**
270-
* Gets a reference to the attribute `attr_name` of the `Cryptodome.PublicKey.ECC`/`Crypto.PublicKey.ECC` module.
271-
* WARNING: Only holds for a few predefined attributes.
272-
*/
273-
private DataFlow::Node ecc_attr(string attr_name) {
274-
result = ecc_attr(DataFlow::TypeTracker::end(), attr_name)
275-
}
276-
277-
/** Gets a reference to the `Cryptodome.PublicKey.ECC.generate`/`Crypto.PublicKey.ECC.generate` function. */
278-
DataFlow::Node generate() { result = ecc_attr("generate") }
279-
}
280-
}
281-
}
282-
28320
// ---------------------------------------------------------------------------
28421
/**
28522
* A call to `Cryptodome.PublicKey.RSA.generate`/`Crypto.PublicKey.RSA.generate`
28623
*
28724
* See https://pycryptodome.readthedocs.io/en/latest/src/public_key/rsa.html#Crypto.PublicKey.RSA.generate
28825
*/
28926
class CryptodomePublicKeyRsaGenerateCall extends Cryptography::PublicKey::KeyGeneration::RsaRange,
290-
DataFlow::CfgNode {
291-
override CallNode node;
292-
27+
DataFlow::CallCfgNode {
29328
CryptodomePublicKeyRsaGenerateCall() {
294-
node.getFunction() = Cryptodome::PublicKey::RSA::generate().asCfgNode()
29+
this =
30+
API::moduleImport(["Crypto", "Cryptodome"])
31+
.getMember("PublicKey")
32+
.getMember("RSA")
33+
.getMember("generate")
34+
.getACall()
29535
}
29636

29737
override DataFlow::Node getKeySizeArg() {
298-
result.asCfgNode() in [node.getArg(0), node.getArgByName("bits")]
38+
result in [this.getArg(0), this.getArgByName("bits")]
29939
}
30040
}
30141

@@ -305,15 +45,18 @@ private module CryptodomeModel {
30545
* See https://pycryptodome.readthedocs.io/en/latest/src/public_key/dsa.html#Crypto.PublicKey.DSA.generate
30646
*/
30747
class CryptodomePublicKeyDsaGenerateCall extends Cryptography::PublicKey::KeyGeneration::DsaRange,
308-
DataFlow::CfgNode {
309-
override CallNode node;
310-
48+
DataFlow::CallCfgNode {
31149
CryptodomePublicKeyDsaGenerateCall() {
312-
node.getFunction() = Cryptodome::PublicKey::DSA::generate().asCfgNode()
50+
this =
51+
API::moduleImport(["Crypto", "Cryptodome"])
52+
.getMember("PublicKey")
53+
.getMember("DSA")
54+
.getMember("generate")
55+
.getACall()
31356
}
31457

31558
override DataFlow::Node getKeySizeArg() {
316-
result.asCfgNode() in [node.getArg(0), node.getArgByName("bits")]
59+
result in [this.getArg(0), this.getArgByName("bits")]
31760
}
31861
}
31962

@@ -323,16 +66,20 @@ private module CryptodomeModel {
32366
* See https://pycryptodome.readthedocs.io/en/latest/src/public_key/ecc.html#Crypto.PublicKey.ECC.generate
32467
*/
32568
class CryptodomePublicKeyEccGenerateCall extends Cryptography::PublicKey::KeyGeneration::EccRange,
326-
DataFlow::CfgNode {
327-
override CallNode node;
328-
69+
DataFlow::CallCfgNode {
32970
CryptodomePublicKeyEccGenerateCall() {
330-
node.getFunction() = Cryptodome::PublicKey::ECC::generate().asCfgNode()
71+
this =
72+
API::moduleImport(["Crypto", "Cryptodome"])
73+
.getMember("PublicKey")
74+
.getMember("ECC")
75+
.getMember("generate")
76+
.getACall()
33177
}
33278

33379
/** Gets the argument that specifies the curve to use (a string). */
334-
DataFlow::Node getCurveArg() { result.asCfgNode() in [node.getArgByName("curve")] }
80+
DataFlow::Node getCurveArg() { result in [this.getArgByName("curve")] }
33581

82+
/** Gets the name of the curve to use, as well as the origin that explains how we obtained this name. */
33683
string getCurveWithOrigin(DataFlow::Node origin) {
33784
exists(StrConst str | origin = DataFlow::exprNode(str) |
33885
origin.(DataFlow::LocalSourceNode).flowsTo(this.getCurveArg()) and
@@ -341,7 +88,7 @@ private module CryptodomeModel {
34188
}
34289

34390
override int getKeySizeWithOrigin(DataFlow::Node origin) {
344-
exists(string curve | curve = getCurveWithOrigin(origin) |
91+
exists(string curve | curve = this.getCurveWithOrigin(origin) |
34592
// using list from https://pycryptodome.readthedocs.io/en/latest/src/public_key/ecc.html
34693
curve in ["NIST P-256", "p256", "P-256", "prime256v1", "secp256r1"] and result = 256
34794
or

0 commit comments

Comments
 (0)