Skip to content

Commit a2fe19a

Browse files
committed
Initial progress on key agreement.
1 parent 69429a3 commit a2fe19a

File tree

2 files changed

+186
-2
lines changed
  • java/ql/lib/experimental/Quantum
  • shared/cryptography/codeql/cryptography

2 files changed

+186
-2
lines changed

java/ql/lib/experimental/Quantum/JCA.qll

Lines changed: 133 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ module JCAModel {
5656
bindingset[name]
5757
predicate elliptic_curve_names(string name) { Crypto::isEllipticCurveAlgorithmName(name) }
5858

59+
bindingset[name]
60+
predicate key_agreement_names(string name) { Crypto::isKeyAgreementAlgorithmName(name) }
61+
5962
bindingset[name]
6063
Crypto::TKeyDerivationType kdf_name_to_kdf_type(string name, string withSubstring) {
6164
name.matches("PBKDF2With%") and
@@ -123,6 +126,10 @@ module JCAModel {
123126
string getStandardEllipticCurveName() { result = this.getValue() }
124127
}
125128

129+
class KeyAgreementStringLiteral extends StringLiteral {
130+
KeyAgreementStringLiteral() { key_agreement_names(this.getValue()) }
131+
}
132+
126133
class CipherGetInstanceCall extends Call {
127134
CipherGetInstanceCall() {
128135
this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance")
@@ -166,7 +173,7 @@ module JCAModel {
166173
TaintTracking::Global<CipherAlgorithmStringToFetchConfig>;
167174

168175
/**
169-
* Data-flow configuration modelling flow from a cipher string literal to a value consumer argument.
176+
* Data-flow configuration modelling flow from a elliptic curve literal to a value consumer argument.
170177
*/
171178
private module EllipticCurveAlgorithmStringToFetchConfig implements DataFlow::ConfigSig {
172179
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof EllipticCurveStringLiteral }
@@ -211,6 +218,89 @@ module JCAModel {
211218
}
212219
}
213220

221+
/**
222+
* Data-flow configuration modelling flow from a key agreement literal to a value consumer argument.
223+
*/
224+
private module KeyAgreementAlgorithmStringToFetchConfig implements DataFlow::ConfigSig {
225+
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KeyAgreementStringLiteral }
226+
227+
predicate isSink(DataFlow::Node sink) {
228+
exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode())
229+
}
230+
}
231+
232+
module KeyAgreementAlgorithmStringToFetchFlow =
233+
TaintTracking::Global<KeyAgreementAlgorithmStringToFetchConfig>;
234+
235+
class KeyAgreementInitCall extends MethodCall {
236+
KeyAgreementInitCall() {
237+
this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "init")
238+
}
239+
240+
Expr getServerKeyArg() { result = this.getArgument(0) }
241+
}
242+
243+
private module KeyAgreementInitQualifierToSecretGenQualifierConfig implements DataFlow::ConfigSig {
244+
predicate isSource(DataFlow::Node src) {
245+
exists(KeyAgreementInitCall init | src.asExpr() = init.getQualifier())
246+
}
247+
248+
predicate isSink(DataFlow::Node sink) {
249+
exists(KeyAgreementGenerateSecretCall c | sink.asExpr() = c.getQualifier())
250+
}
251+
252+
/**
253+
* Barrier if we go into another init, assume the second init overwrites the first
254+
*/
255+
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
256+
}
257+
258+
module KeyAgreementInitQualifierToSecretGenQualifierFlow =
259+
DataFlow::Global<KeyAgreementInitQualifierToSecretGenQualifierConfig>;
260+
261+
class KeyAgreementGenerateSecretCall extends MethodCall {
262+
KeyAgreementGenerateSecretCall() {
263+
this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "generateSecret")
264+
}
265+
266+
KeyAgreementInitCall getKeyAgreementInitCall() {
267+
KeyAgreementInitQualifierToSecretGenQualifierFlow::flow(DataFlow::exprNode(result
268+
.getQualifier()), DataFlow::exprNode(this.getQualifier()))
269+
}
270+
}
271+
272+
private module KeyAgreementAVCToInitQualifierConfig implements DataFlow::ConfigSig {
273+
predicate isSource(DataFlow::Node src) {
274+
exists(KeyAgreementAlgorithmValueConsumer consumer | consumer.getResultNode() = src)
275+
}
276+
277+
predicate isSink(DataFlow::Node sink) {
278+
exists(KeyAgreementInitCall init | sink.asExpr() = init.getQualifier())
279+
}
280+
}
281+
282+
module KeyAgreementAVCToInitQualifierFlow =
283+
DataFlow::Global<KeyAgreementAVCToInitQualifierConfig>;
284+
285+
class KeyAgreementSecretGenerationOperationInstance extends Crypto::KeyAgreementSecretGenerationOperationInstance instanceof KeyAgreementGenerateSecretCall
286+
{
287+
override Crypto::ConsumerInputDataFlowNode getServerKeyConsumer() {
288+
this.(KeyAgreementGenerateSecretCall).getKeyAgreementInitCall().getServerKeyArg() =
289+
result.asExpr()
290+
}
291+
292+
override Crypto::ConsumerInputDataFlowNode getPeerKeyConsumer() {
293+
none() //TODO
294+
}
295+
296+
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
297+
none() // TODO: key agreeement has it's own algorithm consumer, separate from the key
298+
// TODO: the char pred must trace from the consumer to here,
299+
// in theory, along that path we would get the init and doPhase, but can I just get those
300+
// separately avoiding a complicated config state for dataflow?
301+
}
302+
}
303+
214304
class CipherStringLiteralModeAlgorithmInstance extends JCAAlgorithmInstance,
215305
CipherStringLiteralPaddingAlgorithmInstance, Crypto::ModeOfOperationAlgorithmInstance instanceof CipherStringLiteral
216306
{
@@ -339,7 +429,7 @@ module JCAModel {
339429
}
340430

341431
class EllipticCurveStringLiteralAlgorithmInstance extends JCAAlgorithmInstance,
342-
Crypto::EllipticCurveAlgorithmInstance instanceof StringLiteral
432+
Crypto::EllipticCurveAlgorithmInstance instanceof EllipticCurveStringLiteral
343433
{
344434
Crypto::AlgorithmValueConsumer consumer;
345435

@@ -367,6 +457,47 @@ module JCAModel {
367457
}
368458
}
369459

460+
class KeyAgreementGetInstanceCall extends MethodCall {
461+
KeyAgreementGetInstanceCall() {
462+
this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "getInstance")
463+
}
464+
465+
Expr getAlgorithmArg() { result = super.getArgument(0) }
466+
}
467+
468+
class KeyAgreementAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer {
469+
KeyAgreementGetInstanceCall call;
470+
471+
KeyAgreementAlgorithmValueConsumer() { this = call.getAlgorithmArg() }
472+
473+
DataFlow::Node getResultNode() { result.asExpr() = call }
474+
475+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
476+
477+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
478+
result.(KeyAgreementStringLiteralAlgorithmInstance).getConsumer() = this
479+
}
480+
}
481+
482+
class KeyAgreementStringLiteralAlgorithmInstance extends JCAAlgorithmInstance,
483+
Crypto::KeyAgreementAlgorithmInstance instanceof KeyAgreementStringLiteral
484+
{
485+
Crypto::AlgorithmValueConsumer consumer;
486+
487+
KeyAgreementStringLiteralAlgorithmInstance() {
488+
KeyAgreementAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode())
489+
}
490+
491+
override Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
492+
// override Crypto::EllipticCurveAlgorithmInstance getEllipticCurveAlgorithm() {
493+
// this.(KeyAgreementStringLiteral).getValue().toUpperCase() in ["X25519", "X448"] and
494+
// // NOTE: this relies upon modeling the elliptic curve on 'this' separately
495+
// result = this
496+
// // TODO: or is ecdh and go find the curve
497+
// // this.(KeyAgreementStringLiteral).toString().toUpperCase() = ["ECDH"]
498+
// }
499+
}
500+
370501
class CipherStringLiteralAlgorithmInstance extends JCAAlgorithmInstance,
371502
Crypto::CipherAlgorithmInstance instanceof CipherStringLiteral
372503
{

shared/cryptography/codeql/cryptography/Model.qll

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,7 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
837837
TKeyDerivationAlgorithm(KeyDerivationAlgorithmInstanceOrValueConsumer e) or
838838
TKeyEncapsulationAlgorithm(KeyEncapsulationAlgorithmInstance e) or
839839
TMACAlgorithm(MACAlgorithmInstanceOrValueConsumer e) or
840+
TKeyAgreementAlgorithm(KeyAgreementAlgorithmInstance e) or
840841
// Non-standalone Algorithms (e.g., Mode, Padding)
841842
// TODO: need to rename this, as "mode" is getting reused in different contexts, be precise
842843
TModeOfOperationAlgorithm(ModeOfOperationAlgorithmInstance e) or
@@ -2220,4 +2221,56 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
22202221
abstract class KEMAlgorithm extends TKeyEncapsulationAlgorithm, AlgorithmNode {
22212222
final override string getInternalType() { result = "KeyEncapsulationAlgorithm" }
22222223
}
2224+
2225+
/**
2226+
* Key agreement algorithms
2227+
*/
2228+
newtype TKeyAgreementType =
2229+
DH() or // Diffie-Hellman
2230+
EDH() or // Ephemeral Diffie-Hellman
2231+
ECDH() or // Elliptic Curve Diffie-Hellman
2232+
// Note: x25519 and x448 are applications of ECDH
2233+
OtherKeyAgreementType()
2234+
2235+
bindingset[name]
2236+
predicate isKeyAgreementAlgorithmName(string name) { isKeyAgreementAlgorithm(name, _) }
2237+
2238+
bindingset[name]
2239+
predicate isKeyAgreementAlgorithm(string name, TKeyAgreementType type) {
2240+
exists(string name2 | name2 = name.toUpperCase() |
2241+
name2 = "DH" and type = DH()
2242+
or
2243+
name2 = "EDH" and type = EDH()
2244+
or
2245+
name2 = "ECDH" and type = ECDH()
2246+
or
2247+
name2 = "X25519" and type = ECDH()
2248+
or
2249+
name2 = "X448" and type = ECDH()
2250+
)
2251+
}
2252+
2253+
abstract class KeyAgreementAlgorithmInstance extends AlgorithmInstance {
2254+
// /**
2255+
// * If the key agreement uses a curve, (e.g., ECDH) point to the curve instance.
2256+
// * none() if the agreement is not curve based (e.g., plain DH).
2257+
// * Note that if the curve is inherent to the algorithm (e.g., x25519), this will be
2258+
// * the key agreement algorithm instance itself (this).
2259+
// */
2260+
// abstract EllipticCurveAlgorithmInstance getEllipticCurveAlgorithm();
2261+
}
2262+
2263+
abstract class KeyAgreementSecretGenerationOperationInstance extends OperationInstance {
2264+
/**
2265+
* The private key used in the key agreement operation.
2266+
* This key represents the local party in the key agreement.
2267+
*/
2268+
abstract ConsumerInputDataFlowNode getServerKeyConsumer();
2269+
2270+
/**
2271+
* The public key used in the key agreement operation, coming
2272+
* from the peer (the other party in the key agreement).
2273+
*/
2274+
abstract ConsumerInputDataFlowNode getPeerKeyConsumer();
2275+
}
22232276
}

0 commit comments

Comments
 (0)