Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 09d4736

Browse files
committed
Working refactor for cipher, padding, block mode. Still haven't completed connecting padding to algorithm instances if through a set padding interface.
1 parent 7481de7 commit 09d4736

16 files changed

Lines changed: 1205 additions & 131 deletions
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import cpp
2+
import semmle.code.cpp.dataflow.new.DataFlow
3+
import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants
4+
import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers // import all known alg value consummers
5+
6+
/**
7+
* Traces 'known algorithms' to AVCs, specifically
8+
* algorithms that are in the set of known algorithm constants.
9+
* Padding-specific consumers exist that have their own values that
10+
* overlap with the known algorithm constants.
11+
* Padding consumers (specific padding consumers) are excluded from the set of sinks.
12+
*/
13+
module KnownOpenSSLAlgorithmToAlgorithmValueConsumerConfig implements DataFlow::ConfigSig {
14+
predicate isSource(DataFlow::Node source) {
15+
source.asExpr() instanceof KnownOpenSSLAlgorithmConstant
16+
}
17+
18+
predicate isSink(DataFlow::Node sink) {
19+
exists(OpenSSLAlgorithmValueConsumer c |
20+
c.getInputNode() = sink and
21+
not c instanceof PaddingAlgorithmValueConsumer
22+
)
23+
}
24+
25+
predicate isBarrier(DataFlow::Node node) {
26+
// False positive reducer, don't flow out through argv
27+
exists(VariableAccess va, Variable v |
28+
v.getAnAccess() = va and va = node.asExpr()
29+
or
30+
va = node.asIndirectExpr()
31+
|
32+
v.getName().matches("%argv")
33+
)
34+
}
35+
36+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
37+
knownPassThroughStep(node1, node2)
38+
}
39+
}
40+
41+
module KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow =
42+
DataFlow::Global<KnownOpenSSLAlgorithmToAlgorithmValueConsumerConfig>;
43+
44+
module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig implements DataFlow::ConfigSig {
45+
predicate isSource(DataFlow::Node source) {
46+
source.asExpr() instanceof KnownOpenSSLAlgorithmConstant
47+
}
48+
49+
predicate isSink(DataFlow::Node sink) {
50+
exists(PaddingAlgorithmValueConsumer c | c.getInputNode() = sink)
51+
}
52+
53+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
54+
knownPassThroughStep(node1, node2)
55+
}
56+
}
57+
58+
module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow =
59+
DataFlow::Global<RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig>;
60+
61+
class OpenSSLAlgorithmAdditionalFlowStep extends AdditionalFlowInputStep {
62+
OpenSSLAlgorithmAdditionalFlowStep() { exists(AlgorithmPassthroughCall c | c.getInNode() = this) }
63+
64+
override DataFlow::Node getOutput() {
65+
exists(AlgorithmPassthroughCall c | c.getInNode() = this and c.getOutNode() = result)
66+
}
67+
}
68+
69+
abstract class AlgorithmPassthroughCall extends Call {
70+
abstract DataFlow::Node getInNode();
71+
72+
abstract DataFlow::Node getOutNode();
73+
}
74+
75+
class CopyAndDupAlgorithmPassthroughCall extends AlgorithmPassthroughCall {
76+
DataFlow::Node inNode;
77+
DataFlow::Node outNode;
78+
79+
CopyAndDupAlgorithmPassthroughCall() {
80+
// Flow out through any return or other argument of the same type
81+
// Assume flow in and out is asIndirectExpr or asDefinitingArgument since a pointer is assumed
82+
// to be involved
83+
// NOTE: not attempting to detect openssl specific copy/dup functions, but anything suspected to be copy/dup
84+
this.getTarget().getName().toLowerCase().matches(["%_dup%", "%_copy%"]) and
85+
exists(Expr inArg, Type t |
86+
inArg = this.getAnArgument() and t = inArg.getUnspecifiedType().stripType()
87+
|
88+
inNode.asIndirectExpr() = inArg and
89+
(
90+
// Case 1: flow through another argument as an out arg of the same type
91+
exists(Expr outArg |
92+
outArg = this.getAnArgument() and
93+
outArg != inArg and
94+
outArg.getUnspecifiedType().stripType() = t
95+
|
96+
outNode.asDefiningArgument() = outArg
97+
)
98+
or
99+
// Case 2: flow through the return value if the result is the same as the intput type
100+
exists(Expr outArg | outArg = this and outArg.getUnspecifiedType().stripType() = t |
101+
outNode.asIndirectExpr() = outArg
102+
)
103+
)
104+
)
105+
}
106+
107+
override DataFlow::Node getInNode() { result = inNode }
108+
109+
override DataFlow::Node getOutNode() { result = outNode }
110+
}
111+
112+
class NIDToPointerPassthroughCall extends AlgorithmPassthroughCall {
113+
DataFlow::Node inNode;
114+
DataFlow::Node outNode;
115+
116+
NIDToPointerPassthroughCall() {
117+
this.getTarget().getName() in ["OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn"] and
118+
inNode.asExpr() = this.getArgument(0) and
119+
outNode.asExpr() = this
120+
//outNode.asIndirectExpr() = this
121+
}
122+
123+
override DataFlow::Node getInNode() { result = inNode }
124+
125+
override DataFlow::Node getOutNode() { result = outNode }
126+
}
127+
128+
class PointerToPointerPassthroughCall extends AlgorithmPassthroughCall {
129+
DataFlow::Node inNode;
130+
DataFlow::Node outNode;
131+
132+
PointerToPointerPassthroughCall() {
133+
this.getTarget().getName() = "OBJ_txt2obj" and
134+
inNode.asIndirectExpr() = this.getArgument(0) and
135+
outNode.asIndirectExpr() = this
136+
or
137+
//outNode.asExpr() = this
138+
this.getTarget().getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and
139+
inNode.asIndirectExpr() = this.getArgument(2) and
140+
outNode.asDefiningArgument() = this.getArgument(0)
141+
}
142+
143+
override DataFlow::Node getInNode() { result = inNode }
144+
145+
override DataFlow::Node getOutNode() { result = outNode }
146+
}
147+
148+
class PointerToNIDPassthroughCall extends AlgorithmPassthroughCall {
149+
DataFlow::Node inNode;
150+
DataFlow::Node outNode;
151+
152+
PointerToNIDPassthroughCall() {
153+
this.getTarget().getName() in ["OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid", "OBJ_txt2nid"] and
154+
(
155+
inNode.asIndirectExpr() = this.getArgument(0)
156+
or
157+
inNode.asExpr() = this.getArgument(0)
158+
) and
159+
outNode.asExpr() = this
160+
}
161+
162+
override DataFlow::Node getInNode() { result = inNode }
163+
164+
override DataFlow::Node getOutNode() { result = outNode }
165+
}
166+
167+
// TODO: pkeys pass through EVP_PKEY_CTX_new and any similar variant
168+
predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) {
169+
exists(AlgorithmPassthroughCall c | c.getInNode() = node1 and c.getOutNode() = node2)
170+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import cpp
2+
import experimental.Quantum.Language
3+
import OpenSSLAlgorithmInstanceBase
4+
import experimental.Quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants
5+
import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer
6+
import AlgToAVCFlow
7+
8+
/**
9+
* Given a `KnownOpenSSLBlockModeAlgorithmConstant`, converts this to a block family type.
10+
* Does not bind if there is know mapping (no mapping to 'unknown' or 'other').
11+
*/
12+
predicate knownOpenSSLConstantToBlockModeFamilyType(
13+
KnownOpenSSLBlockModeAlgorithmConstant e, Crypto::TBlockCipherModeOfOperationType type
14+
) {
15+
exists(string name |
16+
name = e.getNormalizedName() and
17+
(
18+
name.matches("CBC") and type instanceof Crypto::CBC
19+
or
20+
name.matches("CFB%") and type instanceof Crypto::CFB
21+
or
22+
name.matches("CTR") and type instanceof Crypto::CTR
23+
or
24+
name.matches("GCM") and type instanceof Crypto::GCM
25+
or
26+
name.matches("OFB") and type instanceof Crypto::OFB
27+
or
28+
name.matches("XTS") and type instanceof Crypto::XTS
29+
or
30+
name.matches("CCM") and type instanceof Crypto::CCM
31+
or
32+
name.matches("GCM") and type instanceof Crypto::GCM
33+
or
34+
name.matches("CCM") and type instanceof Crypto::CCM
35+
or
36+
name.matches("ECB") and type instanceof Crypto::ECB
37+
)
38+
)
39+
}
40+
41+
class KnownOpenSSLBlockModeConstantAlgorithmInstance extends OpenSSLAlgorithmInstance,
42+
Crypto::ModeOfOperationAlgorithmInstance instanceof KnownOpenSSLBlockModeAlgorithmConstant
43+
{
44+
OpenSSLAlgorithmValueConsumer getterCall;
45+
46+
KnownOpenSSLBlockModeConstantAlgorithmInstance() {
47+
// Two possibilities:
48+
// 1) The source is a literal and flows to a getter, then we know we have an instance
49+
// 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that
50+
// Possibility 1:
51+
this instanceof Literal and
52+
exists(DataFlow::Node src, DataFlow::Node sink |
53+
// Sink is an argument to a CipherGetterCall
54+
sink = getterCall.(OpenSSLAlgorithmValueConsumer).getInputNode() and
55+
// Source is `this`
56+
src.asExpr() = this and
57+
// This traces to a getter
58+
KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink)
59+
)
60+
or
61+
// Possibility 2:
62+
this instanceof DirectAlgorithmValueConsumer and getterCall = this
63+
}
64+
65+
override Crypto::TBlockCipherModeOfOperationType getModeType() {
66+
knownOpenSSLConstantToBlockModeFamilyType(this, result)
67+
or
68+
not knownOpenSSLConstantToBlockModeFamilyType(this, _) and result = Crypto::OtherMode()
69+
}
70+
71+
// NOTE: I'm not going to attempt to parse out the mode specific part, so returning
72+
// the same as the raw name for now.
73+
override string getRawModeAlgorithmName() { result = this.(Literal).getValue().toString() }
74+
75+
override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall }
76+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import cpp
2+
import experimental.Quantum.Language
3+
import KnownAlgorithmConstants
4+
import Crypto::KeyOpAlg as KeyOpAlg
5+
import OpenSSLAlgorithmInstanceBase
6+
import PaddingAlgorithmInstance
7+
import experimental.Quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
8+
import AlgToAVCFlow
9+
import BlockAlgorithmInstance
10+
11+
/**
12+
* Given a `KnownOpenSSLCipherAlgorithmConstant`, converts this to a cipher family type.
13+
* Does not bind if there is know mapping (no mapping to 'unknown' or 'other').
14+
*/
15+
predicate knownOpenSSLConstantToCipherFamilyType(
16+
KnownOpenSSLCipherAlgorithmConstant e, Crypto::KeyOpAlg::TAlgorithm type
17+
) {
18+
exists(string name |
19+
name = e.getNormalizedName() and
20+
(
21+
name.matches("AES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::AES())
22+
or
23+
name.matches("ARIA%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::ARIA())
24+
or
25+
name.matches("BLOWFISH%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::BLOWFISH())
26+
or
27+
name.matches("BF%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::BLOWFISH())
28+
or
29+
name.matches("CAMELLIA%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CAMELLIA())
30+
or
31+
name.matches("CHACHA20%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CHACHA20())
32+
or
33+
name.matches("CAST5%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CAST5())
34+
or
35+
name.matches("2DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DoubleDES())
36+
or
37+
name.matches("3DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::TripleDES())
38+
or
39+
name.matches("DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DES())
40+
or
41+
name.matches("DESX%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DESX())
42+
or
43+
name.matches("GOST%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::GOST())
44+
or
45+
name.matches("IDEA%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::IDEA())
46+
or
47+
name.matches("KUZNYECHIK%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::KUZNYECHIK())
48+
or
49+
name.matches("MAGMA%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::MAGMA())
50+
or
51+
name.matches("RC2%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::RC2())
52+
or
53+
name.matches("RC4%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::RC4())
54+
or
55+
name.matches("RC5%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::RC5())
56+
or
57+
name.matches("RSA%") and type = KeyOpAlg::TAsymmetricCipher(KeyOpAlg::RSA())
58+
or
59+
name.matches("SEED%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::SEED())
60+
or
61+
name.matches("SM4%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::SM4())
62+
)
63+
)
64+
}
65+
66+
class KnownOpenSSLCipherConstantAlgorithmInstance extends OpenSSLAlgorithmInstance,
67+
Crypto::KeyOperationAlgorithmInstance instanceof KnownOpenSSLCipherAlgorithmConstant
68+
{
69+
//OpenSSLAlgorithmInstance,
70+
OpenSSLAlgorithmValueConsumer getterCall;
71+
72+
KnownOpenSSLCipherConstantAlgorithmInstance() {
73+
(
74+
// Two possibilities:
75+
// 1) The source is a literal and flows to a getter, then we know we have an instance
76+
// 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that
77+
// Possibility 1:
78+
this instanceof Literal and
79+
exists(DataFlow::Node src, DataFlow::Node sink |
80+
// Sink is an argument to a CipherGetterCall
81+
sink = getterCall.(OpenSSLAlgorithmValueConsumer).getInputNode() and
82+
// Source is `this`
83+
src.asExpr() = this and
84+
// This traces to a getter
85+
KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink)
86+
)
87+
or
88+
// Possibility 2:
89+
this instanceof DirectAlgorithmValueConsumer and getterCall = this
90+
)
91+
}
92+
93+
override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() {
94+
// if there is a block mode associated with the same element, then that's the block mode
95+
// note, if none are associated, we may need to parse if the cipher is a block cipher
96+
// to determine if this is an unknown vs not relevant.
97+
result = this
98+
}
99+
100+
override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() {
101+
//TODO: the padding is either self, or it flows through getter ctx to a set padding call
102+
// like EVP_PKEY_CTX_set_rsa_padding
103+
result = this
104+
// or trace through getter ctx to set padding
105+
}
106+
107+
override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() }
108+
109+
override string getKeySizeFixed() {
110+
exists(int keySize |
111+
this.(KnownOpenSSLCipherAlgorithmConstant).getExplicitKeySize() = keySize and
112+
result = keySize.toString()
113+
)
114+
}
115+
116+
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
117+
knownOpenSSLConstantToCipherFamilyType(this, result)
118+
or
119+
not knownOpenSSLConstantToCipherFamilyType(this, _) and
120+
result = Crypto::KeyOpAlg::TUnknownKeyOperationAlgorithmType()
121+
}
122+
123+
override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall }
124+
125+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
126+
// TODO: trace to any key size initializer, symmetric and asymmetric
127+
none()
128+
}
129+
}

0 commit comments

Comments
 (0)