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