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

+1205
-131
lines changed
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)