Skip to content

Commit 50691ec

Browse files
authored
Merge pull request #19 from raulgarciamsft/16-detecting-private-key-usage-windows-cng-apis
16 detecting private key usage windows cng apis
2 parents ad29826 + 345b7e4 commit 50691ec

File tree

7 files changed

+211
-128
lines changed

7 files changed

+211
-128
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import cpp
2+
import DataFlow::PathGraph
3+
import semmle.code.cpp.dataflow.TaintTracking
4+
5+
/**
6+
* Base abstract class to be extended to allow indirect extensions of vulnerable sinks.
7+
*/
8+
abstract class BCryptOpenAlgorithmProviderSink extends DataFlow::Node { }
9+
10+
/**
11+
* Base abstract class to be extended to allow indirect extensions of vulnerable sources.
12+
*/
13+
abstract class BCryptOpenAlgorithmProviderSource extends DataFlow::Node { }
14+
15+
// ------------------ Helper Predicates ----------------------
16+
/**
17+
* Holds if there is a call with global name`funcGlobalName` with argument `arg` of that call
18+
* at argument index `index`.
19+
*/
20+
predicate isCallArgument(string funcGlobalName, Expr arg, int index) {
21+
exists(Call c | c.getArgument(index) = arg and c.getTarget().hasGlobalName(funcGlobalName))
22+
}
23+
24+
/**
25+
* Holdes if StringLiteral `lit` has a string value indicative of a post quantum crypto
26+
* vulnerable algorithm identifier.
27+
*/
28+
predicate vulnProviderLiteral(StringLiteral lit) {
29+
exists(string s | s = lit.getValue() |
30+
s in ["DH", "DSA", "ECDSA", "ECDH"] or
31+
s.matches("ECDH%") or
32+
s.matches("RSA%")
33+
)
34+
}
35+
36+
// ------------------ Default SINKS ----------------------
37+
/**
38+
* Argument at index 0 of call to NCryptSignHash:
39+
* [in] NCRYPT_KEY_HANDLE hKey
40+
*/
41+
class NCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
42+
int index;
43+
string funcName;
44+
45+
NCryptSignHashArgumentSink() {
46+
index = 0 and
47+
funcName = "NCryptSignHash " and
48+
isCallArgument(funcName, this.asExpr(), index)
49+
}
50+
}
51+
52+
/**
53+
* Argument at index 0 of call to BCryptSignHash:
54+
* [in] BCRYPT_KEY_HANDLE hKey,
55+
*/
56+
class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
57+
int index;
58+
string funcName;
59+
60+
BCryptSignHashArgumentSink() {
61+
index = 0 and
62+
funcName = "BCryptSignHash" and
63+
isCallArgument(funcName, this.asExpr(), index)
64+
}
65+
}
66+
67+
/**
68+
* Argument at index 0 of call to BCryptGenerateKeyPair:
69+
* [in, out] BCRYPT_ALG_HANDLE hAlgorithm,
70+
*/
71+
class BCryptGenerateKeyPair extends BCryptOpenAlgorithmProviderSink {
72+
int index;
73+
string funcName;
74+
75+
BCryptGenerateKeyPair() {
76+
index = 0 and
77+
funcName = "BCryptGenerateKeyPair" and
78+
isCallArgument(funcName, this.asExpr(), index)
79+
}
80+
}
81+
82+
/**
83+
* Argument at index 0 of call to BCryptEncrypt:
84+
* [in, out] BCRYPT_KEY_HANDLE hKey,
85+
*/
86+
class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
87+
int index;
88+
string funcName;
89+
90+
BCryptEncryptArgumentSink() {
91+
index = 0 and
92+
funcName = "BCryptEncrypt" and
93+
isCallArgument(funcName, this.asExpr(), index)
94+
}
95+
}
96+
97+
/**
98+
* Argument at index 0 of call to NCryptEncrypt:
99+
* [in] NCRYPT_KEY_HANDLE hKey,
100+
*/
101+
class NCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
102+
int index;
103+
string funcName;
104+
105+
NCryptEncryptArgumentSink() {
106+
index = 0 and
107+
funcName = "NCryptEncrypt" and
108+
isCallArgument(funcName, this.asExpr(), index)
109+
}
110+
}
111+
112+
// ----------------- Default SOURCES -----------------------
113+
/**
114+
* A string identifier of known PQC vulnerable algorithms.
115+
*/
116+
class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
117+
BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() { vulnProviderLiteral(this.asExpr()) }
118+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @id cpp/nist-pqc/pqc-vulnerable-algorithms-cng
3+
* @name Usage of PQC vulnerable algorithms
4+
* @description Usage of PQC vulnerable algorithms.
5+
* @microsoft.severity important
6+
* @kind path-problem
7+
* @problem.severity warning
8+
* @precision high
9+
* @tags security
10+
* pqc
11+
* nist
12+
*/
13+
14+
import cpp
15+
import DataFlow::PathGraph
16+
import WindowsCng
17+
import WindowsCngPQCVulnerableUsage
18+
19+
from BCryptConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
20+
where config.hasFlowPath(source, sink)
21+
select sink.getNode(), source, sink, "PQC vulnerable algorithm $@ in use has been detected.",
22+
source.getNode().asExpr(), source.getNode().asExpr().toString()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import cpp
2+
import WindowsCng
3+
4+
/**
5+
* Steps from input variable (argument 1) to output variable (argument 0)
6+
* for CNG API BCryptOpenAlgorithmProvider.
7+
* Argument 1 represents LPCWSTR (a string algorithm ID)
8+
* Argument 0 represents BCRYPT_ALG_HANDLE
9+
*/
10+
predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2) {
11+
exists(FunctionCall call |
12+
// BCryptOpenAlgorithmProvider 2nd argument specifies the algorithm to be used
13+
node1.asExpr() = call.getArgument(1) and
14+
call.getTarget().hasGlobalName("BCryptOpenAlgorithmProvider") and
15+
node2.asDefiningArgument() = call.getArgument(0)
16+
)
17+
}
18+
19+
/**
20+
* Steps from input variable (argument 0) to output variable (argument 1)
21+
* for CNG APIs BCryptImportKeyPair and BCryptGenerateKeyPair.
22+
* BCryptGenerateKeyPair:
23+
* Argument 0 represents a BCRYPT_ALG_HANDLE.
24+
* Argument 1 represents a BCRYPT_KEY_HANDLE.
25+
* BCryptImportKeyPair:
26+
* Argument 0 represents a BCRYPT_ALG_HANDLE.
27+
* Argument 3 represents a BCRYPT_KEY_HANDLE.
28+
*/
29+
predicate stepImportGenerateKeyPair(DataFlow::Node node1, DataFlow::Node node2) {
30+
exists(FunctionCall call |
31+
node1.asExpr() = call.getArgument(0) and
32+
exists(string name | call.getTarget().hasGlobalName(name) |
33+
name = "BCryptImportKeyPair" and node2.asDefiningArgument() = call.getArgument(3)
34+
or
35+
name = "BCryptGenerateKeyPair" and node2.asDefiningArgument() = call.getArgument(1)
36+
)
37+
)
38+
}
39+
40+
/**
41+
* Additional DataFlow steps from input variables to output handle variables on CNG apis.
42+
*/
43+
predicate isWindowsCngAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
44+
stepOpenAlgorithmProvider(node1, node2)
45+
or
46+
stepImportGenerateKeyPair(node1, node2)
47+
}
48+
49+
/**
50+
* CNG-specific DataFlow configuration
51+
*/
52+
class BCryptConfiguration extends DataFlow::Configuration {
53+
BCryptConfiguration() { this = "BCryptConfiguration" }
54+
55+
/**
56+
* Uses indirect extensions of BCryptOpenAlgorithmProviderSource
57+
*/
58+
override predicate isSource(DataFlow::Node source) {
59+
source instanceof BCryptOpenAlgorithmProviderSource
60+
}
61+
62+
/**
63+
* Uses indirect extensions of BCryptOpenAlgorithmProviderSink
64+
*/
65+
override predicate isSink(DataFlow::Node sink) { sink instanceof BCryptOpenAlgorithmProviderSink }
66+
67+
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
68+
isWindowsCngAdditionalTaintStep(node1, node2)
69+
}
70+
}

cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCng.qll

Lines changed: 0 additions & 7 deletions
This file was deleted.

cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.ql

Lines changed: 0 additions & 40 deletions
This file was deleted.

cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll

Lines changed: 0 additions & 81 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql

0 commit comments

Comments
 (0)