Skip to content

Commit f8b6106

Browse files
committed
have a crack at modelling argon2-cffi
1 parent 8981822 commit f8b6106

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import python
2+
import semmle.python.ApiGraphs
3+
import experimental.cryptography.CryptoArtifact
4+
import experimental.cryptography.CryptoAlgorithmNames
5+
import experimental.cryptography.utils.CallCfgNodeWithTarget
6+
private import experimental.cryptography.utils.Utils as Utils
7+
8+
/**
9+
* Provides models for the `argon2-cffi` PyPI package.
10+
* See https://argon2-cffi.readthedocs.io/en/stable/.
11+
*/
12+
private module Argon2Cffi {
13+
API::CallNode getAPasswordHasher() {
14+
result = API::moduleImport("argon2")
15+
.getMember("PasswordHasher")
16+
.getACall()
17+
}
18+
19+
DataFlow::LocalSourceNode getAnArgonType(string fullName, string member) {
20+
result = API::moduleImport("argon2")
21+
.getMember("low_level")
22+
.getMember("Type")
23+
.getMember(member)
24+
.asSource()
25+
and (
26+
(fullName = "ARGON2D" and member = "D")
27+
or
28+
(fullName = "ARGON2I" and member = "I")
29+
or
30+
(fullName = "ARGON2ID" and member = "ID")
31+
)
32+
}
33+
34+
class ArgonType extends DataFlow::LocalSourceNode {
35+
ArgonType() { this = getAnArgonType(_, _) }
36+
string getName() { this = getAnArgonType(result, _) }
37+
}
38+
39+
// note: password hashers instantiated using `from_parameters` are not modelled (yet)
40+
class PasswordHasher extends KeyDerivationAlgorithm {
41+
PasswordHasher() { this = getAPasswordHasher() }
42+
43+
API::Node getTypeParameter() {
44+
result = this.(API::CallNode).getParameter(6, "type")
45+
}
46+
47+
override string getName() {
48+
if exists(this.getTypeParameter()) then exists(
49+
ArgonType type | type.flowsTo(this.getTypeParameter().asSink()) | result = type.getName()
50+
)
51+
else result = "ARGON2ID"
52+
}
53+
}
54+
55+
abstract class PasswordHasherOperation extends KeyDerivationOperation {
56+
PasswordHasher instance;
57+
58+
PasswordHasherOperation() {
59+
this = instance.(API::CallNode).getAMethodCall(["hash", "verify"])
60+
}
61+
62+
override PasswordHasher getAlgorithm() { result = instance }
63+
64+
override DataFlow::Node getInitialisation() { result = instance }
65+
66+
override predicate requiresHash() { none() }
67+
68+
override predicate requiresMode() { none() }
69+
70+
// although Argon is salted, the salt parameter is optional as the library generates one for you
71+
override predicate requiresSalt() { none() }
72+
73+
override predicate requiresIteration() { none() }
74+
75+
override predicate requiresLanes() { none() }
76+
77+
override predicate requiresMemoryCost() { none() }
78+
79+
override DataFlow::Node getSaltConfigSink() {
80+
result = this.getSaltConfigSink(_)
81+
}
82+
83+
private DataFlow::Node getSaltConfigSink(API::Node apiNode) {
84+
result = apiNode.asSink() and
85+
apiNode = this.(API::CallNode).getKeywordParameter("salt")
86+
}
87+
88+
override DataFlow::Node getSaltConfigSrc() {
89+
result = this.getSaltConfigSrc(_)
90+
}
91+
92+
private DataFlow::Node getSaltConfigSrc(API::Node apiNode) {
93+
exists(this.getSaltConfigSink(apiNode)) and
94+
result = Utils::getUltimateSrcFromApiNode(apiNode)
95+
}
96+
97+
override DataFlow::Node getHashConfigSrc() { none() }
98+
99+
override DataFlow::Node getIterationSizeSrc() {
100+
result = Utils::getUltimateSrcFromApiNode(instance.(API::CallNode).getParameter(0, "time_cost"))
101+
}
102+
103+
override DataFlow::Node getMemoryCostConfigSrc() {
104+
result = Utils::getUltimateSrcFromApiNode(instance.(API::CallNode).getParameter(1, "memory_cost"))
105+
}
106+
107+
override DataFlow::Node getLanesConfigSrc() {
108+
result = Utils::getUltimateSrcFromApiNode(instance.(API::CallNode).getParameter(2, "parallelism"))
109+
}
110+
111+
override DataFlow::Node getDerivedKeySizeSrc() {
112+
result = Utils::getUltimateSrcFromApiNode(instance.(API::CallNode).getParameter(3, "hash_len"))
113+
}
114+
115+
override DataFlow::Node getModeSrc() { none() }
116+
}
117+
118+
class HashOperation extends PasswordHasherOperation {
119+
HashOperation() {
120+
this = instance.(API::CallNode).getAMethodCall("hash")
121+
}
122+
123+
override DataFlow::Node getAnInput() {
124+
result = this.(API::CallNode).getArg(0) or result = this.(API::CallNode).getArgByName("password")
125+
}
126+
}
127+
128+
class VerifyOperation extends PasswordHasherOperation {
129+
VerifyOperation() {
130+
this = instance.(API::CallNode).getAMethodCall("verify")
131+
}
132+
133+
override DataFlow::Node getAnInput() {
134+
result = this.(API::CallNode).getArg(1) or result = this.(API::CallNode).getArgByName("password")
135+
}
136+
}
137+
}

0 commit comments

Comments
 (0)