Skip to content

Commit fb35929

Browse files
committed
init work for openssl signatures
1 parent daad2a4 commit fb35929

File tree

4 files changed

+235
-0
lines changed

4 files changed

+235
-0
lines changed

cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,6 +2866,8 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized,
28662866
or
28672867
name = "rsa-sha256" and nid = 668 and normalized = "SHA-256" and algType = "HASH"
28682868
or
2869+
name = "rsa-sha256" and nid = 668 and normalized = "SHA-256" and algType = "SIGNATURE"
2870+
or
28692871
name = "rsa-sha3-224" and nid = 1116 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION"
28702872
or
28712873
name = "rsa-sha3-224" and nid = 1116 and normalized = "SHA3-224" and algType = "HASH"
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import cpp
2+
private import experimental.quantum.Language
3+
private import KnownAlgorithmConstants
4+
private import Crypto::KeyOpAlg as KeyOpAlg
5+
private import OpenSSLAlgorithmInstanceBase
6+
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase
7+
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer
8+
private import AlgToAVCFlow
9+
10+
/**
11+
* Gets the signature algorithm type based on the normalized algorithm name.
12+
*/
13+
private predicate knownOpenSSLConstantToSignatureFamilyType(
14+
KnownOpenSSLSignatureAlgorithmConstant e, Crypto::KeyOpAlg::TAlgorithm type
15+
) {
16+
exists(string name |
17+
name = e.getNormalizedName() and
18+
(
19+
name.matches("RSA%") and type = KeyOpAlg::TAsymmetricCipher(KeyOpAlg::RSA())
20+
or
21+
name.matches("DSA%") and type = KeyOpAlg::TSignature(KeyOpAlg::DSA())
22+
or
23+
name.matches("ECDSA%") and type = KeyOpAlg::TSignature(KeyOpAlg::ECDSA())
24+
or
25+
name.matches("ED25519%") and type = KeyOpAlg::TSignature(KeyOpAlg::Ed25519())
26+
or
27+
name.matches("ED448%") and type = KeyOpAlg::TSignature(KeyOpAlg::Ed448())
28+
// or
29+
// name.matches("sm2%") and type = KeyOpAlg::TSignature(KeyOpAlg::SM2())
30+
// or
31+
// name.matches("ml-dsa%") and type = KeyOpAlg::TSignature(KeyOpAlg::MLDSA())
32+
// or
33+
// name.matches("slh-dsa%") and type = KeyOpAlg::TSignature(KeyOpAlg::SLHDSA())
34+
)
35+
)
36+
}
37+
38+
/**
39+
* A signature algorithm instance derived from an OpenSSL constant.
40+
*/
41+
class KnownOpenSSLSignatureConstantAlgorithmInstance extends OpenSSLAlgorithmInstance,
42+
Crypto::KeyOperationAlgorithmInstance instanceof KnownOpenSSLSignatureAlgorithmConstant
43+
{
44+
OpenSSLAlgorithmValueConsumer getterCall;
45+
46+
KnownOpenSSLSignatureConstantAlgorithmInstance() {
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 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 signature getter call
54+
sink = getterCall.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::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() }
66+
67+
override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() }
68+
69+
override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() }
70+
71+
override int getKeySizeFixed() {
72+
// this.(KnownOpenSSLSignatureAlgorithmConstant).getExplicitKeySize() = result
73+
none()
74+
}
75+
76+
override KeyOpAlg::Algorithm getAlgorithmType() {
77+
knownOpenSSLConstantToSignatureFamilyType(this, result)
78+
or
79+
not knownOpenSSLConstantToSignatureFamilyType(this, _) and
80+
result = KeyOpAlg::TSignature(KeyOpAlg::OtherSignatureAlgorithmType())
81+
}
82+
83+
override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall }
84+
85+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
86+
// TODO: trace to any key size initializer, symmetric and asymmetric
87+
none()
88+
}
89+
90+
override predicate shouldHaveModeOfOperation() { none() }
91+
92+
override predicate shouldHavePaddingScheme() { none() }
93+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import cpp
2+
private import experimental.quantum.Language
3+
private import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants
4+
private import experimental.quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstanceBase
5+
private import OpenSSLAlgorithmValueConsumerBase
6+
private import experimental.quantum.OpenSSL.LibraryDetector
7+
8+
abstract class SignatureAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { }
9+
10+
class EVPSignatureAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer {
11+
DataFlow::Node valueArgNode;
12+
DataFlow::Node resultNode;
13+
14+
EVPSignatureAlgorithmValueConsumer() {
15+
resultNode.asExpr() = this and
16+
isPossibleOpenSSLFunction(this.(Call).getTarget()) and
17+
(
18+
// EVP_SIGNATURE
19+
this.(Call).getTarget().getName() = "EVP_SIGNATURE_fetch" and
20+
valueArgNode.asExpr() = this.(Call).getArgument(1)
21+
// EVP_PKEY_get1_DSA, DSA_SIG_new, EVP_RSA_gen
22+
)
23+
}
24+
25+
override DataFlow::Node getResultNode() { result = resultNode }
26+
27+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result = valueArgNode }
28+
29+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
30+
exists(OpenSSLAlgorithmInstance i | i.getAVC() = this and result = i)
31+
}
32+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* Provides classes for modeling OpenSSL's EVP signature operations
3+
*/
4+
5+
private import experimental.quantum.Language
6+
private import OpenSSLOperationBase
7+
private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow
8+
9+
// TODO: verification
10+
class EVP_Cipher_Initializer extends EVPInitialize {
11+
EVP_Cipher_Initializer() {
12+
this.(Call).getTarget().getName() in [
13+
"EVP_DigestSignInit", "EVP_DigestSignInit_ex", "EVP_SignInit", "EVP_SignInit_ex",
14+
"EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex", "EVP_PKEY_sign_init_ex2",
15+
"EVP_PKEY_sign_message_init"
16+
]
17+
}
18+
19+
override Expr getAlgorithmArg() {
20+
this.(Call).getTarget().getName() = "EVP_DigestSignInit" and
21+
result = this.(Call).getArgument(1)
22+
or
23+
this.(Call).getTarget().getName() = "EVP_DigestSignInit_ex" and
24+
result = this.(Call).getArgument(1)
25+
or
26+
this.(Call).getTarget().getName() = "EVP_PKEY_sign_init_ex2" and
27+
result = this.(Call).getArgument(1)
28+
or
29+
this.(Call).getTarget().getName() = "EVP_PKEY_sign_message_init" and
30+
result = this.(Call).getArgument(1)
31+
}
32+
33+
override Expr getKeyArg() {
34+
this.(Call).getTarget().getName() = "EVP_DigestSignInit" and
35+
result = this.(Call).getArgument(4)
36+
or
37+
this.(Call).getTarget().getName() = "EVP_DigestSignInit_ex" and
38+
result = this.(Call).getArgument(5)
39+
}
40+
41+
override Expr getIVArg() { none() }
42+
43+
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
44+
if this.(Call).getTarget().getName().toLowerCase().matches("%sign%")
45+
then result instanceof Crypto::TSignMode
46+
else
47+
if this.(Call).getTarget().getName().toLowerCase().matches("%verify%")
48+
then result instanceof Crypto::TVerifyMode
49+
else result instanceof Crypto::TUnknownKeyOperationMode
50+
}
51+
}
52+
53+
class EVP_Signature_Update_Call extends EVPUpdate {
54+
EVP_Signature_Update_Call() {
55+
this.(Call).getTarget().getName() in [
56+
"EVP_DigestSignUpdate", "EVP_SignUpdate", "EVP_PKEY_sign_message_update"
57+
]
58+
}
59+
60+
override Expr getInputArg() { result = this.(Call).getArgument(1) }
61+
}
62+
63+
abstract class EVP_Signature_Operation extends EVPOperation, Crypto::KeyOperationInstance {
64+
EVP_Signature_Operation() { this.(Call).getTarget().getName().matches("EVP_%") }
65+
66+
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
67+
if this.(Call).getTarget().getName().toLowerCase().matches("%sign%")
68+
then result instanceof Crypto::TSignMode
69+
else
70+
if this.(Call).getTarget().getName().toLowerCase().matches("%verify%")
71+
then result instanceof Crypto::TVerifyMode
72+
else result instanceof Crypto::TUnknownKeyOperationMode
73+
}
74+
75+
override Expr getOutputArg() { result = this.(Call).getArgument(1) }
76+
77+
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
78+
// this.getInitCall().getIVArg() = result.asExpr()
79+
none()
80+
}
81+
82+
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
83+
this.getInitCall().getKeyArg() = result.asExpr()
84+
// todo: or track to the EVP_PKEY_CTX_new
85+
}
86+
87+
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
88+
result = this.(EVPOperation).getOutputArtifact()
89+
}
90+
91+
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
92+
result = this.(EVPOperation).getInputConsumer()
93+
}
94+
}
95+
96+
class EVP_Signature_Call extends EVPOneShot, EVP_Signature_Operation {
97+
EVP_Signature_Call() { this.(Call).getTarget().getName() in ["EVP_DigestSign", "EVP_PKEY_sign"] }
98+
99+
override Expr getInputArg() { result = this.(Call).getArgument(3) }
100+
}
101+
102+
class EVP_Signature_Final_Call extends EVPFinal, EVP_Signature_Operation {
103+
EVP_Signature_Final_Call() {
104+
this.(Call).getTarget().getName() in [
105+
"EVP_DigestSignFinal", "EVP_SignFinal_ex", "EVP_SignFinal", "EVP_PKEY_sign_message_final"
106+
]
107+
}
108+
}

0 commit comments

Comments
 (0)