From 43e5e40823354923fa163ec8876b23960c37b179 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Thu, 15 May 2025 18:14:53 +0200 Subject: [PATCH 01/32] Added initial support for BouncyCastle signers --- .../lib/experimental/quantum/BouncyCastle.qll | 164 +++++++++++++++++ .../BouncyCastle/AlgorithmInstances.qll | 10 ++ .../SignatureAlgorithmInstances.qll | 44 +++++ .../quantum/BouncyCastle/FlowAnalysis.qll | 166 ++++++++++++++++++ java/ql/lib/experimental/quantum/Language.qll | 1 + 5 files changed, 385 insertions(+) create mode 100644 java/ql/lib/experimental/quantum/BouncyCastle.qll create mode 100644 java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll create mode 100644 java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll create mode 100644 java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll new file mode 100644 index 000000000000..7ef83ca99667 --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -0,0 +1,164 @@ + +import java + +/** + * Models for the signature algorithms defined by the `org.bouncycastle.crypto.signers` package. + * + */ +module Signers { + import Language + import BouncyCastle.FlowAnalysis + import BouncyCastle.AlgorithmInstances + + abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } + + /** + * A model of the `Signer` class in Bouncy Castle. + */ + class Signer extends RefType { + Signer() { + this.getPackage().getName() = "org.bouncycastle.crypto.signers" and + this.getName().matches("%Signer") + } + + MethodCall getAnInitCall() { + result = this.getAMethodCall("init") + } + + MethodCall getAUseCall() { + result = this.getAMethodCall(["update", "generateSignature", "verifySignature"]) + } + + MethodCall getAMethodCall(string name) { + result.getCallee().hasQualifiedName("org.bouncycastle.crypto.signers", this.getName(), name) + } + } + + /** + * BouncyCastle algorithms are instantiated by calling the constructor of the + * corresponding class. The algorithm is implicitly defined by the constructor + * call. + */ + class NewCall extends SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr { + NewCall() { + this.getConstructedType() instanceof Signer + } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result = getSignatureAlgorithmInstanceFromType(super.getConstructedType()) + } + + // TODO: Since the algorithm is implicitly defined by the constructor, should + // the input node be `this`? + override Crypto::ConsumerInputDataFlowNode getInputNode() { + result.asExpr() = this + } + } + + /** + * The type is instantiated by a constructor call and initialized by a call to + * `init()` which takes two arguments. The first argument is a flag indicating + * whether the operation is signing data or verifying a signature, and the + * second is the key to use. + */ + class InitCall extends MethodCall { + InitCall() { + this = any(Signer signer).getAnInitCall() + } + + Expr getForSigningArg() { result = this.getArgument(0) } + + Expr getKeyArg() { result = this.getArgument(1) } + + Crypto::KeyOperationAlgorithmInstance getAlgorithm() { + result = getSignatureAlgorithmInstanceFromType(this.getReceiverType()) + } + + Crypto::KeyOperationSubtype getKeyOperationSubtype() { + ( + this.getForSigningArg().(BooleanLiteral).getBooleanValue() = true and + result = Crypto::TSignMode() + ) or ( + this.getForSigningArg().(BooleanLiteral).getBooleanValue() = false and + result = Crypto::TVerifyMode() + ) or ( + result = Crypto::TUnknownKeyOperationMode() + ) + } + } + + /** + * The `update()` method is used to pass message data to the signer, and the + * `generateSignature()` or `verifySignature()` methods are used to produce or + * verify the signature, respectively. + */ + class UseCall extends MethodCall { + UseCall() { + this = any(Signer signer).getAUseCall() + } + + predicate isIntermediate() { + this.getCallee().getName() = "update" + } + + Expr getInput() { result = this.getArgument(0) } + + Expr getOutput() { + result = this + } + } + + /** + * Instantiate the flow analysis module for the `Signer` class. + */ + module FlowAnalysis = NewToInitToUseFlowAnalysis; + + UseCall getIntermediateUse(UseCall use) { + result = FlowAnalysis::getAnIntermediateUseFromFinalUse(use, _, _) + } + + /** + * A signing operation instance is a call to either `update()`, `generateSignature()`, + * or `verifySignature()` on a `Signer` instance. + */ + class SignatureOperationInstance extends Crypto::KeyOperationInstance instanceof UseCall { + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result = FlowAnalysis::getInstantiationFromInit(this.getInitCall(), _, _) + } + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + if FlowAnalysis::hasInit(this) + then result = this.getInitCall().getKeyOperationSubtype() + else result = Crypto::TUnknownKeyOperationMode() + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + result.asExpr() = this.getInitCall().getKeyArg() + } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { none() } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result.asExpr() = this.getAnUpdateCall().getInput() + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + this.getKeyOperationSubtype() = Crypto::TSignMode() and + not super.isIntermediate() and + result.asExpr() = super.getOutput() + } + + InitCall getInitCall() { + result = FlowAnalysis::getInitFromUse(this, _, _) + } + + UseCall getAnUpdateCall() { + ( + super.isIntermediate() and result = this + ) or ( + result = FlowAnalysis::getAnIntermediateUseFromFinalUse(this, _, _) + ) + } + } +} + diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll new file mode 100644 index 000000000000..a493da9689ba --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -0,0 +1,10 @@ +import java +import experimental.quantum.Language +import AlgorithmInstances.SignatureAlgorithmInstances + +SignatureAlgorithmInstance getSignatureAlgorithmInstanceFromType(Type t) { + t.getName() = "Ed25519Signer" and result instanceof Ed25519AlgorithmInstance + or + t.getName() = "Ed448Signer" and result instanceof Ed448AlgorithmInstance +} + diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll new file mode 100644 index 000000000000..27a5833c49d2 --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll @@ -0,0 +1,44 @@ +import java +import experimental.quantum.Language + +abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance { + + // TODO: Could potentially be used to model signature modes like Ed25519ph and Ed25519ctx. + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } +} + +class Ed25519AlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { + Ed25519AlgorithmInstance() { + this.getConstructedType().hasQualifiedName("org.bouncycastle.crypto.signers", "Ed25519Signer") + } + + override string getRawAlgorithmName() { result = "Ed25519" } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) + } + + // TODO: May be redundant. + override string getKeySizeFixed() { result = "256" } +} + +class Ed448AlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { + Ed448AlgorithmInstance() { + this.getConstructedType().hasQualifiedName("org.bouncycastle.crypto.signers", "Ed448Signer") + } + + override string getRawAlgorithmName() { result = "Ed448" } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) + } + + // TODO: May be redundant. + override string getKeySizeFixed() { result = "448" } +} + + diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll new file mode 100644 index 000000000000..d2f02e3f9a52 --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll @@ -0,0 +1,166 @@ +import java +import semmle.code.java.dataflow.DataFlow + +/** + * A signature for the `getInstance()` method calls used in JCA, or direct + * constructor calls used in BouncyCastle. + */ +signature class NewCallSig instanceof Call; + +signature class InitCallSig instanceof MethodCall; + +signature class UseCallSig instanceof MethodCall { + /** + * Holds if the use is not a final use, such as an `update()` call before `doFinal()` + */ + predicate isIntermediate(); +} + +/** + * An generic analysis module for analyzing the `getInstance()` to `initialize()` + * to `doOperation()` pattern in the JCA, and similar patterns in other libraries. + * + * For example: + * ``` + * kpg = KeyPairGenerator.getInstance(); + * kpg.initialize(...); + * kpg.generate(...); + * ``` + */ +module NewToInitToUseFlowAnalysis +{ + newtype TFlowState = + TUninitialized() or + TInitialized(Init call) or + TIntermediateUse(Use call) + + abstract class InitFlowState extends TFlowState { + string toString() { + this = TUninitialized() and result = "Uninitialized" + or + this = TInitialized(_) and result = "Initialized" + // TODO: add intermediate use + } + } + + class UninitializedFlowState extends InitFlowState, TUninitialized { } + + class InitializedFlowState extends InitFlowState, TInitialized { + Init call; + DataFlow::Node node1; + DataFlow::Node node2; + + InitializedFlowState() { + this = TInitialized(call) and + node2.asExpr() = call.(MethodCall).getQualifier() and + DataFlow::localFlowStep(node1, node2) and + node1 != node2 + } + + Init getInitCall() { result = call } + + DataFlow::Node getFstNode() { result = node1 } + + DataFlow::Node getSndNode() { result = node2 } + } + + class IntermediateUseState extends InitFlowState, TIntermediateUse { + Use call; + DataFlow::Node node1; + DataFlow::Node node2; + + IntermediateUseState() { + this = TIntermediateUse(call) and + call.isIntermediate() and + node1.asExpr() = call.(MethodCall).getQualifier() and + node2 = node1 + } + + Use getUseCall() { result = call } + + DataFlow::Node getFstNode() { result = node1 } + + DataFlow::Node getSndNode() { result = node2 } + } + + module GetInstanceToInitToUseConfig implements DataFlow::StateConfigSig { + class FlowState = InitFlowState; + + predicate isSource(DataFlow::Node src, FlowState state) { + state instanceof UninitializedFlowState and + src.asExpr() instanceof GetInstance + or + src = state.(InitializedFlowState).getSndNode() + or + src = state.(IntermediateUseState).getSndNode() + } + + // TODO: document this, but this is intentional (avoid cross products?) + predicate isSink(DataFlow::Node sink, FlowState state) { none() } + + predicate isSink(DataFlow::Node sink) { + exists(Init c | c.(MethodCall).getQualifier() = sink.asExpr()) + or + exists(Use c | not c.isIntermediate() and c.(MethodCall).getQualifier() = sink.asExpr()) + } + + predicate isAdditionalFlowStep( + DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 + ) { + state1 = state1 and + ( + node1 = state2.(InitializedFlowState).getFstNode() and + node2 = state2.(InitializedFlowState).getSndNode() + or + node1 = state2.(IntermediateUseState).getFstNode() and + node2 = state2.(IntermediateUseState).getSndNode() + ) + } + + predicate isBarrier(DataFlow::Node node, FlowState state) { + exists(Init call | node.asExpr() = call.(MethodCall).getQualifier() | + state instanceof UninitializedFlowState + or + state.(InitializedFlowState).getInitCall() != call + ) + } + } + + module GetInstanceToInitToUseFlow = DataFlow::GlobalWithState; + + GetInstance getInstantiationFromUse( + Use use, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink + ) { + src.getNode().asExpr() = result and + sink.getNode().asExpr() = use.(MethodCall).getQualifier() and + GetInstanceToInitToUseFlow::flowPath(src, sink) + } + + GetInstance getInstantiationFromInit( + Init init, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink + ) { + src.getNode().asExpr() = result and + sink.getNode().asExpr() = init.(MethodCall).getQualifier() and + GetInstanceToInitToUseFlow::flowPath(src, sink) + } + + Init getInitFromUse( + Use use, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink + ) { + src.getNode().asExpr() = result.(MethodCall).getQualifier() and + sink.getNode().asExpr() = use.(MethodCall).getQualifier() and + GetInstanceToInitToUseFlow::flowPath(src, sink) + } + + predicate hasInit(Use use) { exists(getInitFromUse(use, _, _)) } + + Use getAnIntermediateUseFromFinalUse( + Use final, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink + ) { + not final.isIntermediate() and + result.isIntermediate() and + src.getNode().asExpr() = result.(MethodCall).getQualifier() and + sink.getNode().asExpr() = final.(MethodCall).getQualifier() and + GetInstanceToInitToUseFlow::flowPath(src, sink) + } +} \ No newline at end of file diff --git a/java/ql/lib/experimental/quantum/Language.qll b/java/ql/lib/experimental/quantum/Language.qll index 59164901c10c..f3f97ae14d07 100644 --- a/java/ql/lib/experimental/quantum/Language.qll +++ b/java/ql/lib/experimental/quantum/Language.qll @@ -216,3 +216,4 @@ module ArtifactFlow = DataFlow::Global; // Import library-specific modeling import JCA +import BouncyCastle From c588d11297acc44f12a4d27a4059be2db3debcb1 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Thu, 15 May 2025 18:26:40 +0200 Subject: [PATCH 02/32] Removed unused getIntermediateUse function --- java/ql/lib/experimental/quantum/BouncyCastle.qll | 4 ---- 1 file changed, 4 deletions(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll index 7ef83ca99667..3cc9ef31b505 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -113,10 +113,6 @@ module Signers { */ module FlowAnalysis = NewToInitToUseFlowAnalysis; - UseCall getIntermediateUse(UseCall use) { - result = FlowAnalysis::getAnIntermediateUseFromFinalUse(use, _, _) - } - /** * A signing operation instance is a call to either `update()`, `generateSignature()`, * or `verifySignature()` on a `Signer` instance. From e9c6c3350a0d6dfb9103a74416ca26695dbd4443 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Fri, 16 May 2025 15:50:56 +0200 Subject: [PATCH 03/32] Multiple bug fixes in BouncyCastle signature model --- .../lib/experimental/quantum/BouncyCastle.qll | 82 ++++++------------- .../BouncyCastle/AlgorithmInstances.qll | 7 -- .../SignatureAlgorithmInstances.qll | 57 +++++++------ 3 files changed, 56 insertions(+), 90 deletions(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll index 3cc9ef31b505..edb961e632f1 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -1,17 +1,13 @@ - import java /** * Models for the signature algorithms defined by the `org.bouncycastle.crypto.signers` package. - * */ module Signers { import Language import BouncyCastle.FlowAnalysis import BouncyCastle.AlgorithmInstances - abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } - /** * A model of the `Signer` class in Bouncy Castle. */ @@ -21,14 +17,12 @@ module Signers { this.getName().matches("%Signer") } - MethodCall getAnInitCall() { - result = this.getAMethodCall("init") - } + MethodCall getAnInitCall() { result = this.getAMethodCall("init") } MethodCall getAUseCall() { result = this.getAMethodCall(["update", "generateSignature", "verifySignature"]) } - + MethodCall getAMethodCall(string name) { result.getCallee().hasQualifiedName("org.bouncycastle.crypto.signers", this.getName(), name) } @@ -36,55 +30,36 @@ module Signers { /** * BouncyCastle algorithms are instantiated by calling the constructor of the - * corresponding class. The algorithm is implicitly defined by the constructor - * call. + * corresponding class. */ - class NewCall extends SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr { - NewCall() { - this.getConstructedType() instanceof Signer - } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - result = getSignatureAlgorithmInstanceFromType(super.getConstructedType()) - } - - // TODO: Since the algorithm is implicitly defined by the constructor, should - // the input node be `this`? - override Crypto::ConsumerInputDataFlowNode getInputNode() { - result.asExpr() = this - } - } + class NewCall = SignatureAlgorithmInstance; /** * The type is instantiated by a constructor call and initialized by a call to * `init()` which takes two arguments. The first argument is a flag indicating * whether the operation is signing data or verifying a signature, and the - * second is the key to use. + * second is the key to use. */ class InitCall extends MethodCall { - InitCall() { - this = any(Signer signer).getAnInitCall() - } + InitCall() { this = any(Signer signer).getAnInitCall() } Expr getForSigningArg() { result = this.getArgument(0) } Expr getKeyArg() { result = this.getArgument(1) } - Crypto::KeyOperationAlgorithmInstance getAlgorithm() { - result = getSignatureAlgorithmInstanceFromType(this.getReceiverType()) - } - + // TODO: Support dataflow for the operation sub-type. Crypto::KeyOperationSubtype getKeyOperationSubtype() { - ( + if this.isOperationSubTypeKnown() + then this.getForSigningArg().(BooleanLiteral).getBooleanValue() = true and result = Crypto::TSignMode() - ) or ( + or this.getForSigningArg().(BooleanLiteral).getBooleanValue() = false and result = Crypto::TVerifyMode() - ) or ( - result = Crypto::TUnknownKeyOperationMode() - ) + else result = Crypto::TUnknownKeyOperationMode() } + + predicate isOperationSubTypeKnown() { this.getForSigningArg() instanceof BooleanLiteral } } /** @@ -93,19 +68,13 @@ module Signers { * verify the signature, respectively. */ class UseCall extends MethodCall { - UseCall() { - this = any(Signer signer).getAUseCall() - } + UseCall() { this = any(Signer signer).getAUseCall() } - predicate isIntermediate() { - this.getCallee().getName() = "update" - } + predicate isIntermediate() { this.getCallee().getName() = "update" } Expr getInput() { result = this.getArgument(0) } - Expr getOutput() { - result = this - } + Expr getOutput() { result = this } } /** @@ -118,10 +87,12 @@ module Signers { * or `verifySignature()` on a `Signer` instance. */ class SignatureOperationInstance extends Crypto::KeyOperationInstance instanceof UseCall { - + SignatureOperationInstance() { not this.isIntermediate() } + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - result = FlowAnalysis::getInstantiationFromInit(this.getInitCall(), _, _) + result = FlowAnalysis::getInstantiationFromUse(this, _, _) } + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { if FlowAnalysis::hasInit(this) then result = this.getInitCall().getKeyOperationSubtype() @@ -144,17 +115,12 @@ module Signers { result.asExpr() = super.getOutput() } - InitCall getInitCall() { - result = FlowAnalysis::getInitFromUse(this, _, _) - } + InitCall getInitCall() { result = FlowAnalysis::getInitFromUse(this, _, _) } UseCall getAnUpdateCall() { - ( - super.isIntermediate() and result = this - ) or ( - result = FlowAnalysis::getAnIntermediateUseFromFinalUse(this, _, _) - ) + super.isIntermediate() and result = this + or + result = FlowAnalysis::getAnIntermediateUseFromFinalUse(this, _, _) } } } - diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index a493da9689ba..fa2e964787ed 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -1,10 +1,3 @@ import java import experimental.quantum.Language import AlgorithmInstances.SignatureAlgorithmInstances - -SignatureAlgorithmInstance getSignatureAlgorithmInstanceFromType(Type t) { - t.getName() = "Ed25519Signer" and result instanceof Ed25519AlgorithmInstance - or - t.getName() = "Ed448Signer" and result instanceof Ed448AlgorithmInstance -} - diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll index 27a5833c49d2..b256c575950d 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll @@ -1,44 +1,51 @@ import java import experimental.quantum.Language -abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance { +/** + * Signature algorithms are implicitly defined by the constructor. + */ +private abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result = this + } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { + none() + } +} - // TODO: Could potentially be used to model signature modes like Ed25519ph and Ed25519ctx. +class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr { + SignatureAlgorithmInstance() { + super.getConstructedType().getPackage().getName() = "org.bouncycastle.crypto.signers" and + super.getConstructedType().getName().matches("%Signer") + + } + + // TODO: Could potentially be used to model signature modes like Ed25519ph and Ed25519ctx. override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } -} -class Ed25519AlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { - Ed25519AlgorithmInstance() { - this.getConstructedType().hasQualifiedName("org.bouncycastle.crypto.signers", "Ed25519Signer") + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + nameToTypeAndKeySizeMapping(this.getRawAlgorithmName(), _, result) } - override string getRawAlgorithmName() { result = "Ed25519" } - - override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) - } - - // TODO: May be redundant. - override string getKeySizeFixed() { result = "256" } -} - -class Ed448AlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { - Ed448AlgorithmInstance() { - this.getConstructedType().hasQualifiedName("org.bouncycastle.crypto.signers", "Ed448Signer") + override string getRawAlgorithmName() { + result = super.getConstructedType().getName().splitAt("Signer", 0) } - override string getRawAlgorithmName() { result = "Ed448" } - - override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) + override string getKeySizeFixed() { + nameToTypeAndKeySizeMapping(this.getRawAlgorithmName(), result, _) } +} - // TODO: May be redundant. - override string getKeySizeFixed() { result = "448" } +predicate nameToTypeAndKeySizeMapping(string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm) { + name = "Ed25519" and keySize = "256" and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) + or + name = "Ed448" and keySize = "448" and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) } + From d8cdd3c2d1d762012c4499e4aa14c7b6c8f49c34 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Wed, 21 May 2025 12:27:58 +0200 Subject: [PATCH 04/32] Added support for BouncyCastle key generation algorithms This commit adds the `KeyGenerationOperationInstance` and `KeyGenerationAlgorithmInstance` types to the BouncyCastle model. It also adds data flow support from key pairs to the corresponding public and private components. --- .../lib/experimental/quantum/BouncyCastle.qll | 150 +++++++++++++++++- .../BouncyCastle/AlgorithmInstances.qll | 1 + .../KeyGenerationAlgorithmInstance.qll | 85 ++++++++++ .../SignatureAlgorithmInstances.qll | 49 +++--- .../quantum/BouncyCastle/FlowAnalysis.qll | 85 +++++++--- 5 files changed, 311 insertions(+), 59 deletions(-) create mode 100644 java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/KeyGenerationAlgorithmInstance.qll diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll index edb961e632f1..a305f71f5c91 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -32,7 +32,7 @@ module Signers { * BouncyCastle algorithms are instantiated by calling the constructor of the * corresponding class. */ - class NewCall = SignatureAlgorithmInstance; + private class NewCall = SignatureAlgorithmInstance; /** * The type is instantiated by a constructor call and initialized by a call to @@ -40,7 +40,7 @@ module Signers { * whether the operation is signing data or verifying a signature, and the * second is the key to use. */ - class InitCall extends MethodCall { + private class InitCall extends MethodCall { InitCall() { this = any(Signer signer).getAnInitCall() } Expr getForSigningArg() { result = this.getArgument(0) } @@ -67,7 +67,7 @@ module Signers { * `generateSignature()` or `verifySignature()` methods are used to produce or * verify the signature, respectively. */ - class UseCall extends MethodCall { + private class UseCall extends MethodCall { UseCall() { this = any(Signer signer).getAUseCall() } predicate isIntermediate() { this.getCallee().getName() = "update" } @@ -80,7 +80,7 @@ module Signers { /** * Instantiate the flow analysis module for the `Signer` class. */ - module FlowAnalysis = NewToInitToUseFlowAnalysis; + private module FlowAnalysis = NewToInitToUseFlowAnalysis; /** * A signing operation instance is a call to either `update()`, `generateSignature()`, @@ -90,7 +90,7 @@ module Signers { SignatureOperationInstance() { not this.isIntermediate() } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - result = FlowAnalysis::getInstantiationFromUse(this, _, _) + result = FlowAnalysis::getNewFromUse(this, _, _) } override Crypto::KeyOperationSubtype getKeyOperationSubtype() { @@ -111,16 +111,150 @@ module Signers { override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { this.getKeyOperationSubtype() = Crypto::TSignMode() and - not super.isIntermediate() and result.asExpr() = super.getOutput() } InitCall getInitCall() { result = FlowAnalysis::getInitFromUse(this, _, _) } UseCall getAnUpdateCall() { - super.isIntermediate() and result = this - or result = FlowAnalysis::getAnIntermediateUseFromFinalUse(this, _, _) } } } + +/** + * Models for the key generation algorithms defined by the `org.bouncycastle.crypto.generators` package. + */ +module Generators { + import Language + import BouncyCastle.FlowAnalysis + import BouncyCastle.AlgorithmInstances + + /** + * A model of the `KeyGenerator` and `KeyPairGenerator` classes in Bouncy Castle. + */ + class KeyGenerator extends RefType { + Crypto::KeyArtifactType type; + + KeyGenerator() { + this.getPackage().getName() = "org.bouncycastle.crypto.generators" and + ( + this.getName().matches("%KeyGenerator") and type instanceof Crypto::TSymmetricKeyType + or + this.getName().matches("%KeyPairGenerator") and type instanceof Crypto::TAsymmetricKeyType + ) + } + + MethodCall getAnInitCall() { result = this.getAMethodCall("init") } + + MethodCall getAUseCall() { result = this.getAMethodCall(["generateKey", "generateKeyPair"]) } + + MethodCall getAMethodCall(string name) { + result + .getCallee() + .hasQualifiedName("org.bouncycastle.crypto.generators", this.getName(), name) + } + + Crypto::KeyArtifactType getKeyType() { result = type } + + string getRawAlgorithmName() { + this.getKeyType() = Crypto::TSymmetricKeyType() and + result = this.getName().splitAt("KeyGenerator", 0) + or + this.getKeyType() = Crypto::TAsymmetricKeyType() and + result = this.getName().splitAt("KeyPairGenerator", 0) + } + } + + /** + * This type is used to model data flow from a key pair to the private and + * public components of the key pair. + */ + class KeyPair extends RefType { + KeyPair() { + this.getPackage().getName() = "org.bouncycastle.crypto" and + this.getName() = "%KeyPair" // `AsymmetricCipherKeyPair` or `EphemeralKeyPair` + } + + MethodCall getPublicKeyCall() { result = this.getAMethodCall("getPublic") } + + MethodCall getPrivateKeyCall() { result = this.getAMethodCall("getPrivate") } + + MethodCall getAMethodCall(string name) { + result.getCallee().hasQualifiedName("org.bouncycastle.crypto", this.getName(), name) + } + } + + /** + * BouncyCastle algorithms are instantiated by calling the constructor of the + * corresponding class. + */ + private class KeyGeneratorNewCall = KeyGenerationAlgorithmInstance; + + /** + * The type is instantiated by a constructor call and initialized by a call to + * `init()` which takes a single `KeyGenerationParameters` argument. + */ + private class KeyGeneratorInitCall extends MethodCall { + KeyGenerator gen; + + KeyGeneratorInitCall() { this = gen.getAnInitCall() } + + // TODO: We may need to model this using the `parameters` argument passed to + // the `init()` method. + Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + } + + /** + * The `generateKey()` and `generateKeyPair()` methods are used to generate + * the resulting key, depending on the type of the generator. + */ + private class KeyGeneratorUseCall extends MethodCall { + KeyGenerator gen; + + KeyGeneratorUseCall() { this = gen.getAUseCall() } + + // Since key generators don't have `update()` methods, this is always false. + predicate isIntermediate() { none() } + + Crypto::KeyArtifactType getKeyType() { result = gen.getKeyType() } + + Expr getOutput() { result = this } + } + + private module KeyGeneratorFlow = + NewToInitToUseFlowAnalysis; + + /** + * A key generation operation instance is a call to `generateKey()` or + * `generateKeyPair()` on a key generator defined under + * `org.bouncycastle.crypto.generators`. + */ + class KeyGenerationOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGeneratorUseCall + { + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result = KeyGeneratorFlow::getNewFromUse(this, _, _) + } + + override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { + result.asExpr() = super.getOutput() + } + + override Crypto::KeyArtifactType getOutputKeyType() { result = super.getKeyType() } + + override string getKeySizeFixed() { + result = KeyGeneratorFlow::getNewFromUse(this, _, _).getKeySizeFixed() + } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { + result = KeyGeneratorFlow::getInitFromUse(this, _, _).getKeySizeConsumer() + } + } + + class KeyGenerationParameters extends RefType { + KeyGenerationParameters() { + this.getPackage().getName() = "org.bouncycastle.crypto.generators" and + this.getName().matches("%KeyGenerationParameters") + } + } +} diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index fa2e964787ed..9f9ec765fe8a 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -1,3 +1,4 @@ import java import experimental.quantum.Language import AlgorithmInstances.SignatureAlgorithmInstances +import AlgorithmInstances.KeyGenerationAlgorithmInstance diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/KeyGenerationAlgorithmInstance.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/KeyGenerationAlgorithmInstance.qll new file mode 100644 index 000000000000..bf51984d28d4 --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/KeyGenerationAlgorithmInstance.qll @@ -0,0 +1,85 @@ +import java +import experimental.quantum.Language + +/** + * Key generation algorithms are implicitly defined by the constructor. + */ +abstract private class KeyGenerationAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } +} + +class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, + KeyGenerationAlgorithmValueConsumer instanceof ClassInstanceExpr +{ + KeyGenerationAlgorithmInstance() { + super.getConstructedType() instanceof Generators::KeyGenerator + } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result) + } + + override string getKeySizeFixed() { + nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) + } + + override string getRawAlgorithmName() { + result = super.getConstructedType().(Generators::KeyGenerator).getRawAlgorithmName() + } + + // This is overridden if a specific generator type has a key size consumer. + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } +} + +class CramerShoupKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance { + CramerShoupKeyGenerationAlgorithmInstance() { super.getRawAlgorithmName() = "CramerShoup" } + + override string getKeySizeFixed() { none() } + + // TODO: Model flow from the `CramerShoupParametersGenerator::init` method + // (which takes a size of the prime order group in bits), via the + // `CramerShoupParameters` object, to the key-pair generator. + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + result = Crypto::KeyOpAlg::TAsymmetricCipher(Crypto::KeyOpAlg::OtherAsymmetricCipherType()) + } +} + +class DESedeKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance { + DESedeKeyGenerationAlgorithmInstance() { super.getRawAlgorithmName() = "DESede" } + + // Key size is 112 or 168 bits. + override string getKeySizeFixed() { none() } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::TripleDES()) + } +} + +private predicate nameToKeySizeAndAlgorithmMapping( + string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm +) { + // DES + name = "DES" and + keySize = "56" and + algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES()) + or + // Ed25519, Ed25519ph, and Ed25519ctx + name = "Ed25519" and + keySize = "256" and + algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) + or + // Ed448 and Ed448ph + name = "Ed448" and + keySize = "448" and + algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) +} diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll index b256c575950d..0cacfe607d39 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll @@ -4,48 +4,47 @@ import experimental.quantum.Language /** * Signature algorithms are implicitly defined by the constructor. */ -private abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - result = this - } +abstract private class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } - override Crypto::ConsumerInputDataFlowNode getInputNode() { - none() - } + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } } -class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr { - SignatureAlgorithmInstance() { - super.getConstructedType().getPackage().getName() = "org.bouncycastle.crypto.signers" and - super.getConstructedType().getName().matches("%Signer") - - } +class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, + SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr +{ + SignatureAlgorithmInstance() { super.getConstructedType() instanceof Signers::Signer } - // TODO: Could potentially be used to model signature modes like Ed25519ph and Ed25519ctx. + // TODO: Could potentially be used to model signature modes like Ed25519ph and Ed25519ctx. override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } - + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - nameToTypeAndKeySizeMapping(this.getRawAlgorithmName(), _, result) + nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result) } - override string getRawAlgorithmName() { + override string getRawAlgorithmName() { result = super.getConstructedType().getName().splitAt("Signer", 0) } - override string getKeySizeFixed() { - nameToTypeAndKeySizeMapping(this.getRawAlgorithmName(), result, _) + override string getKeySizeFixed() { + nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) } } -predicate nameToTypeAndKeySizeMapping(string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm) { - name = "Ed25519" and keySize = "256" and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) +private predicate nameToKeySizeAndAlgorithmMapping( + string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm +) { + // Ed25519, Ed25519ph, and Ed25519ctx + name = "Ed25519%" and + keySize = "256" and + algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) or - name = "Ed448" and keySize = "448" and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) + // Ed448 and Ed448ph + name = "Ed448%" and + keySize = "448" and + algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) } - - - diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll index d2f02e3f9a52..1da88aad3b2f 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll @@ -1,4 +1,5 @@ import java +import experimental.quantum.Language import semmle.code.java.dataflow.DataFlow /** @@ -17,18 +18,18 @@ signature class UseCallSig instanceof MethodCall { } /** - * An generic analysis module for analyzing the `getInstance()` to `initialize()` - * to `doOperation()` pattern in the JCA, and similar patterns in other libraries. + * A generic analysis module for analyzing data flow from class instantiation, + * to `init()`, to `doOperation()` in BouncyCastle, and similar patterns in + * other libraries. * * For example: * ``` - * kpg = KeyPairGenerator.getInstance(); - * kpg.initialize(...); - * kpg.generate(...); + * gen = new KeyGenerator(...); + * gen.init(...); + * gen.generateKeyPair(...); * ``` */ -module NewToInitToUseFlowAnalysis -{ +module NewToInitToUseFlowAnalysis { newtype TFlowState = TUninitialized() or TInitialized(Init call) or @@ -43,12 +44,13 @@ module NewToInitToUseFlowAnalysis; + module NewToInitToUseFlow = DataFlow::GlobalWithState; - GetInstance getInstantiationFromUse( - Use use, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink - ) { + New getNewFromUse(Use use, NewToInitToUseFlow::PathNode src, NewToInitToUseFlow::PathNode sink) { src.getNode().asExpr() = result and sink.getNode().asExpr() = use.(MethodCall).getQualifier() and - GetInstanceToInitToUseFlow::flowPath(src, sink) + NewToInitToUseFlow::flowPath(src, sink) } - GetInstance getInstantiationFromInit( - Init init, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink - ) { + New getNewFromInit(Init init, NewToInitToUseFlow::PathNode src, NewToInitToUseFlow::PathNode sink) { src.getNode().asExpr() = result and sink.getNode().asExpr() = init.(MethodCall).getQualifier() and - GetInstanceToInitToUseFlow::flowPath(src, sink) + NewToInitToUseFlow::flowPath(src, sink) } - Init getInitFromUse( - Use use, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink - ) { + Init getInitFromUse(Use use, NewToInitToUseFlow::PathNode src, NewToInitToUseFlow::PathNode sink) { src.getNode().asExpr() = result.(MethodCall).getQualifier() and sink.getNode().asExpr() = use.(MethodCall).getQualifier() and - GetInstanceToInitToUseFlow::flowPath(src, sink) + NewToInitToUseFlow::flowPath(src, sink) } predicate hasInit(Use use) { exists(getInitFromUse(use, _, _)) } Use getAnIntermediateUseFromFinalUse( - Use final, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink + Use final, NewToInitToUseFlow::PathNode src, NewToInitToUseFlow::PathNode sink ) { not final.isIntermediate() and result.isIntermediate() and src.getNode().asExpr() = result.(MethodCall).getQualifier() and sink.getNode().asExpr() = final.(MethodCall).getQualifier() and - GetInstanceToInitToUseFlow::flowPath(src, sink) + NewToInitToUseFlow::flowPath(src, sink) + } +} + +/** + * Model data flow from a key pair to the public and private components of the + * key pair. + */ +class KeyAdditionalFlowSteps extends MethodCall { + KeyAdditionalFlowSteps() { + this.getCallee().getDeclaringType().getPackage().getName() = "org.bouncycastle.crypto" and + this.getCallee().getDeclaringType().getName().matches("%KeyPair") and + ( + this.getCallee().getName().matches("getPublic") + or + this.getCallee().getName().matches("getPrivate") + ) } -} \ No newline at end of file + + DataFlow::Node getInputNode() { result.asExpr() = this.getQualifier() } + + DataFlow::Node getOutputNode() { result.asExpr() = this } +} + +predicate additionalFlowSteps(DataFlow::Node node1, DataFlow::Node node2) { + exists(KeyAdditionalFlowSteps call | + node1 = call.getInputNode() and + node2 = call.getOutputNode() + ) +} + +class ArtifactAdditionalFlowStep extends AdditionalFlowInputStep { + DataFlow::Node output; + + ArtifactAdditionalFlowStep() { additionalFlowSteps(this, output) } + + override DataFlow::Node getOutput() { result = output } +} From 45416d28cbdda07ba73427b8196c0a6c4282c8ea Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Fri, 23 May 2025 11:42:53 +0200 Subject: [PATCH 05/32] Refactored algorithm instances This commit also adds associated elliptic curves to the key generation and key nodes. --- .../lib/experimental/quantum/BouncyCastle.qll | 44 +++-- .../BouncyCastle/AlgorithmInstances.qll | 160 +++++++++++++++++- .../KeyGenerationAlgorithmInstance.qll | 85 ---------- .../SignatureAlgorithmInstances.qll | 50 ------ .../BouncyCastle/AlgorithmValueConsumers.qll | 26 +++ 5 files changed, 204 insertions(+), 161 deletions(-) delete mode 100644 java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/KeyGenerationAlgorithmInstance.qll delete mode 100644 java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll create mode 100644 java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll index a305f71f5c91..25ce23ab136c 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -24,15 +24,15 @@ module Signers { } MethodCall getAMethodCall(string name) { - result.getCallee().hasQualifiedName("org.bouncycastle.crypto.signers", this.getName(), name) + result.getCallee().hasQualifiedName(this.getPackage().getName(), this.getName(), name) } } /** * BouncyCastle algorithms are instantiated by calling the constructor of the - * corresponding class. + * corresponding class, which also represents the algorithm instance. */ - private class NewCall = SignatureAlgorithmInstance; + private class SignerNewCall = SignatureAlgorithmInstance; /** * The type is instantiated by a constructor call and initialized by a call to @@ -40,8 +40,8 @@ module Signers { * whether the operation is signing data or verifying a signature, and the * second is the key to use. */ - private class InitCall extends MethodCall { - InitCall() { this = any(Signer signer).getAnInitCall() } + private class SignerInitCall extends MethodCall { + SignerInitCall() { this = any(Signer signer).getAnInitCall() } Expr getForSigningArg() { result = this.getArgument(0) } @@ -67,8 +67,8 @@ module Signers { * `generateSignature()` or `verifySignature()` methods are used to produce or * verify the signature, respectively. */ - private class UseCall extends MethodCall { - UseCall() { this = any(Signer signer).getAUseCall() } + private class SignerUseCall extends MethodCall { + SignerUseCall() { this = any(Signer signer).getAUseCall() } predicate isIntermediate() { this.getCallee().getName() = "update" } @@ -80,13 +80,14 @@ module Signers { /** * Instantiate the flow analysis module for the `Signer` class. */ - private module FlowAnalysis = NewToInitToUseFlowAnalysis; + private module FlowAnalysis = + NewToInitToUseFlowAnalysis; /** * A signing operation instance is a call to either `update()`, `generateSignature()`, * or `verifySignature()` on a `Signer` instance. */ - class SignatureOperationInstance extends Crypto::KeyOperationInstance instanceof UseCall { + class SignatureOperationInstance extends Crypto::KeyOperationInstance instanceof SignerUseCall { SignatureOperationInstance() { not this.isIntermediate() } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { @@ -114,9 +115,9 @@ module Signers { result.asExpr() = super.getOutput() } - InitCall getInitCall() { result = FlowAnalysis::getInitFromUse(this, _, _) } + SignerInitCall getInitCall() { result = FlowAnalysis::getInitFromUse(this, _, _) } - UseCall getAnUpdateCall() { + SignerUseCall getAnUpdateCall() { result = FlowAnalysis::getAnIntermediateUseFromFinalUse(this, _, _) } } @@ -150,20 +151,10 @@ module Generators { MethodCall getAUseCall() { result = this.getAMethodCall(["generateKey", "generateKeyPair"]) } MethodCall getAMethodCall(string name) { - result - .getCallee() - .hasQualifiedName("org.bouncycastle.crypto.generators", this.getName(), name) + result.getCallee().hasQualifiedName(this.getPackage().getName(), this.getName(), name) } Crypto::KeyArtifactType getKeyType() { result = type } - - string getRawAlgorithmName() { - this.getKeyType() = Crypto::TSymmetricKeyType() and - result = this.getName().splitAt("KeyGenerator", 0) - or - this.getKeyType() = Crypto::TAsymmetricKeyType() and - result = this.getName().splitAt("KeyPairGenerator", 0) - } } /** @@ -187,7 +178,7 @@ module Generators { /** * BouncyCastle algorithms are instantiated by calling the constructor of the - * corresponding class. + * corresponding class, which also represents the algorithm instance. */ private class KeyGeneratorNewCall = KeyGenerationAlgorithmInstance; @@ -250,10 +241,15 @@ module Generators { result = KeyGeneratorFlow::getInitFromUse(this, _, _).getKeySizeConsumer() } } +} +/** + * Models for cryptographic parameters defined by the `org.bouncycastle.crypto.params` package. + */ +module Parameters { class KeyGenerationParameters extends RefType { KeyGenerationParameters() { - this.getPackage().getName() = "org.bouncycastle.crypto.generators" and + this.getPackage().getName() = "org.bouncycastle.crypto.params" and this.getName().matches("%KeyGenerationParameters") } } diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index 9f9ec765fe8a..67ed5ab2af30 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -1,4 +1,160 @@ import java import experimental.quantum.Language -import AlgorithmInstances.SignatureAlgorithmInstances -import AlgorithmInstances.KeyGenerationAlgorithmInstance +import AlgorithmValueConsumers + +abstract private class EllipticCurveAlgorithmInstance extends Crypto::EllipticCurveInstance, + EllipticCurveAlgorithmValueConsumer +{ + override Crypto::TEllipticCurveType getEllipticCurveType() { + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), _, result) + } + + override string getKeySize() { + exists(int keySize | + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), keySize, _) and + result = keySize.toString() + ) + } +} + +/** + * Signature algorithms. + */ +abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, + SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr +{ + // TODO: Could potentially be used to model signature modes like Ed25519ph and Ed25519ctx. + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + signatureNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result) + } + + override string getKeySizeFixed() { + signatureNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) + } + + override string getRawAlgorithmName() { + typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + } +} + +abstract private class EllipticCurveSignatureAlgorithmInstance extends SignatureAlgorithmInstance, + EllipticCurveAlgorithmInstance +{ } + +class Ed25519SignatureAlgorithmInstance extends EllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr +{ + Ed25519SignatureAlgorithmInstance() { + super.getConstructedType() instanceof Signers::Signer and + super.getConstructedType().getName().matches("Ed25519%") + } + + override string getRawEllipticCurveName() { result = "CURVE25519" } +} + +class Ed448SignatureAlgorithmInstance extends EllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr +{ + Ed448SignatureAlgorithmInstance() { + super.getConstructedType() instanceof Signers::Signer and + super.getConstructedType().getName().matches("Ed448%") + } + + override string getRawAlgorithmName() { + typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + } + + override string getRawEllipticCurveName() { result = "CURVE448" } +} + +/** + * Key generation algorithms. + */ +abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, + KeyGenerationAlgorithmValueConsumer instanceof ClassInstanceExpr +{ + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } + + // TODO: Model flow from the parameter specification to the key generator. + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + generatorNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result) + } + + override string getKeySizeFixed() { + generatorNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) + } + + override string getRawAlgorithmName() { + typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + } +} + +abstract private class EllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance, + EllipticCurveAlgorithmInstance +{ } + +class Ed25519KeyGenerationAlgorithmInstance extends EllipticCurveKeyGenerationAlgorithmInstance instanceof ClassInstanceExpr +{ + Ed25519KeyGenerationAlgorithmInstance() { + super.getConstructedType() instanceof Generators::KeyGenerator and + super.getConstructedType().getName().matches("Ed25519%") + } + + override string getRawEllipticCurveName() { result = "CURVE25519" } +} + +class Ed448KeyGenerationAlgorithmInstance extends EllipticCurveKeyGenerationAlgorithmInstance instanceof ClassInstanceExpr +{ + Ed448KeyGenerationAlgorithmInstance() { + super.getConstructedType() instanceof Generators::KeyGenerator and + super.getConstructedType().getName().matches("Ed448%") + } + + override string getRawEllipticCurveName() { result = "CURVE25519" } +} + +/** + * Private predicates mapping type names to raw names, key sizes and algorithms. + */ +bindingset[typeName] +private predicate typeNameToRawAlgorithmName(string typeName, string algorithmName) { + // Ed25519, Ed25519ph, and Ed25519ctx key generators and signers + typeName.matches("Ed25519%") and + algorithmName = "ED25519" + or + // Ed448 and Ed448ph key generators and signers + typeName.matches("Ed448%") and + algorithmName = "ED448" +} + +private predicate signatureNameToKeySizeAndAlgorithmMapping( + string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm +) { + name = "ED25519" and + keySize = "256" and + algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) + or + name = "ED448" and + keySize = "448" and + algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) +} + +private predicate generatorNameToKeySizeAndAlgorithmMapping( + string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm +) { + name = "ED25519" and + keySize = "256" and + algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) + or + name = "ED448" and + keySize = "448" and + algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) +} diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/KeyGenerationAlgorithmInstance.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/KeyGenerationAlgorithmInstance.qll deleted file mode 100644 index bf51984d28d4..000000000000 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/KeyGenerationAlgorithmInstance.qll +++ /dev/null @@ -1,85 +0,0 @@ -import java -import experimental.quantum.Language - -/** - * Key generation algorithms are implicitly defined by the constructor. - */ -abstract private class KeyGenerationAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } - - override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } -} - -class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, - KeyGenerationAlgorithmValueConsumer instanceof ClassInstanceExpr -{ - KeyGenerationAlgorithmInstance() { - super.getConstructedType() instanceof Generators::KeyGenerator - } - - override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } - - override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } - - override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result) - } - - override string getKeySizeFixed() { - nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) - } - - override string getRawAlgorithmName() { - result = super.getConstructedType().(Generators::KeyGenerator).getRawAlgorithmName() - } - - // This is overridden if a specific generator type has a key size consumer. - override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } -} - -class CramerShoupKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance { - CramerShoupKeyGenerationAlgorithmInstance() { super.getRawAlgorithmName() = "CramerShoup" } - - override string getKeySizeFixed() { none() } - - // TODO: Model flow from the `CramerShoupParametersGenerator::init` method - // (which takes a size of the prime order group in bits), via the - // `CramerShoupParameters` object, to the key-pair generator. - override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } - - override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - result = Crypto::KeyOpAlg::TAsymmetricCipher(Crypto::KeyOpAlg::OtherAsymmetricCipherType()) - } -} - -class DESedeKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance { - DESedeKeyGenerationAlgorithmInstance() { super.getRawAlgorithmName() = "DESede" } - - // Key size is 112 or 168 bits. - override string getKeySizeFixed() { none() } - - override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } - - override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::TripleDES()) - } -} - -private predicate nameToKeySizeAndAlgorithmMapping( - string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm -) { - // DES - name = "DES" and - keySize = "56" and - algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES()) - or - // Ed25519, Ed25519ph, and Ed25519ctx - name = "Ed25519" and - keySize = "256" and - algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) - or - // Ed448 and Ed448ph - name = "Ed448" and - keySize = "448" and - algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) -} diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll deleted file mode 100644 index 0cacfe607d39..000000000000 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances/SignatureAlgorithmInstances.qll +++ /dev/null @@ -1,50 +0,0 @@ -import java -import experimental.quantum.Language - -/** - * Signature algorithms are implicitly defined by the constructor. - */ -abstract private class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } - - override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } -} - -class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, - SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr -{ - SignatureAlgorithmInstance() { super.getConstructedType() instanceof Signers::Signer } - - // TODO: Could potentially be used to model signature modes like Ed25519ph and Ed25519ctx. - override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } - - override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } - - override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } - - override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result) - } - - override string getRawAlgorithmName() { - result = super.getConstructedType().getName().splitAt("Signer", 0) - } - - override string getKeySizeFixed() { - nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) - } -} - -private predicate nameToKeySizeAndAlgorithmMapping( - string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm -) { - // Ed25519, Ed25519ph, and Ed25519ctx - name = "Ed25519%" and - keySize = "256" and - algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) - or - // Ed448 and Ed448ph - name = "Ed448%" and - keySize = "448" and - algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) -} diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll new file mode 100644 index 000000000000..37f7d7acf17a --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll @@ -0,0 +1,26 @@ +import java +import experimental.quantum.Language + +abstract class HashAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } + +abstract class CipherAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } + +abstract class EllipticCurveAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } + +/** + * Signature algorithms are implicitly defined by the constructor. + */ +abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } +} + +/** + * Key generation algorithms are implicitly defined by the constructor. + */ +abstract class KeyGenerationAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } +} From 9c602f8e38e850195f7dd36f99896339b6e864d9 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Fri, 23 May 2025 13:40:58 +0200 Subject: [PATCH 06/32] Updated returned key sizes to be integers --- .../lib/experimental/quantum/BouncyCastle.qll | 2 +- .../BouncyCastle/AlgorithmInstances.qll | 23 ++++++++----------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll index 25ce23ab136c..5a82a2cd78fa 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -233,7 +233,7 @@ module Generators { override Crypto::KeyArtifactType getOutputKeyType() { result = super.getKeyType() } - override string getKeySizeFixed() { + override int getKeySizeFixed() { result = KeyGeneratorFlow::getNewFromUse(this, _, _).getKeySizeFixed() } diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index 67ed5ab2af30..0d3920d920d1 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -9,11 +9,8 @@ abstract private class EllipticCurveAlgorithmInstance extends Crypto::EllipticCu Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), _, result) } - override string getKeySize() { - exists(int keySize | - Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), keySize, _) and - result = keySize.toString() - ) + override int getKeySize() { + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), result, _) } } @@ -34,7 +31,7 @@ abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmI signatureNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result) } - override string getKeySizeFixed() { + override int getKeySizeFixed() { signatureNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) } @@ -88,7 +85,7 @@ abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgori generatorNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result) } - override string getKeySizeFixed() { + override int getKeySizeFixed() { generatorNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) } @@ -136,25 +133,25 @@ private predicate typeNameToRawAlgorithmName(string typeName, string algorithmNa } private predicate signatureNameToKeySizeAndAlgorithmMapping( - string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm + string name, int keySize, Crypto::KeyOpAlg::Algorithm algorithm ) { name = "ED25519" and - keySize = "256" and + keySize = 256 and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) or name = "ED448" and - keySize = "448" and + keySize = 448 and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) } private predicate generatorNameToKeySizeAndAlgorithmMapping( - string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm + string name, int keySize, Crypto::KeyOpAlg::Algorithm algorithm ) { name = "ED25519" and - keySize = "256" and + keySize = 256 and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) or name = "ED448" and - keySize = "448" and + keySize = 448 and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) } From 4aecf8b6672155a4b48ceef9ab535eb2a7cb91fe Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Fri, 23 May 2025 15:31:55 +0200 Subject: [PATCH 07/32] Added stubs for BouncyCastle EdDSA signature algorithms and key generators --- .../crypto/AsymmetricCipherKeyPair.java | 19 ++++++++++++++++++ .../generators/Ed25519KeyPairGenerator.java | 20 +++++++++++++++++++ .../generators/Ed448KeyPairGenerator.java | 20 +++++++++++++++++++ .../Ed25519KeyGenerationParameters.java | 15 ++++++++++++++ .../params/Ed25519PrivateKeyParameters.java | 18 +++++++++++++++++ .../params/Ed25519PublicKeyParameters.java | 11 ++++++++++ .../params/Ed448KeyGenerationParameters.java | 15 ++++++++++++++ .../params/Ed448PrivateKeyParameters.java | 18 +++++++++++++++++ .../params/Ed448PublicKeyParameters.java | 11 ++++++++++ .../crypto/signers/Ed25519Signer.java | 18 +++++++++++++++++ .../crypto/signers/Ed448Signer.java | 20 +++++++++++++++++++ .../jce/provider/BouncyCastleProvider.java | 9 +++++++++ 12 files changed, 194 insertions(+) create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/Ed25519KeyPairGenerator.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/Ed448KeyPairGenerator.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519KeyGenerationParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519PrivateKeyParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519PublicKeyParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448KeyGenerationParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448PrivateKeyParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448PublicKeyParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/Ed25519Signer.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/Ed448Signer.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/provider/BouncyCastleProvider.java diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java new file mode 100644 index 000000000000..85380bff1dd7 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/AsymmetricCipherKeyPair.java @@ -0,0 +1,19 @@ +package org.bouncycastle.crypto; + +public class AsymmetricCipherKeyPair { + private final Object privateKey; + private final Object publicKey; + + public AsymmetricCipherKeyPair(Object publicKey, Object privateKey) { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + public Object getPrivate() { + return privateKey; + } + + public Object getPublic() { + return publicKey; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/Ed25519KeyPairGenerator.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/Ed25519KeyPairGenerator.java new file mode 100644 index 000000000000..e40aa7f750d2 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/Ed25519KeyPairGenerator.java @@ -0,0 +1,20 @@ +package org.bouncycastle.crypto.generators; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters; +import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; + +public class Ed25519KeyPairGenerator { + private Ed25519KeyGenerationParameters parameters; + + public void init(Ed25519KeyGenerationParameters parameters) { + this.parameters = parameters; + } + + public AsymmetricCipherKeyPair generateKeyPair() { + Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(parameters.getRandom()); + Ed25519PublicKeyParameters publicKey = privateKey.generatePublicKey(); + return new AsymmetricCipherKeyPair(publicKey, privateKey); + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/Ed448KeyPairGenerator.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/Ed448KeyPairGenerator.java new file mode 100644 index 000000000000..a24ee0512954 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/Ed448KeyPairGenerator.java @@ -0,0 +1,20 @@ +package org.bouncycastle.crypto.generators; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.params.Ed448KeyGenerationParameters; +import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed448PublicKeyParameters; + +public class Ed448KeyPairGenerator { + private Ed448KeyGenerationParameters parameters; + + public void init(Ed448KeyGenerationParameters parameters) { + this.parameters = parameters; + } + + public AsymmetricCipherKeyPair generateKeyPair() { + Ed448PrivateKeyParameters privateKey = new Ed448PrivateKeyParameters(parameters.getRandom()); + Ed448PublicKeyParameters publicKey = privateKey.generatePublicKey(); + return new AsymmetricCipherKeyPair(publicKey, privateKey); + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519KeyGenerationParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519KeyGenerationParameters.java new file mode 100644 index 000000000000..56564db7e792 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519KeyGenerationParameters.java @@ -0,0 +1,15 @@ +package org.bouncycastle.crypto.params; + +import java.security.SecureRandom; + +public class Ed25519KeyGenerationParameters { + private final SecureRandom random; + + public Ed25519KeyGenerationParameters(SecureRandom random) { + this.random = random; + } + + public SecureRandom getRandom() { + return random; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519PrivateKeyParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519PrivateKeyParameters.java new file mode 100644 index 000000000000..c2c9c0b8523a --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519PrivateKeyParameters.java @@ -0,0 +1,18 @@ +package org.bouncycastle.crypto.params; + +import java.security.SecureRandom; + +public class Ed25519PrivateKeyParameters { + + public Ed25519PrivateKeyParameters(SecureRandom random) { } + + public Ed25519PrivateKeyParameters(byte[] privateKey) { } + + public Ed25519PublicKeyParameters generatePublicKey() { + return new Ed25519PublicKeyParameters(new byte[32]); + } + + public byte[] getEncoded() { + return new byte[32]; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519PublicKeyParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519PublicKeyParameters.java new file mode 100644 index 000000000000..95e9e68c4a74 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed25519PublicKeyParameters.java @@ -0,0 +1,11 @@ +package org.bouncycastle.crypto.params; + +public class Ed25519PublicKeyParameters { + private byte[] publicKey = new byte[32]; + + public Ed25519PublicKeyParameters(byte[] publicKey) { } + + public byte[] getEncoded() { + return publicKey; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448KeyGenerationParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448KeyGenerationParameters.java new file mode 100644 index 000000000000..05e871d2e693 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448KeyGenerationParameters.java @@ -0,0 +1,15 @@ +package org.bouncycastle.crypto.params; + +import java.security.SecureRandom; + +public class Ed448KeyGenerationParameters { + private final SecureRandom random; + + public Ed448KeyGenerationParameters(SecureRandom random) { + this.random = random; + } + + public SecureRandom getRandom() { + return random; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448PrivateKeyParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448PrivateKeyParameters.java new file mode 100644 index 000000000000..b8ad86e9a74e --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448PrivateKeyParameters.java @@ -0,0 +1,18 @@ +package org.bouncycastle.crypto.params; + +import java.security.SecureRandom; + +public class Ed448PrivateKeyParameters { + + public Ed448PrivateKeyParameters(SecureRandom random) { } + + public Ed448PrivateKeyParameters(byte[] privateKey) { } + + public Ed448PublicKeyParameters generatePublicKey() { + return new Ed448PublicKeyParameters(new byte[57]); // Ed448 public keys are 57 bytes + } + + public byte[] getEncoded() { + return new byte[57]; // Ed448 private keys are also 57 bytes + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448PublicKeyParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448PublicKeyParameters.java new file mode 100644 index 000000000000..05745baa5f70 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/Ed448PublicKeyParameters.java @@ -0,0 +1,11 @@ +package org.bouncycastle.crypto.params; + +public class Ed448PublicKeyParameters { + private byte[] publicKey = new byte[57]; // Ed448 public keys are 57 bytes + + public Ed448PublicKeyParameters(byte[] publicKey) { } + + public byte[] getEncoded() { + return publicKey; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/Ed25519Signer.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/Ed25519Signer.java new file mode 100644 index 000000000000..20bbc9166578 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/Ed25519Signer.java @@ -0,0 +1,18 @@ +package org.bouncycastle.crypto.signers; + +import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; + +public class Ed25519Signer { + public void init(boolean forSigning, Object keyParameter) { } + + public void update(byte[] message, int offset, int length) { } + + public byte[] generateSignature() { + return new byte[64]; + } + + public boolean verifySignature(byte[] signature) { + return true; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/Ed448Signer.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/Ed448Signer.java new file mode 100644 index 000000000000..a1a9870f36a2 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/Ed448Signer.java @@ -0,0 +1,20 @@ +package org.bouncycastle.crypto.signers; + +import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed448PublicKeyParameters; + +public class Ed448Signer { + public Ed448Signer(byte[] context) { } + + public void init(boolean forSigning, Object keyParameter) { } + + public void update(byte[] message, int offset, int length) { } + + public byte[] generateSignature() { + return new byte[114]; + } + + public boolean verifySignature(byte[] signature) { + return true; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/provider/BouncyCastleProvider.java new file mode 100644 index 000000000000..6f3d585c8ba6 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -0,0 +1,9 @@ +package org.bouncycastle.jce.provider; + +import java.security.Provider; + +public class BouncyCastleProvider extends Provider { + public BouncyCastleProvider() { + super("BC", 1.0, "Bouncy Castle Security Provider v2.73.7"); + } +} \ No newline at end of file From d7f1c701b2c91f5d0fbf5be3fd17dcd43d388a0c Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Fri, 23 May 2025 15:33:00 +0200 Subject: [PATCH 08/32] Added unit tests for BouncyCastle EdDSA signatures and key generators --- .../BouncyCastle/Ed25519SignAndVerify.java | 47 +++++++++++++++++++ .../BouncyCastle/Ed448SignAndVerify.java | 47 +++++++++++++++++++ .../BouncyCastle/key_artifacts.expected | 4 ++ .../quantum/BouncyCastle/key_artifacts.ql | 5 ++ .../key_generation_operations.expected | 4 ++ .../BouncyCastle/key_generation_operations.ql | 5 ++ .../quantum/BouncyCastle/options | 1 + .../signature_operations.expected | 4 ++ .../BouncyCastle/signature_operations.ql | 13 +++++ 9 files changed, 130 insertions(+) create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed25519SignAndVerify.java create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed448SignAndVerify.java create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.ql create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.ql create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/options create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.ql diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed25519SignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed25519SignAndVerify.java new file mode 100644 index 000000000000..ffa6c88a86f3 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed25519SignAndVerify.java @@ -0,0 +1,47 @@ +import java.security.KeyPair; +import java.security.Security; +import java.security.SecureRandom; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator; +import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters; +import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; +import org.bouncycastle.crypto.signers.Ed25519Signer; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class Ed25519SignAndVerify { + public static void main(String[] args) { + Security.addProvider(new BouncyCastleProvider()); + + try { + // Generate a key pair + SecureRandom random = new SecureRandom(); + Ed25519KeyPairGenerator keyPairGenerator = new Ed25519KeyPairGenerator(); + keyPairGenerator.init(new Ed25519KeyGenerationParameters(random)); + AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair(); + + Ed25519PrivateKeyParameters privateKey = (Ed25519PrivateKeyParameters) keyPair.getPrivate(); + Ed25519PublicKeyParameters publicKey = (Ed25519PublicKeyParameters) keyPair.getPublic(); + + byte[] message = "Hello, Ed25519 signature!".getBytes("UTF-8"); + + // Sign the message + Ed25519Signer signer = new Ed25519Signer(); + signer.init(true, privateKey); // true for signing + signer.update(message, 0, message.length); + byte[] signature = signer.generateSignature(); + + System.out.println("Signature generated!"); + + // Verify the signature + Ed25519Signer verifier = new Ed25519Signer(); + verifier.init(false, publicKey); // false for verification + verifier.update(message, 0, message.length); + boolean verified = verifier.verifySignature(signature); + + System.out.println("Signature verified: " + verified); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed448SignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed448SignAndVerify.java new file mode 100644 index 000000000000..68188f14b213 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed448SignAndVerify.java @@ -0,0 +1,47 @@ +import java.security.KeyPair; +import java.security.Security; +import java.security.SecureRandom; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.Ed448KeyPairGenerator; +import org.bouncycastle.crypto.params.Ed448KeyGenerationParameters; +import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters; +import org.bouncycastle.crypto.params.Ed448PublicKeyParameters; +import org.bouncycastle.crypto.signers.Ed448Signer; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class Ed448SignAndVerify { + public static void main(String[] args) { + Security.addProvider(new BouncyCastleProvider()); + + try { + // Generate a key pair + SecureRandom random = new SecureRandom(); + Ed448KeyPairGenerator keyPairGenerator = new Ed448KeyPairGenerator(); + keyPairGenerator.init(new Ed448KeyGenerationParameters(random)); + AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair(); + + Ed448PrivateKeyParameters privateKey = (Ed448PrivateKeyParameters) keyPair.getPrivate(); + Ed448PublicKeyParameters publicKey = (Ed448PublicKeyParameters) keyPair.getPublic(); + + byte[] message = "Hello, Ed448 signature!".getBytes("UTF-8"); + + // Sign the message + Ed448Signer signer = new Ed448Signer("context".getBytes()); + signer.init(true, privateKey); // true for signing + signer.update(message, 0, message.length); + byte[] signature = signer.generateSignature(); + + System.out.println("Signature generated!"); + + // Verify the signature + Ed448Signer verifier = new Ed448Signer("context".getBytes()); + verifier.init(false, publicKey); // false for verification + verifier.update(message, 0, message.length); + boolean verified = verifier.verifySignature(signature); + + System.out.println("Signature verified: " + verified); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected new file mode 100644 index 000000000000..6c100160ed39 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected @@ -0,0 +1,4 @@ +| Ed448SignAndVerify.java:21:47:21:80 | Key | CURVE25519 | +| Ed448SignAndVerify.java:21:47:21:80 | Key | Ed448 | +| Ed25519SignAndVerify.java:21:47:21:80 | Key | CURVE25519 | +| Ed25519SignAndVerify.java:21:47:21:80 | Key | Ed25519 | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.ql new file mode 100644 index 000000000000..394c82d70fce --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.ql @@ -0,0 +1,5 @@ +import java +import experimental.quantum.Language + +from Crypto::KeyArtifactNode n +select n, n.getAKnownAlgorithm() diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected new file mode 100644 index 000000000000..d9f511091ed2 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected @@ -0,0 +1,4 @@ +| Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed448SignAndVerify.java:19:54:19:80 | EllipticCurve | Ed448SignAndVerify.java:21:47:21:80 | Key | +| Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | Ed448SignAndVerify.java:21:47:21:80 | Key | +| Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed25519SignAndVerify.java:19:56:19:84 | EllipticCurve | Ed25519SignAndVerify.java:21:47:21:80 | Key | +| Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:21:47:21:80 | Key | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.ql new file mode 100644 index 000000000000..30447906aed0 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.ql @@ -0,0 +1,5 @@ +import java +import experimental.quantum.Language + +from Crypto::KeyGenerationOperationNode n +select n, n.getAKnownAlgorithm(), n.getOutputKeyArtifact() diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/options b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/options new file mode 100644 index 000000000000..cb978c1dc068 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/bcprov-lts8on-2.73.7 diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected new file mode 100644 index 000000000000..6fb7e925a1ba --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected @@ -0,0 +1,4 @@ +| Ed448SignAndVerify.java:32:32:32:57 | SignOperation | Ed448SignAndVerify.java:30:31:30:40 | Key | Ed448SignAndVerify.java:31:27:31:33 | Message | KeyOperationOutput | +| Ed448SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed448SignAndVerify.java:38:34:38:42 | Key | Ed448SignAndVerify.java:39:29:39:35 | Message | | +| Ed25519SignAndVerify.java:32:32:32:57 | SignOperation | Ed25519SignAndVerify.java:30:31:30:40 | Key | Ed25519SignAndVerify.java:31:27:31:33 | Message | KeyOperationOutput | +| Ed25519SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed25519SignAndVerify.java:38:34:38:42 | Key | Ed25519SignAndVerify.java:39:29:39:35 | Message | | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.ql new file mode 100644 index 000000000000..6ad205cd5676 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.ql @@ -0,0 +1,13 @@ +import java +import experimental.quantum.Language + +string getAnOutputArtifact(Crypto::KeyOperationNode n) { + exists(Crypto::KeyOperationOutputNode output | + output = n.getAnOutputArtifact() and result = output.toString() + ) + or + not exists(n.getAnOutputArtifact()) and result = "" +} + +from Crypto::SignatureOperationNode n +select n, n.getAKey(), n.getAnInputArtifact(), getAnOutputArtifact(n) From fca90b3292d407d031d081444672d4fb68342d56 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Wed, 28 May 2025 10:05:54 +0200 Subject: [PATCH 09/32] Added support for BouncyCast ECDSA This commit adds support for ECDSA. This includes tracking the instantiated curve parameters using data flow. It also adds SignatureArtifactInstance and SignatureOperationInstance types to the shared model. --- .../lib/experimental/quantum/BouncyCastle.qll | 232 ++++++++++++++++-- .../BouncyCastle/AlgorithmInstances.qll | 109 +++++++- .../quantum/BouncyCastle/FlowAnalysis.qll | 103 +++++++- .../codeql/quantum/experimental/Model.qll | 3 + 4 files changed, 411 insertions(+), 36 deletions(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll index 5a82a2cd78fa..4c6b8a6327e1 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -1,5 +1,101 @@ import java +module Params { + import Language + import BouncyCastle.FlowAnalysis + import BouncyCastle.AlgorithmInstances + + /** + * A model of the `Parameters` class in Bouncy Castle. + */ + class Parameters extends RefType { + Parameters() { + // Matches `org.bouncycastle.crypto.params`, `org.bouncycastle.asn1.x9`, etc. + this.getPackage().getName().matches("org.bouncycastle.%") and + this.getName().matches("%Parameters") + } + } + + class KeyParameters extends Parameters { + KeyParameters() { + this.getPackage().getName() = "org.bouncycastle.crypto.params" and + this.getName().matches("%KeyParameters") + } + } + + /** + * Any call that returns a BouncyCastle parameters object. This type is used + * to model data flow to resolve algorithm instances like elliptic curves. + * + * Examples: + * ``` + * curveParams = SECNamedCurves.getByName(...); + * domainParams = new ECDomainParameters(...); + * ``` + */ + class ParametersInstantiation extends Call { + ParametersInstantiation() { + // Class instantiations + this.(ConstructorCall) + .getConstructedType() + .getPackage() + .getName() + .matches("org.bouncycastle.%") and + this.(ConstructorCall).getConstructedType() instanceof Parameters + or + // (Static) factory methods + this.(MethodCall) + .getCallee() + .getDeclaringType() + .getPackage() + .getName() + .matches("org.bouncycastle.%") and + this.(MethodCall).getType() instanceof Parameters + } + + // Can be overridden by subclasses which take a key size argument. + Expr getKeySizeArg() { none() } + + Crypto::ConsumerInputDataFlowNode getAKeySizeConsumer() { + result.asExpr() = this.getKeySizeArg() + } + + // Can be overridden by subclasses which take an algorithm argument. + Expr getAlgorithmArg() { none() } + + Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result.getInputNode().asExpr() = this.getAlgorithmArg() + } + + Expr getAParametersArg() { + result = this.getAnArgument() and + result.getType() instanceof Parameters + } + + Crypto::ConsumerInputDataFlowNode getAParametersConsumer() { + result.asExpr() = this.getAParametersArg() + } + } + + class X9ECParametersInstantiation extends ParametersInstantiation { + X9ECParametersInstantiation() { this.(Expr).getType().getName() = "X9ECParameters" } + + override Expr getAlgorithmArg() { result = this.getArgument(0) } + } + + class EllipticCurveStringLiteralArg extends EllipticCurveAlgorithmValueConsumer instanceof Expr { + ParametersInstantiation params; + + EllipticCurveStringLiteralArg() { this = params.getAlgorithmArg() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(EllipticCurveStringLiteralInstance).getConsumer() = this + } + } +} + /** * Models for the signature algorithms defined by the `org.bouncycastle.crypto.signers` package. */ @@ -26,6 +122,40 @@ module Signers { MethodCall getAMethodCall(string name) { result.getCallee().hasQualifiedName(this.getPackage().getName(), this.getName(), name) } + + // Overridden by subclasses to provide the message argument. + Expr getMessageArg(MethodCall call) { + call.getCallee().getName() = "update" and + result = call.getArgument(0) + } + + // Overridden by subclasses to provide the signature argument. + Expr getSignatureArg(MethodCall call) { + call.getCallee().getName() = "verifySignature" and + result = call.getArgument(0) + } + + // Overridden by subclasses to provide the signature output. + Expr getSignatureOutput(MethodCall call) { + call.getCallee().getName() = "generateSignature" and + result = call + } + } + + class ECDSASigner extends Signer { + ECDSASigner() { this.getName().matches("ECDSA%") } + + override Expr getMessageArg(MethodCall call) { + // For ECDSA the message is passed directly to `generateSignature()`. + call.getCallee().getName().matches(["generateSignature", "verifySignature"]) and + result = call.getArgument(0) + } + + override Expr getSignatureArg(MethodCall call) { + // For ECDSA, r and s are passed to `verifySignature()` as separate arguments. + call.getCallee().getName() = "verifySignature" and + result = call.getArgument([1, 2]) + } } /** @@ -45,7 +175,17 @@ module Signers { Expr getForSigningArg() { result = this.getArgument(0) } - Expr getKeyArg() { result = this.getArgument(1) } + Expr getKeyArg() { + this.getParameterArg().getType() instanceof Params::KeyParameters and + result = this.getParameterArg() + } + + // The second argument is used to provide parameters (like the key) to the signer. + Expr getParameterArg() { result = this.getArgument(1) } + + Crypto::ConsumerInputDataFlowNode getAParametersConsumer() { + result.asExpr() = this.getParameterArg() + } // TODO: Support dataflow for the operation sub-type. Crypto::KeyOperationSubtype getKeyOperationSubtype() { @@ -68,36 +208,48 @@ module Signers { * verify the signature, respectively. */ private class SignerUseCall extends MethodCall { - SignerUseCall() { this = any(Signer signer).getAUseCall() } + Signer signer; + + SignerUseCall() { this = signer.getAUseCall() } predicate isIntermediate() { this.getCallee().getName() = "update" } - Expr getInput() { result = this.getArgument(0) } + Expr getMessageInput() { result = signer.getMessageArg(this) } - Expr getOutput() { result = this } + Expr getSignatureInput() { result = signer.getSignatureArg(this) } + + Expr getSignatureOutput() { result = signer.getSignatureOutput(this) } } /** * Instantiate the flow analysis module for the `Signer` class. */ - private module FlowAnalysis = + private module SignerFlow = NewToInitToUseFlowAnalysis; /** * A signing operation instance is a call to either `update()`, `generateSignature()`, * or `verifySignature()` on a `Signer` instance. */ - class SignatureOperationInstance extends Crypto::KeyOperationInstance instanceof SignerUseCall { + class SignatureOperationInstance extends Crypto::SignatureOperationInstance instanceof SignerUseCall + { SignatureOperationInstance() { not this.isIntermediate() } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - result = FlowAnalysis::getNewFromUse(this, _, _) + result = this.getParameters().getAnAlgorithmValueConsumer() + or + result = SignerFlow::getNewFromUse(this, _, _) } override Crypto::KeyOperationSubtype getKeyOperationSubtype() { - if FlowAnalysis::hasInit(this) - then result = this.getInitCall().getKeyOperationSubtype() - else result = Crypto::TUnknownKeyOperationMode() + // This is less expensive and more robust than resolving the subtype using + // dataflow from the `forSigning` argument to `init()`. + if super.getMethod().getName() = "generateSignature" + then result = Crypto::TSignMode() + else + if super.getMethod().getName() = "verifySignature" + then result = Crypto::TVerifyMode() + else result = Crypto::TUnknownKeyOperationMode() } override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { @@ -107,18 +259,36 @@ module Signers { override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { none() } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result.asExpr() = this.getAnUpdateCall().getInput() + // Inputs to signers with streaming APIs + result.asExpr() = this.getAnUpdateCall().getMessageInput() + or + // Inputs to signers with one shot APIs + result.asExpr() = super.getMessageInput() + } + + override Crypto::ConsumerInputDataFlowNode getSignatureArtifactConsumer() { + result.asExpr() = super.getSignatureInput() } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - this.getKeyOperationSubtype() = Crypto::TSignMode() and - result.asExpr() = super.getOutput() + // Signature output + result.asExpr() = super.getSignatureOutput() } - SignerInitCall getInitCall() { result = FlowAnalysis::getInitFromUse(this, _, _) } + SignerInitCall getInitCall() { result = SignerFlow::getInitFromUse(this, _, _) } SignerUseCall getAnUpdateCall() { - result = FlowAnalysis::getAnIntermediateUseFromFinalUse(this, _, _) + result = SignerFlow::getAnIntermediateUseFromFinalUse(this, _, _) + } + + Crypto::KeyArtifactOutputInstance getKey() { result.flowsTo(this.getInitCall().getKeyArg()) } + + Generators::KeyGenerationOperationInstance getKeyGenerationOperationInstance() { + result.getKeyArtifactOutputInstance() = this.getKey() + } + + Params::ParametersInstantiation getParameters() { + result = this.getKeyGenerationOperationInstance().getParameters() } } } @@ -194,6 +364,11 @@ module Generators { // TODO: We may need to model this using the `parameters` argument passed to // the `init()` method. Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + // The `KeyGenerationParameters` argument used to configure the key generator. + Crypto::ConsumerInputDataFlowNode getAParametersConsumer() { + result.asExpr() = this.getArgument(0) + } } /** @@ -216,6 +391,14 @@ module Generators { private module KeyGeneratorFlow = NewToInitToUseFlowAnalysis; + private module ParametersFlow = + ParametersToInitFlowAnalysis; + + Params::ParametersInstantiation getParametersFromInit(KeyGeneratorInitCall init) { + result = ParametersFlow::getParametersFromInit(init, _, _) and + result instanceof Params::X9ECParametersInstantiation + } + /** * A key generation operation instance is a call to `generateKey()` or * `generateKeyPair()` on a key generator defined under @@ -224,6 +407,10 @@ module Generators { class KeyGenerationOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGeneratorUseCall { override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + // The algorithm value consumer flows through a parameters argument to `init()` + result = this.getParameters().getAnAlgorithmValueConsumer() + or + // The algorithm is implicit in the key generator type result = KeyGeneratorFlow::getNewFromUse(this, _, _) } @@ -240,17 +427,12 @@ module Generators { override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { result = KeyGeneratorFlow::getInitFromUse(this, _, _).getKeySizeConsumer() } - } -} -/** - * Models for cryptographic parameters defined by the `org.bouncycastle.crypto.params` package. - */ -module Parameters { - class KeyGenerationParameters extends RefType { - KeyGenerationParameters() { - this.getPackage().getName() = "org.bouncycastle.crypto.params" and - this.getName().matches("%KeyGenerationParameters") + Params::ParametersInstantiation getParameters() { + exists(KeyGeneratorInitCall init | + init = KeyGeneratorFlow::getInitFromUse(this, _, _) and + result = ParametersFlow::getParametersFromInit(init, _, _) + ) } } } diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index 0d3920d920d1..5b73422588b6 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -1,10 +1,12 @@ import java import experimental.quantum.Language import AlgorithmValueConsumers +import FlowAnalysis -abstract private class EllipticCurveAlgorithmInstance extends Crypto::EllipticCurveInstance, - EllipticCurveAlgorithmValueConsumer -{ +/** + * Elliptic curve algorithms where the curve is implicitly defined by the type. + */ +abstract private class EllipticCurveAlgorithmInstance extends Crypto::EllipticCurveInstance { override Crypto::TEllipticCurveType getEllipticCurveType() { Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), _, result) } @@ -15,7 +17,23 @@ abstract private class EllipticCurveAlgorithmInstance extends Crypto::EllipticCu } /** - * Signature algorithms. + * A string literal that represents an elliptic curve name. + */ +class EllipticCurveStringLiteralInstance extends EllipticCurveAlgorithmInstance instanceof StringLiteral +{ + EllipticCurveStringLiteralInstance() { + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getValue(), _, _) + } + + override string getRawEllipticCurveName() { result = super.getValue() } + + EllipticCurveAlgorithmValueConsumer getConsumer() { + result = EllipticCurveStringLiteralToConsumer::getConsumerFromLiteral(this, _, _) + } +} + +/** + * Signature algorithms where the algorithm is implicitly defined by the type. */ abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr @@ -38,12 +56,31 @@ abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmI override string getRawAlgorithmName() { typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) } + + Crypto::ConsumerInputDataFlowNode getAParametersConsumer() { none() } +} + +/** + * DSA and DSADigest signers. + */ +class DSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { + DSASignatureAlgorithmInstance() { + super.getConstructedType() instanceof Signers::Signer and + super.getConstructedType().getName().matches("DSA%") + } + + override string getRawAlgorithmName() { + typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + } } abstract private class EllipticCurveSignatureAlgorithmInstance extends SignatureAlgorithmInstance, EllipticCurveAlgorithmInstance { } +/** + * Ed25519, Ed25519ph, and Ed25519ctx signers. + */ class Ed25519SignatureAlgorithmInstance extends EllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr { Ed25519SignatureAlgorithmInstance() { @@ -54,6 +91,9 @@ class Ed25519SignatureAlgorithmInstance extends EllipticCurveSignatureAlgorithmI override string getRawEllipticCurveName() { result = "CURVE25519" } } +/** + * Ed448 and Ed448ph signers. + */ class Ed448SignatureAlgorithmInstance extends EllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr { Ed448SignatureAlgorithmInstance() { @@ -69,7 +109,39 @@ class Ed448SignatureAlgorithmInstance extends EllipticCurveSignatureAlgorithmIns } /** - * Key generation algorithms. + * ECDSA signers. + * + * ECDSA curve parameters can be set in at least five ways: + * - By using the `ECDomainParameters` class, which is passed to the constructor of the signer. + * - By using the `ECNamedDomainParameters` class, which is passed to the constructor of the signer. + * - By using the `ECNamedCurveTable` class, which is used to obtain the curve parameters. + * - By using the `ECNamedCurveSpec` class, which is passed to the constructor of the signer. + * - By using the `ECParameterSpec` class, which is passed to the constructor of the signer. + * + * NOTE: This type does not inherit from `EllipticCurveSignatureAlgorithmInstance` because the curve + * is not implicitly defined by the type, but rather by the key parameters passed to `init()`. + */ +class ECDSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr +{ + ECDSASignatureAlgorithmInstance() { + super.getConstructedType() instanceof Signers::Signer and + super.getConstructedType().getName().matches("ECDSA%") + } + + override string getRawAlgorithmName() { + typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + // We need to specify this explicitly since the key size is not fixed. + result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::ECDSA()) + } + + override int getKeySizeFixed() { none() } +} + +/** + * Key generation algorithms where the algorithm is implicitly defined by the type. */ abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, KeyGenerationAlgorithmValueConsumer instanceof ClassInstanceExpr @@ -92,8 +164,13 @@ abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgori override string getRawAlgorithmName() { typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) } + + Crypto::ConsumerInputDataFlowNode getAParametersConsumer() { none() } } +/** + * Key generation algorithms for elliptic curves where the curve is implicitly defined by the type. + */ abstract private class EllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance, EllipticCurveAlgorithmInstance { } @@ -118,6 +195,24 @@ class Ed448KeyGenerationAlgorithmInstance extends EllipticCurveKeyGenerationAlgo override string getRawEllipticCurveName() { result = "CURVE25519" } } +/** + * Represents a generic `ECKeyPairGenerator` instances. + * + * NOTE: This type does not inherit from `EllipticCurveKeyGenerationAlgorithmInstance` because the curve + * is not implicitly defined by the type, but rather by parameters passed to the constructor. + */ +class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance instanceof ClassInstanceExpr +{ + GenericEllipticCurveKeyGenerationAlgorithmInstance() { + super.getConstructedType() instanceof Generators::KeyGenerator and + super.getConstructedType().getName().matches("EC%") + } + + override string getRawAlgorithmName() { + typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + } +} + /** * Private predicates mapping type names to raw names, key sizes and algorithms. */ @@ -130,6 +225,10 @@ private predicate typeNameToRawAlgorithmName(string typeName, string algorithmNa // Ed448 and Ed448ph key generators and signers typeName.matches("Ed448%") and algorithmName = "ED448" + or + // ECDSA + typeName.matches("ECDSA%") and + algorithmName = "ECDSA" } private predicate signatureNameToKeySizeAndAlgorithmMapping( diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll index 1da88aad3b2f..475dd380114c 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll @@ -1,18 +1,30 @@ import java import experimental.quantum.Language import semmle.code.java.dataflow.DataFlow +import AlgorithmInstances +import AlgorithmValueConsumers /** * A signature for the `getInstance()` method calls used in JCA, or direct * constructor calls used in BouncyCastle. */ -signature class NewCallSig instanceof Call; +signature class NewCallSig instanceof Call { + /** + * Gets a parameter argument that is used to initialize the object. + */ + Crypto::ConsumerInputDataFlowNode getAParametersConsumer(); +} -signature class InitCallSig instanceof MethodCall; +signature class InitCallSig instanceof MethodCall { + /** + * Gets a parameter argument that is used to initialize the object. + */ + Crypto::ConsumerInputDataFlowNode getAParametersConsumer(); +} signature class UseCallSig instanceof MethodCall { /** - * Holds if the use is not a final use, such as an `update()` call before `doFinal()` + * Holds if the use is not a final use, such as an `update()` call before `doFinal()`. */ predicate isIntermediate(); } @@ -22,7 +34,7 @@ signature class UseCallSig instanceof MethodCall { * to `init()`, to `doOperation()` in BouncyCastle, and similar patterns in * other libraries. * - * For example: + * Example: * ``` * gen = new KeyGenerator(...); * gen.init(...); @@ -85,7 +97,7 @@ module NewToInitToUseFlowAnalysis; + module NewToInitToUseFlow = DataFlow::GlobalWithState; New getNewFromUse(Use use, NewToInitToUseFlow::PathNode src, NewToInitToUseFlow::PathNode sink) { src.getNode().asExpr() = result and @@ -163,6 +175,58 @@ module NewToInitToUseFlowAnalysis { + module ParametersToInitConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof New } + + predicate isSink(DataFlow::Node sink) { + exists(Init init | sink = init.getAParametersConsumer()) + } + + /** + * Pass-through for parameters created from other parameters. + * + * As an example, below we want to track the flow from the `X9ECParameters` + * constructor call to the `keyPairGenerator.init()` call to be able to + * determine the curve associated with the generator. + * + * Example: + * ``` + * X9ECParameters ecParams = SECNamedCurves.getByName("secp256r1"); + * ECDomainParameters domainParams = new ECDomainParameters(ecParams); + * ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, random); + * ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + * keyPairGenerator.init(keyGenParams); + * ``` + */ + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + node1 = node2.asExpr().(New).getAParametersConsumer() + } + } + + module ParametersToInitFlow = DataFlow::Global; + + New getParametersFromInit( + Init init, ParametersToInitFlow::PathNode src, ParametersToInitFlow::PathNode sink + ) { + src.getNode().asExpr() = result and + sink.getNode() = init.getAParametersConsumer() and + ParametersToInitFlow::flowPath(src, sink) + } +} + /** * Model data flow from a key pair to the public and private components of the * key pair. @@ -197,3 +261,30 @@ class ArtifactAdditionalFlowStep extends AdditionalFlowInputStep { override DataFlow::Node getOutput() { result = output } } + +module EllipticCurveStringLiteralToConsumer { + /** + * Flow from a known elliptic curve name to an elliptic curve algorithm consumer. + */ + module EllipticCurveStringLiteralToAlgorithmValueConsumerConfig implements DataFlow::ConfigSig { + // NOTE: We do not reference EllipticCurveStringLiteralInstance directly + // here to avoid non-monotonic recursion. + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof StringLiteral } + + predicate isSink(DataFlow::Node sink) { + exists(EllipticCurveAlgorithmValueConsumer consumer | sink = consumer.getInputNode()) + } + } + + module EllipticCurveStringLiteralToAlgorithmValueConsumerFlow = + DataFlow::Global; + + EllipticCurveAlgorithmValueConsumer getConsumerFromLiteral( + StringLiteral literal, EllipticCurveStringLiteralToAlgorithmValueConsumerFlow::PathNode src, + EllipticCurveStringLiteralToAlgorithmValueConsumerFlow::PathNode sink + ) { + src.getNode().asExpr() = literal and + sink.getNode() = result.getInputNode() and + EllipticCurveStringLiteralToAlgorithmValueConsumerFlow::flowPath(src, sink) + } +} diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index e7bbe65d3115..33707527d774 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -1300,6 +1300,7 @@ module CryptographyBase Input> { TKeyOperation(KeyOperationInstance e) or TKeyOperationAlgorithm(KeyOperationAlgorithmInstanceOrValueConsumer e) or TKeyOperationOutput(KeyOperationOutputArtifactInstance e) or + TSignature(SignatureOperationInstance e) or // Non-Standalone Algorithms (e.g., Mode, Padding) // These algorithms are always tied to a key operation algorithm TModeOfOperationAlgorithm(ModeOfOperationAlgorithmInstance e) or @@ -2631,6 +2632,8 @@ module CryptographyBase Input> { or curveName = "CURVE448" and keySize = 448 and curveFamily = CURVE448() or + curveName = "CURVE448" and keySize = 448 and curveFamily = CURVE448() + or // TODO: separate these into key agreement logic or sign/verify (ECDSA / ECDH) // or // curveName = "X25519" and keySize = 255 and curveFamily = CURVE25519() From efd3266b1cc18afcd52b08fc0541d42f7dbad911 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Thu, 29 May 2025 13:19:06 +0200 Subject: [PATCH 10/32] Added signature input nodes to signature verify operation nodes --- .../lib/experimental/quantum/BouncyCastle.qll | 14 +------ .../BouncyCastle/AlgorithmInstances.qll | 1 - .../quantum/BouncyCastle/FlowAnalysis.qll | 38 +++++++++++++++++-- .../codeql/quantum/experimental/Model.qll | 30 ++++++++++++++- 4 files changed, 65 insertions(+), 18 deletions(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll index 4c6b8a6327e1..ad7b00f0a419 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -236,8 +236,6 @@ module Signers { SignatureOperationInstance() { not this.isIntermediate() } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - result = this.getParameters().getAnAlgorithmValueConsumer() - or result = SignerFlow::getNewFromUse(this, _, _) } @@ -266,7 +264,7 @@ module Signers { result.asExpr() = super.getMessageInput() } - override Crypto::ConsumerInputDataFlowNode getSignatureArtifactConsumer() { + override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { result.asExpr() = super.getSignatureInput() } @@ -280,16 +278,6 @@ module Signers { SignerUseCall getAnUpdateCall() { result = SignerFlow::getAnIntermediateUseFromFinalUse(this, _, _) } - - Crypto::KeyArtifactOutputInstance getKey() { result.flowsTo(this.getInitCall().getKeyArg()) } - - Generators::KeyGenerationOperationInstance getKeyGenerationOperationInstance() { - result.getKeyArtifactOutputInstance() = this.getKey() - } - - Params::ParametersInstantiation getParameters() { - result = this.getKeyGenerationOperationInstance().getParameters() - } } } diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index 5b73422588b6..2ebd3ea2a5c2 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -150,7 +150,6 @@ abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgori override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } - // TODO: Model flow from the parameter specification to the key generator. override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll index 475dd380114c..e2ab88acedf2 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll @@ -247,10 +247,42 @@ class KeyAdditionalFlowSteps extends MethodCall { DataFlow::Node getOutputNode() { result.asExpr() = this } } +/** + * Model data flow from an ECDSA signature to the scalars r and s passed to + * `verifySignature()`. The ECDSA signature is represented as a `BigInteger` + * array, where the first element is the scalar r and the second element is the + * scalar s. + */ +class ECDSASignatureAdditionalFlowSteps extends ArrayAccess { + ECDSASignatureAdditionalFlowSteps() { + this.getArray().getType().getName() = "BigInteger[]" and + // It is reasonable to assume that the indices are integer literals + this.getIndexExpr().(IntegerLiteral).getValue().toInt() = [0, 1] + } + + /** + * The input node is the ECDSA signature represented as a `BigInteger` array. + */ + DataFlow::Node getInputNode() { + // TODO: This should be the array node `this.getArray()` + result.asExpr() = this.getArray() + } + + /** + * The output node is the `BigInteger` element accessed by this array access. + */ + DataFlow::Node getOutputNode() { result.asExpr() = this } +} + predicate additionalFlowSteps(DataFlow::Node node1, DataFlow::Node node2) { - exists(KeyAdditionalFlowSteps call | - node1 = call.getInputNode() and - node2 = call.getOutputNode() + exists(KeyAdditionalFlowSteps fs | + node1 = fs.getInputNode() and + node2 = fs.getOutputNode() + ) + or + exists(ECDSASignatureAdditionalFlowSteps fs | + node1 = fs.getInputNode() and + node2 = fs.getOutputNode() ) } diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index 33707527d774..d92e62180bae 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -435,6 +435,17 @@ module CryptographyBase Input> { final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } } + final private class SignatureArtifactConsumer extends ArtifactConsumerAndInstance { + ConsumerInputDataFlowNode inputNode; + + SignatureArtifactConsumer() { + exists(SignatureOperationInstance op | inputNode = op.getSignatureConsumer()) and + this = Input::dfn_to_element(inputNode) + } + + final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } + } + /** * An artifact that is produced by an operation, representing a concrete artifact instance rather than a synthetic consumer artifact. */ @@ -471,6 +482,8 @@ module CryptographyBase Input> { override DataFlowNode getOutputNode() { result = creator.getOutputArtifact() } KeyOperationInstance getCreator() { result = creator } + + KeyOperationInstance getCreator() { result = creator } } /** @@ -800,6 +813,8 @@ module CryptographyBase Input> { */ abstract class SignatureOperationInstance extends KeyOperationInstance { /** + * Gets the consumer of the signature that is being verified in case of a + * verification operation. * Gets the consumer of the signature that is being verified in case of a * verification operation. */ @@ -1300,7 +1315,6 @@ module CryptographyBase Input> { TKeyOperation(KeyOperationInstance e) or TKeyOperationAlgorithm(KeyOperationAlgorithmInstanceOrValueConsumer e) or TKeyOperationOutput(KeyOperationOutputArtifactInstance e) or - TSignature(SignatureOperationInstance e) or // Non-Standalone Algorithms (e.g., Mode, Padding) // These algorithms are always tied to a key operation algorithm TModeOfOperationAlgorithm(ModeOfOperationAlgorithmInstance e) or @@ -1547,6 +1561,20 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } } + /** + * A signature input. This may represent a signature, or a signature component + * such as the scalar values r and s in ECDSA. + */ + final class SignatureArtifactNode extends ArtifactNode, TSignatureInput { + SignatureArtifactConsumer instance; + + SignatureArtifactNode() { this = TSignatureInput(instance) } + + final override string getInternalType() { result = "SignatureInput" } + + override LocatableElement asElement() { result = instance } + } + /** * A salt input. */ From b57bf9ad208c53a24605f84a072d2f8bda61a23a Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Fri, 30 May 2025 13:18:35 +0200 Subject: [PATCH 11/32] Updated signature operations test query --- .../BouncyCastle/AlgorithmInstances.qll | 27 +++++--- .../BouncyCastle/ECDSAP256SignAndVerify.java | 61 +++++++++++++++++++ .../BouncyCastle/key_artifacts.expected | 6 +- .../key_generation_operations.expected | 2 + .../signature_operations.expected | 10 +-- .../BouncyCastle/signature_operations.ql | 13 +++- .../bouncycastle/asn1/sec/SECNamedCurves.java | 13 ++++ .../bouncycastle/asn1/x9/X9ECParameters.java | 27 ++++++++ .../crypto/generators/ECKeyPairGenerator.java | 16 +++++ .../crypto/params/ECDomainParameters.java | 15 +++++ .../params/ECKeyGenerationParameters.java | 21 +++++++ .../crypto/params/ECPrivateKeyParameters.java | 21 +++++++ .../crypto/params/ECPublicKeyParameters.java | 19 ++++++ .../crypto/signers/ECDSASigner.java | 23 +++++++ 14 files changed, 258 insertions(+), 16 deletions(-) create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/ECDSAP256SignAndVerify.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/x9/X9ECParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECDomainParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECPublicKeyParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/ECDSASigner.java diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index 2ebd3ea2a5c2..df082f877346 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -8,11 +8,13 @@ import FlowAnalysis */ abstract private class EllipticCurveAlgorithmInstance extends Crypto::EllipticCurveInstance { override Crypto::TEllipticCurveType getEllipticCurveType() { - Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), _, result) + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName().toUpperCase(), + _, result) } override int getKeySize() { - Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), result, _) + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName().toUpperCase(), + result, _) } } @@ -22,7 +24,7 @@ abstract private class EllipticCurveAlgorithmInstance extends Crypto::EllipticCu class EllipticCurveStringLiteralInstance extends EllipticCurveAlgorithmInstance instanceof StringLiteral { EllipticCurveStringLiteralInstance() { - Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getValue(), _, _) + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getValue().toUpperCase(), _, _) } override string getRawEllipticCurveName() { result = super.getValue() } @@ -76,7 +78,9 @@ class DSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceo abstract private class EllipticCurveSignatureAlgorithmInstance extends SignatureAlgorithmInstance, EllipticCurveAlgorithmInstance -{ } +{ + override Crypto::EllipticCurveInstance getEllipticCurve() { result = this } +} /** * Ed25519, Ed25519ph, and Ed25519ctx signers. @@ -133,7 +137,6 @@ class ECDSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanc } override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - // We need to specify this explicitly since the key size is not fixed. result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::ECDSA()) } @@ -181,7 +184,7 @@ class Ed25519KeyGenerationAlgorithmInstance extends EllipticCurveKeyGenerationAl super.getConstructedType().getName().matches("Ed25519%") } - override string getRawEllipticCurveName() { result = "CURVE25519" } + override string getRawEllipticCurveName() { result = "Curve25519" } } class Ed448KeyGenerationAlgorithmInstance extends EllipticCurveKeyGenerationAlgorithmInstance instanceof ClassInstanceExpr @@ -191,7 +194,7 @@ class Ed448KeyGenerationAlgorithmInstance extends EllipticCurveKeyGenerationAlgo super.getConstructedType().getName().matches("Ed448%") } - override string getRawEllipticCurveName() { result = "CURVE25519" } + override string getRawEllipticCurveName() { result = "Curve25519" } } /** @@ -208,7 +211,15 @@ class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAl } override string getRawAlgorithmName() { - typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + // The generator constructs an elliptic curve key pair, but the algorithm is + // not determined at key generation. As an example, the key could be used + // for either ECDSA or ECDH For this reason, we just return "EllipticCurve". + result = "EllipticCurve" + } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + // The algorithm type is not known. See above. + result = Crypto::KeyOpAlg::TUnknownKeyOperationAlgorithmType() } } diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/ECDSAP256SignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/ECDSAP256SignAndVerify.java new file mode 100644 index 000000000000..c3bfdcb524e0 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/ECDSAP256SignAndVerify.java @@ -0,0 +1,61 @@ +import java.security.Security; +import java.security.SecureRandom; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.signers.ECDSASigner; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; + +/** + * Example of using Bouncy Castle's low-level API for ECDSA signing and verification over P-256. + */ +public class ECDSAP256SignAndVerify { + public static void main(String[] args) { + // Add Bouncy Castle provider + Security.addProvider(new BouncyCastleProvider()); + + try { + // Get P-256 curve parameters using BouncyCastle's SECNamedCurves + String curveName = "secp256r1"; + X9ECParameters ecParams = SECNamedCurves.getByName(curveName); + ECDomainParameters domainParams = new ECDomainParameters(ecParams); + + // Generate a key pair + SecureRandom random = new SecureRandom(); + ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, random); + keyPairGenerator.init(keyGenParams); + AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair(); + + ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate(); + ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic(); + + byte[] message = "Hello, ECDSA P-256 signature!".getBytes("UTF-8"); + + // Sign the message + ECDSASigner signer = new ECDSASigner(); + signer.init(true, privateKey); // true for signing + // Note: ECDSA typically signs a hash of the message, not the message directly + // For simplicity, we're signing the message bytes directly here + java.math.BigInteger[] signature = signer.generateSignature(message); + + System.out.println("Signature generated!"); + System.out.println("Signature r: " + signature[0].toString(16)); + System.out.println("Signature s: " + signature[1].toString(16)); + + // Verify the signature + ECDSASigner verifier = new ECDSASigner(); + verifier.init(false, publicKey); // false for verification + boolean verified = verifier.verifySignature(message, signature[0], signature[1]); + + System.out.println("Signature verified: " + verified); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected index 6c100160ed39..ccc86f795474 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected @@ -1,4 +1,6 @@ -| Ed448SignAndVerify.java:21:47:21:80 | Key | CURVE25519 | +| ECDSAP256SignAndVerify.java:33:47:33:80 | Key | Unknown | +| ECDSAP256SignAndVerify.java:33:47:33:80 | Key | secp256r1 | +| Ed448SignAndVerify.java:21:47:21:80 | Key | Curve25519 | | Ed448SignAndVerify.java:21:47:21:80 | Key | Ed448 | -| Ed25519SignAndVerify.java:21:47:21:80 | Key | CURVE25519 | +| Ed25519SignAndVerify.java:21:47:21:80 | Key | Curve25519 | | Ed25519SignAndVerify.java:21:47:21:80 | Key | Ed25519 | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected index d9f511091ed2..0f1df1598069 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected @@ -1,3 +1,5 @@ +| ECDSAP256SignAndVerify.java:33:47:33:80 | KeyGeneration | ECDSAP256SignAndVerify.java:24:32:24:42 | EllipticCurve | ECDSAP256SignAndVerify.java:33:47:33:80 | Key | +| ECDSAP256SignAndVerify.java:33:47:33:80 | KeyGeneration | ECDSAP256SignAndVerify.java:30:51:30:74 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:33:47:33:80 | Key | | Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed448SignAndVerify.java:19:54:19:80 | EllipticCurve | Ed448SignAndVerify.java:21:47:21:80 | Key | | Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | Ed448SignAndVerify.java:21:47:21:80 | Key | | Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed25519SignAndVerify.java:19:56:19:84 | EllipticCurve | Ed25519SignAndVerify.java:21:47:21:80 | Key | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected index 6fb7e925a1ba..56895dde92f2 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected @@ -1,4 +1,6 @@ -| Ed448SignAndVerify.java:32:32:32:57 | SignOperation | Ed448SignAndVerify.java:30:31:30:40 | Key | Ed448SignAndVerify.java:31:27:31:33 | Message | KeyOperationOutput | -| Ed448SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed448SignAndVerify.java:38:34:38:42 | Key | Ed448SignAndVerify.java:39:29:39:35 | Message | | -| Ed25519SignAndVerify.java:32:32:32:57 | SignOperation | Ed25519SignAndVerify.java:30:31:30:40 | Key | Ed25519SignAndVerify.java:31:27:31:33 | Message | KeyOperationOutput | -| Ed25519SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed25519SignAndVerify.java:38:34:38:42 | Key | Ed25519SignAndVerify.java:39:29:39:35 | Message | | +| ECDSAP256SignAndVerify.java:45:48:45:80 | SignOperation | ECDSAP256SignAndVerify.java:41:34:41:50 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:42:31:42:40 | Key | ECDSAP256SignAndVerify.java:45:73:45:79 | Message | | SignatureOutput | +| ECDSAP256SignAndVerify.java:54:32:54:92 | VerifyOperation | ECDSAP256SignAndVerify.java:52:36:52:52 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:53:34:53:42 | Key | ECDSAP256SignAndVerify.java:54:57:54:63 | Message | SignatureInput | | +| Ed448SignAndVerify.java:32:32:32:57 | SignOperation | Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | Ed448SignAndVerify.java:30:31:30:40 | Key | Ed448SignAndVerify.java:31:27:31:33 | Message | | SignatureOutput | +| Ed448SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | Ed448SignAndVerify.java:38:34:38:42 | Key | Ed448SignAndVerify.java:39:29:39:35 | Message | SignatureInput | | +| Ed25519SignAndVerify.java:32:32:32:57 | SignOperation | Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:30:31:30:40 | Key | Ed25519SignAndVerify.java:31:27:31:33 | Message | | SignatureOutput | +| Ed25519SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:38:34:38:42 | Key | Ed25519SignAndVerify.java:39:29:39:35 | Message | SignatureInput | | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.ql index 6ad205cd5676..9eda60a6a0a8 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.ql +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.ql @@ -1,7 +1,15 @@ import java import experimental.quantum.Language -string getAnOutputArtifact(Crypto::KeyOperationNode n) { +string getASignatureInput(Crypto::SignatureOperationNode n) { + exists(Crypto::SignatureArtifactNode input | + input = n.getASignatureArtifact() and result = input.toString() + ) + or + not exists(n.getASignatureArtifact()) and result = "" +} + +string getASignatureOutput(Crypto::SignatureOperationNode n) { exists(Crypto::KeyOperationOutputNode output | output = n.getAnOutputArtifact() and result = output.toString() ) @@ -10,4 +18,5 @@ string getAnOutputArtifact(Crypto::KeyOperationNode n) { } from Crypto::SignatureOperationNode n -select n, n.getAKey(), n.getAnInputArtifact(), getAnOutputArtifact(n) +select n, n.getAKnownAlgorithm(), n.getAKey(), n.getAnInputArtifact(), getASignatureInput(n), + getASignatureOutput(n) diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java new file mode 100644 index 000000000000..e235d7a81f56 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java @@ -0,0 +1,13 @@ +package org.bouncycastle.asn1.sec; + +import org.bouncycastle.asn1.x9.X9ECParameters; + +public class SECNamedCurves { + public static X9ECParameters getByName(String name) { + return new X9ECParameters(); + } + + public static String getName(Object oid) { + return "secp256r1"; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/x9/X9ECParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/x9/X9ECParameters.java new file mode 100644 index 000000000000..bce95e918858 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/x9/X9ECParameters.java @@ -0,0 +1,27 @@ +package org.bouncycastle.asn1.x9; + +import java.math.BigInteger; + +public class X9ECParameters { + public X9ECParameters() { } + + public Object getCurve() { + return new Object(); + } + + public Object getG() { + return new Object(); + } + + public BigInteger getN() { + return BigInteger.ZERO; + } + + public BigInteger getH() { + return BigInteger.ONE; + } + + public byte[] getSeed() { + return new byte[0]; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java new file mode 100644 index 000000000000..9d61d9ff2db1 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java @@ -0,0 +1,16 @@ +package org.bouncycastle.crypto.generators; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; + +public class ECKeyPairGenerator { + private ECKeyGenerationParameters parameters; + + public void init(ECKeyGenerationParameters parameters) { + this.parameters = parameters; + } + + public AsymmetricCipherKeyPair generateKeyPair() { + return new AsymmetricCipherKeyPair(null, null); + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECDomainParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECDomainParameters.java new file mode 100644 index 000000000000..9334d3db6a3d --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECDomainParameters.java @@ -0,0 +1,15 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.asn1.x9.X9ECParameters; + +public class ECDomainParameters { + private final X9ECParameters parameters; + + public ECDomainParameters(X9ECParameters parameters) { + this.parameters = parameters; + } + + public X9ECParameters getParameters() { + return parameters; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java new file mode 100644 index 000000000000..edda1a50edef --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECKeyGenerationParameters.java @@ -0,0 +1,21 @@ +package org.bouncycastle.crypto.params; + +import java.security.SecureRandom; + +public class ECKeyGenerationParameters { + private final ECDomainParameters domainParameters; + private final SecureRandom random; + + public ECKeyGenerationParameters(ECDomainParameters domainParameters, SecureRandom random) { + this.domainParameters = domainParameters; + this.random = random; + } + + public ECDomainParameters getDomainParameters() { + return domainParameters; + } + + public SecureRandom getRandom() { + return random; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java new file mode 100644 index 000000000000..13c5b920ad02 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECPrivateKeyParameters.java @@ -0,0 +1,21 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +public class ECPrivateKeyParameters { + private final BigInteger d; + private final ECDomainParameters parameters; + + public ECPrivateKeyParameters(BigInteger d, ECDomainParameters parameters) { + this.d = d; + this.parameters = parameters; + } + + public BigInteger getD() { + return d; + } + + public ECDomainParameters getParameters() { + return parameters; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECPublicKeyParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECPublicKeyParameters.java new file mode 100644 index 000000000000..fcffd94c9a26 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECPublicKeyParameters.java @@ -0,0 +1,19 @@ +package org.bouncycastle.crypto.params; + +public class ECPublicKeyParameters { + private final Object q; + private final ECDomainParameters parameters; + + public ECPublicKeyParameters(Object q, ECDomainParameters parameters) { + this.q = q; + this.parameters = parameters; + } + + public Object getQ() { + return q; + } + + public ECDomainParameters getParameters() { + return parameters; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/ECDSASigner.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/ECDSASigner.java new file mode 100644 index 000000000000..7cb2ab2ddf25 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/signers/ECDSASigner.java @@ -0,0 +1,23 @@ +package org.bouncycastle.crypto.signers; + +import java.math.BigInteger; + +public class ECDSASigner { + private boolean forSigning; + private Object keyParameter; + + public void init(boolean forSigning, Object keyParameter) { + this.forSigning = forSigning; + this.keyParameter = keyParameter; + } + + public void update(byte[] message, int offset, int length) { } + + public BigInteger[] generateSignature(byte[] message) { + return new BigInteger[] { BigInteger.ZERO, BigInteger.ZERO }; + } + + public boolean verifySignature(byte[] message, BigInteger r, BigInteger s) { + return true; + } +} From 4a34a5c176dd299bf816decbfb36e71b36ae0987 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Mon, 2 Jun 2025 16:32:05 +0200 Subject: [PATCH 12/32] Created additional stubs for ECDSA --- .../bouncycastle/asn1/x9/X9ECParameters.java | 10 +++-- .../crypto/params/ECDomainParameters.java | 33 +++++++++++++++ .../asymmetric/ec/BCECPrivateKey.java | 41 +++++++++++++++++++ .../provider/asymmetric/ec/BCECPublicKey.java | 37 +++++++++++++++++ .../bouncycastle/jce/ECNamedCurveTable.java | 13 ++++++ .../jce/spec/ECNamedCurveParameterSpec.java | 39 ++++++++++++++++++ .../jce/spec/ECNamedCurveSpec.java | 16 ++++++++ .../org/bouncycastle/math/ec/ECCurve.java | 19 +++++++++ .../org/bouncycastle/math/ec/ECPoint.java | 35 ++++++++++++++++ 9 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/spec/ECNamedCurveParameterSpec.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/spec/ECNamedCurveSpec.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/math/ec/ECCurve.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/math/ec/ECPoint.java diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/x9/X9ECParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/x9/X9ECParameters.java index bce95e918858..88c2c8d77c8b 100644 --- a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/x9/X9ECParameters.java +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/x9/X9ECParameters.java @@ -1,16 +1,18 @@ package org.bouncycastle.asn1.x9; import java.math.BigInteger; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; public class X9ECParameters { public X9ECParameters() { } - public Object getCurve() { - return new Object(); + public ECCurve getCurve() { + return new ECCurve(); } - public Object getG() { - return new Object(); + public ECPoint getG() { + return new ECPoint(); } public BigInteger getN() { diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECDomainParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECDomainParameters.java index 9334d3db6a3d..127792dc9528 100644 --- a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECDomainParameters.java +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECDomainParameters.java @@ -1,15 +1,48 @@ package org.bouncycastle.crypto.params; import org.bouncycastle.asn1.x9.X9ECParameters; +import java.math.BigInteger; public class ECDomainParameters { private final X9ECParameters parameters; + private final Object curve; + private final Object g; + private final BigInteger n; + private final BigInteger h; public ECDomainParameters(X9ECParameters parameters) { this.parameters = parameters; + this.curve = null; + this.g = null; + this.n = null; + this.h = null; + } + + public ECDomainParameters(Object curve, Object g, BigInteger n, BigInteger h) { + this.parameters = null; + this.curve = curve; + this.g = g; + this.n = n; + this.h = h; } public X9ECParameters getParameters() { return parameters; } + + public Object getCurve() { + return curve; + } + + public Object getG() { + return g; + } + + public BigInteger getN() { + return n; + } + + public BigInteger getH() { + return h; + } } diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java new file mode 100644 index 000000000000..9702a431e8be --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java @@ -0,0 +1,41 @@ +package org.bouncycastle.jcajce.provider.asymmetric.ec; + +import java.security.interfaces.ECPrivateKey; +import java.math.BigInteger; + +public class BCECPrivateKey implements ECPrivateKey { + private final BigInteger d; + + public BCECPrivateKey(BigInteger d) { + this.d = d; + } + + public BigInteger getD() { + return d; + } + + @Override + public String getAlgorithm() { + return "EC"; + } + + @Override + public String getFormat() { + return "PKCS#8"; + } + + @Override + public byte[] getEncoded() { + return new byte[0]; + } + + @Override + public java.security.spec.ECParameterSpec getParams() { + return null; + } + + @Override + public BigInteger getS() { + return d; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java new file mode 100644 index 000000000000..d7b9effbb97f --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java @@ -0,0 +1,37 @@ +package org.bouncycastle.jcajce.provider.asymmetric.ec; + +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECPoint; + +public class BCECPublicKey implements ECPublicKey { + private final ECPoint w; + + public BCECPublicKey(ECPoint w) { + this.w = w; + } + + @Override + public String getAlgorithm() { + return "EC"; + } + + @Override + public String getFormat() { + return "X.509"; + } + + @Override + public byte[] getEncoded() { + return new byte[0]; + } + + @Override + public java.security.spec.ECParameterSpec getParams() { + return null; + } + + @Override + public ECPoint getW() { + return w; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java new file mode 100644 index 000000000000..527015011822 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java @@ -0,0 +1,13 @@ +package org.bouncycastle.jce; + +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; + +public class ECNamedCurveTable { + public static ECNamedCurveParameterSpec getParameterSpec(String name) { + return new ECNamedCurveParameterSpec(name, null, null, null, null); + } + + public static String[] getNames() { + return new String[] { "secp256r1", "secp384r1", "secp521r1" }; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/spec/ECNamedCurveParameterSpec.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/spec/ECNamedCurveParameterSpec.java new file mode 100644 index 000000000000..17853711d869 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/spec/ECNamedCurveParameterSpec.java @@ -0,0 +1,39 @@ +package org.bouncycastle.jce.spec; + +import java.math.BigInteger; + +public class ECNamedCurveParameterSpec { + private final String name; + private final Object curve; + private final Object g; + private final BigInteger n; + private final BigInteger h; + + public ECNamedCurveParameterSpec(String name, Object curve, Object g, BigInteger n, BigInteger h) { + this.name = name; + this.curve = curve; + this.g = g; + this.n = n; + this.h = h; + } + + public String getName() { + return name; + } + + public Object getCurve() { + return curve; + } + + public Object getG() { + return g; + } + + public BigInteger getN() { + return n; + } + + public BigInteger getH() { + return h; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/spec/ECNamedCurveSpec.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/spec/ECNamedCurveSpec.java new file mode 100644 index 000000000000..e627a4e9e8d5 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/spec/ECNamedCurveSpec.java @@ -0,0 +1,16 @@ +package org.bouncycastle.jce.spec; + +import java.security.spec.ECParameterSpec; + +public class ECNamedCurveSpec extends ECParameterSpec { + private final String name; + + public ECNamedCurveSpec(String name, Object curve, Object generator, Object order) { + super(null, null, null, 0); + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/math/ec/ECCurve.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/math/ec/ECCurve.java new file mode 100644 index 000000000000..9af93d8f6369 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/math/ec/ECCurve.java @@ -0,0 +1,19 @@ +package org.bouncycastle.math.ec; + +import java.math.BigInteger; + +public class ECCurve { + public ECCurve() { } + + public ECPoint createPoint(BigInteger x, BigInteger y) { + return new ECPoint(); + } + + public ECPoint getInfinity() { + return new ECPoint(); + } + + public int getFieldSize() { + return 256; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/math/ec/ECPoint.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/math/ec/ECPoint.java new file mode 100644 index 000000000000..753d132c3fdb --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/math/ec/ECPoint.java @@ -0,0 +1,35 @@ +package org.bouncycastle.math.ec; + +import java.math.BigInteger; + +public class ECPoint { + public ECPoint() { } + + public ECPoint add(ECPoint other) { + return new ECPoint(); + } + + public ECPoint multiply(BigInteger k) { + return new ECPoint(); + } + + public ECPoint negate() { + return new ECPoint(); + } + + public boolean isInfinity() { + return false; + } + + public ECPoint normalize() { + return this; + } + + public BigInteger getAffineXCoord() { + return BigInteger.ZERO; + } + + public BigInteger getAffineYCoord() { + return BigInteger.ZERO; + } +} From f17bc7e99562f66cd66600e071ab31899f8a1b9c Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Mon, 2 Jun 2025 16:33:13 +0200 Subject: [PATCH 13/32] Added EllipticCurveConsumingAlgorithmInstance to Model.qll This commit adds EllipticCurveConsumingAlgorithmInstance to the shared model, allowing us to model and graph elliptic curve algorithms. --- .../lib/experimental/quantum/BouncyCastle.qll | 428 +---------------- .../BouncyCastle/AlgorithmInstances.qll | 177 +++++-- .../BouncyCastle/AlgorithmValueConsumers.qll | 13 + .../quantum/BouncyCastle/FlowAnalysis.qll | 68 ++- .../BouncyCastle/OperationInstances.qll | 443 ++++++++++++++++++ .../codeql/quantum/experimental/Model.qll | 58 ++- 6 files changed, 707 insertions(+), 480 deletions(-) create mode 100644 java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll index ad7b00f0a419..774f82f91c92 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -1,426 +1,4 @@ import java - -module Params { - import Language - import BouncyCastle.FlowAnalysis - import BouncyCastle.AlgorithmInstances - - /** - * A model of the `Parameters` class in Bouncy Castle. - */ - class Parameters extends RefType { - Parameters() { - // Matches `org.bouncycastle.crypto.params`, `org.bouncycastle.asn1.x9`, etc. - this.getPackage().getName().matches("org.bouncycastle.%") and - this.getName().matches("%Parameters") - } - } - - class KeyParameters extends Parameters { - KeyParameters() { - this.getPackage().getName() = "org.bouncycastle.crypto.params" and - this.getName().matches("%KeyParameters") - } - } - - /** - * Any call that returns a BouncyCastle parameters object. This type is used - * to model data flow to resolve algorithm instances like elliptic curves. - * - * Examples: - * ``` - * curveParams = SECNamedCurves.getByName(...); - * domainParams = new ECDomainParameters(...); - * ``` - */ - class ParametersInstantiation extends Call { - ParametersInstantiation() { - // Class instantiations - this.(ConstructorCall) - .getConstructedType() - .getPackage() - .getName() - .matches("org.bouncycastle.%") and - this.(ConstructorCall).getConstructedType() instanceof Parameters - or - // (Static) factory methods - this.(MethodCall) - .getCallee() - .getDeclaringType() - .getPackage() - .getName() - .matches("org.bouncycastle.%") and - this.(MethodCall).getType() instanceof Parameters - } - - // Can be overridden by subclasses which take a key size argument. - Expr getKeySizeArg() { none() } - - Crypto::ConsumerInputDataFlowNode getAKeySizeConsumer() { - result.asExpr() = this.getKeySizeArg() - } - - // Can be overridden by subclasses which take an algorithm argument. - Expr getAlgorithmArg() { none() } - - Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - result.getInputNode().asExpr() = this.getAlgorithmArg() - } - - Expr getAParametersArg() { - result = this.getAnArgument() and - result.getType() instanceof Parameters - } - - Crypto::ConsumerInputDataFlowNode getAParametersConsumer() { - result.asExpr() = this.getAParametersArg() - } - } - - class X9ECParametersInstantiation extends ParametersInstantiation { - X9ECParametersInstantiation() { this.(Expr).getType().getName() = "X9ECParameters" } - - override Expr getAlgorithmArg() { result = this.getArgument(0) } - } - - class EllipticCurveStringLiteralArg extends EllipticCurveAlgorithmValueConsumer instanceof Expr { - ParametersInstantiation params; - - EllipticCurveStringLiteralArg() { this = params.getAlgorithmArg() } - - override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { - result.(EllipticCurveStringLiteralInstance).getConsumer() = this - } - } -} - -/** - * Models for the signature algorithms defined by the `org.bouncycastle.crypto.signers` package. - */ -module Signers { - import Language - import BouncyCastle.FlowAnalysis - import BouncyCastle.AlgorithmInstances - - /** - * A model of the `Signer` class in Bouncy Castle. - */ - class Signer extends RefType { - Signer() { - this.getPackage().getName() = "org.bouncycastle.crypto.signers" and - this.getName().matches("%Signer") - } - - MethodCall getAnInitCall() { result = this.getAMethodCall("init") } - - MethodCall getAUseCall() { - result = this.getAMethodCall(["update", "generateSignature", "verifySignature"]) - } - - MethodCall getAMethodCall(string name) { - result.getCallee().hasQualifiedName(this.getPackage().getName(), this.getName(), name) - } - - // Overridden by subclasses to provide the message argument. - Expr getMessageArg(MethodCall call) { - call.getCallee().getName() = "update" and - result = call.getArgument(0) - } - - // Overridden by subclasses to provide the signature argument. - Expr getSignatureArg(MethodCall call) { - call.getCallee().getName() = "verifySignature" and - result = call.getArgument(0) - } - - // Overridden by subclasses to provide the signature output. - Expr getSignatureOutput(MethodCall call) { - call.getCallee().getName() = "generateSignature" and - result = call - } - } - - class ECDSASigner extends Signer { - ECDSASigner() { this.getName().matches("ECDSA%") } - - override Expr getMessageArg(MethodCall call) { - // For ECDSA the message is passed directly to `generateSignature()`. - call.getCallee().getName().matches(["generateSignature", "verifySignature"]) and - result = call.getArgument(0) - } - - override Expr getSignatureArg(MethodCall call) { - // For ECDSA, r and s are passed to `verifySignature()` as separate arguments. - call.getCallee().getName() = "verifySignature" and - result = call.getArgument([1, 2]) - } - } - - /** - * BouncyCastle algorithms are instantiated by calling the constructor of the - * corresponding class, which also represents the algorithm instance. - */ - private class SignerNewCall = SignatureAlgorithmInstance; - - /** - * The type is instantiated by a constructor call and initialized by a call to - * `init()` which takes two arguments. The first argument is a flag indicating - * whether the operation is signing data or verifying a signature, and the - * second is the key to use. - */ - private class SignerInitCall extends MethodCall { - SignerInitCall() { this = any(Signer signer).getAnInitCall() } - - Expr getForSigningArg() { result = this.getArgument(0) } - - Expr getKeyArg() { - this.getParameterArg().getType() instanceof Params::KeyParameters and - result = this.getParameterArg() - } - - // The second argument is used to provide parameters (like the key) to the signer. - Expr getParameterArg() { result = this.getArgument(1) } - - Crypto::ConsumerInputDataFlowNode getAParametersConsumer() { - result.asExpr() = this.getParameterArg() - } - - // TODO: Support dataflow for the operation sub-type. - Crypto::KeyOperationSubtype getKeyOperationSubtype() { - if this.isOperationSubTypeKnown() - then - this.getForSigningArg().(BooleanLiteral).getBooleanValue() = true and - result = Crypto::TSignMode() - or - this.getForSigningArg().(BooleanLiteral).getBooleanValue() = false and - result = Crypto::TVerifyMode() - else result = Crypto::TUnknownKeyOperationMode() - } - - predicate isOperationSubTypeKnown() { this.getForSigningArg() instanceof BooleanLiteral } - } - - /** - * The `update()` method is used to pass message data to the signer, and the - * `generateSignature()` or `verifySignature()` methods are used to produce or - * verify the signature, respectively. - */ - private class SignerUseCall extends MethodCall { - Signer signer; - - SignerUseCall() { this = signer.getAUseCall() } - - predicate isIntermediate() { this.getCallee().getName() = "update" } - - Expr getMessageInput() { result = signer.getMessageArg(this) } - - Expr getSignatureInput() { result = signer.getSignatureArg(this) } - - Expr getSignatureOutput() { result = signer.getSignatureOutput(this) } - } - - /** - * Instantiate the flow analysis module for the `Signer` class. - */ - private module SignerFlow = - NewToInitToUseFlowAnalysis; - - /** - * A signing operation instance is a call to either `update()`, `generateSignature()`, - * or `verifySignature()` on a `Signer` instance. - */ - class SignatureOperationInstance extends Crypto::SignatureOperationInstance instanceof SignerUseCall - { - SignatureOperationInstance() { not this.isIntermediate() } - - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - result = SignerFlow::getNewFromUse(this, _, _) - } - - override Crypto::KeyOperationSubtype getKeyOperationSubtype() { - // This is less expensive and more robust than resolving the subtype using - // dataflow from the `forSigning` argument to `init()`. - if super.getMethod().getName() = "generateSignature" - then result = Crypto::TSignMode() - else - if super.getMethod().getName() = "verifySignature" - then result = Crypto::TVerifyMode() - else result = Crypto::TUnknownKeyOperationMode() - } - - override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { - result.asExpr() = this.getInitCall().getKeyArg() - } - - override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { none() } - - override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - // Inputs to signers with streaming APIs - result.asExpr() = this.getAnUpdateCall().getMessageInput() - or - // Inputs to signers with one shot APIs - result.asExpr() = super.getMessageInput() - } - - override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { - result.asExpr() = super.getSignatureInput() - } - - override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - // Signature output - result.asExpr() = super.getSignatureOutput() - } - - SignerInitCall getInitCall() { result = SignerFlow::getInitFromUse(this, _, _) } - - SignerUseCall getAnUpdateCall() { - result = SignerFlow::getAnIntermediateUseFromFinalUse(this, _, _) - } - } -} - -/** - * Models for the key generation algorithms defined by the `org.bouncycastle.crypto.generators` package. - */ -module Generators { - import Language - import BouncyCastle.FlowAnalysis - import BouncyCastle.AlgorithmInstances - - /** - * A model of the `KeyGenerator` and `KeyPairGenerator` classes in Bouncy Castle. - */ - class KeyGenerator extends RefType { - Crypto::KeyArtifactType type; - - KeyGenerator() { - this.getPackage().getName() = "org.bouncycastle.crypto.generators" and - ( - this.getName().matches("%KeyGenerator") and type instanceof Crypto::TSymmetricKeyType - or - this.getName().matches("%KeyPairGenerator") and type instanceof Crypto::TAsymmetricKeyType - ) - } - - MethodCall getAnInitCall() { result = this.getAMethodCall("init") } - - MethodCall getAUseCall() { result = this.getAMethodCall(["generateKey", "generateKeyPair"]) } - - MethodCall getAMethodCall(string name) { - result.getCallee().hasQualifiedName(this.getPackage().getName(), this.getName(), name) - } - - Crypto::KeyArtifactType getKeyType() { result = type } - } - - /** - * This type is used to model data flow from a key pair to the private and - * public components of the key pair. - */ - class KeyPair extends RefType { - KeyPair() { - this.getPackage().getName() = "org.bouncycastle.crypto" and - this.getName() = "%KeyPair" // `AsymmetricCipherKeyPair` or `EphemeralKeyPair` - } - - MethodCall getPublicKeyCall() { result = this.getAMethodCall("getPublic") } - - MethodCall getPrivateKeyCall() { result = this.getAMethodCall("getPrivate") } - - MethodCall getAMethodCall(string name) { - result.getCallee().hasQualifiedName("org.bouncycastle.crypto", this.getName(), name) - } - } - - /** - * BouncyCastle algorithms are instantiated by calling the constructor of the - * corresponding class, which also represents the algorithm instance. - */ - private class KeyGeneratorNewCall = KeyGenerationAlgorithmInstance; - - /** - * The type is instantiated by a constructor call and initialized by a call to - * `init()` which takes a single `KeyGenerationParameters` argument. - */ - private class KeyGeneratorInitCall extends MethodCall { - KeyGenerator gen; - - KeyGeneratorInitCall() { this = gen.getAnInitCall() } - - // TODO: We may need to model this using the `parameters` argument passed to - // the `init()` method. - Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } - - // The `KeyGenerationParameters` argument used to configure the key generator. - Crypto::ConsumerInputDataFlowNode getAParametersConsumer() { - result.asExpr() = this.getArgument(0) - } - } - - /** - * The `generateKey()` and `generateKeyPair()` methods are used to generate - * the resulting key, depending on the type of the generator. - */ - private class KeyGeneratorUseCall extends MethodCall { - KeyGenerator gen; - - KeyGeneratorUseCall() { this = gen.getAUseCall() } - - // Since key generators don't have `update()` methods, this is always false. - predicate isIntermediate() { none() } - - Crypto::KeyArtifactType getKeyType() { result = gen.getKeyType() } - - Expr getOutput() { result = this } - } - - private module KeyGeneratorFlow = - NewToInitToUseFlowAnalysis; - - private module ParametersFlow = - ParametersToInitFlowAnalysis; - - Params::ParametersInstantiation getParametersFromInit(KeyGeneratorInitCall init) { - result = ParametersFlow::getParametersFromInit(init, _, _) and - result instanceof Params::X9ECParametersInstantiation - } - - /** - * A key generation operation instance is a call to `generateKey()` or - * `generateKeyPair()` on a key generator defined under - * `org.bouncycastle.crypto.generators`. - */ - class KeyGenerationOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGeneratorUseCall - { - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - // The algorithm value consumer flows through a parameters argument to `init()` - result = this.getParameters().getAnAlgorithmValueConsumer() - or - // The algorithm is implicit in the key generator type - result = KeyGeneratorFlow::getNewFromUse(this, _, _) - } - - override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { - result.asExpr() = super.getOutput() - } - - override Crypto::KeyArtifactType getOutputKeyType() { result = super.getKeyType() } - - override int getKeySizeFixed() { - result = KeyGeneratorFlow::getNewFromUse(this, _, _).getKeySizeFixed() - } - - override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { - result = KeyGeneratorFlow::getInitFromUse(this, _, _).getKeySizeConsumer() - } - - Params::ParametersInstantiation getParameters() { - exists(KeyGeneratorInitCall init | - init = KeyGeneratorFlow::getInitFromUse(this, _, _) and - result = ParametersFlow::getParametersFromInit(init, _, _) - ) - } - } -} +import BouncyCastle.AlgorithmInstances +import BouncyCastle.AlgorithmValueConsumers +import BouncyCastle.OperationInstances diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index df082f877346..a44e8cbd0b81 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -4,9 +4,32 @@ import AlgorithmValueConsumers import FlowAnalysis /** - * Elliptic curve algorithms where the curve is implicitly defined by the type. + * Elliptic curve algorithms. */ -abstract private class EllipticCurveAlgorithmInstance extends Crypto::EllipticCurveInstance { +abstract private class EllipticCurveConsumingAlgorithmInstance extends Crypto::EllipticCurveConsumingAlgorithmInstance +{ + string getFixedEllipticCurveName() { none() } + + override Crypto::AlgorithmValueConsumer getEllipticCurveConsumer() { + result.(ImplicitEllipticCurveInstance).getAlgorithm() = this + } +} + +/** + * A string literal that represents an elliptic curve name. + */ +class EllipticCurveStringLiteralInstance extends Crypto::EllipticCurveInstance instanceof StringLiteral +{ + EllipticCurveStringLiteralInstance() { + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getValue().toUpperCase(), _, _) + } + + override string getRawEllipticCurveName() { result = super.getValue() } + + EllipticCurveAlgorithmValueConsumer getConsumer() { + result = EllipticCurveStringLiteralToConsumer::getConsumerFromLiteral(this, _, _) + } + override Crypto::TEllipticCurveType getEllipticCurveType() { Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName().toUpperCase(), _, result) @@ -19,18 +42,36 @@ abstract private class EllipticCurveAlgorithmInstance extends Crypto::EllipticCu } /** - * A string literal that represents an elliptic curve name. + * Represents an elliptic curve that is implicitly defined by the underlying + * algorithm. In this case, we view the algorithm and elliptic curve as being + * implicitly defined by the constructor call. */ -class EllipticCurveStringLiteralInstance extends EllipticCurveAlgorithmInstance instanceof StringLiteral +class ImplicitEllipticCurveInstance extends Crypto::EllipticCurveInstance, + EllipticCurveAlgorithmValueConsumer instanceof ClassInstanceExpr { - EllipticCurveStringLiteralInstance() { - Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getValue().toUpperCase(), _, _) + EllipticCurveConsumingAlgorithmInstance algorithm; + + ImplicitEllipticCurveInstance() { + this = algorithm and + exists(algorithm.getFixedEllipticCurveName()) } - override string getRawEllipticCurveName() { result = super.getValue() } + EllipticCurveConsumingAlgorithmInstance getAlgorithm() { result = this } - EllipticCurveAlgorithmValueConsumer getConsumer() { - result = EllipticCurveStringLiteralToConsumer::getConsumerFromLiteral(this, _, _) + override string getRawEllipticCurveName() { result = algorithm.getFixedEllipticCurveName() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } + + override Crypto::TEllipticCurveType getEllipticCurveType() { + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName().toUpperCase(), + _, result) + } + + override int getKeySize() { + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName().toUpperCase(), + result, _) } } @@ -40,7 +81,6 @@ class EllipticCurveStringLiteralInstance extends EllipticCurveAlgorithmInstance abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr { - // TODO: Could potentially be used to model signature modes like Ed25519ph and Ed25519ctx. override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } @@ -59,7 +99,17 @@ abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmI typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) } - Crypto::ConsumerInputDataFlowNode getAParametersConsumer() { none() } + /** + * Used for data flow from elliptic curve string literals to the algorithm + * instance. + */ + DataFlow::Node getParametersInput() { none() } + + /** + * Used for data flow from elliptic curve string literals to the algorithm + * instance. + */ + DataFlow::Node getEllipticCurveInput() { none() } } /** @@ -76,29 +126,25 @@ class DSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceo } } -abstract private class EllipticCurveSignatureAlgorithmInstance extends SignatureAlgorithmInstance, - EllipticCurveAlgorithmInstance -{ - override Crypto::EllipticCurveInstance getEllipticCurve() { result = this } -} - /** * Ed25519, Ed25519ph, and Ed25519ctx signers. */ -class Ed25519SignatureAlgorithmInstance extends EllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr +class Ed25519SignatureAlgorithmInstance extends SignatureAlgorithmInstance, + EllipticCurveConsumingAlgorithmInstance instanceof ClassInstanceExpr { Ed25519SignatureAlgorithmInstance() { super.getConstructedType() instanceof Signers::Signer and super.getConstructedType().getName().matches("Ed25519%") } - override string getRawEllipticCurveName() { result = "CURVE25519" } + override string getFixedEllipticCurveName() { result = "Curve25519" } } /** * Ed448 and Ed448ph signers. */ -class Ed448SignatureAlgorithmInstance extends EllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr +class Ed448SignatureAlgorithmInstance extends SignatureAlgorithmInstance, + EllipticCurveConsumingAlgorithmInstance instanceof ClassInstanceExpr { Ed448SignatureAlgorithmInstance() { super.getConstructedType() instanceof Signers::Signer and @@ -109,7 +155,7 @@ class Ed448SignatureAlgorithmInstance extends EllipticCurveSignatureAlgorithmIns typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) } - override string getRawEllipticCurveName() { result = "CURVE448" } + override string getFixedEllipticCurveName() { result = "Curve448" } } /** @@ -121,9 +167,6 @@ class Ed448SignatureAlgorithmInstance extends EllipticCurveSignatureAlgorithmIns * - By using the `ECNamedCurveTable` class, which is used to obtain the curve parameters. * - By using the `ECNamedCurveSpec` class, which is passed to the constructor of the signer. * - By using the `ECParameterSpec` class, which is passed to the constructor of the signer. - * - * NOTE: This type does not inherit from `EllipticCurveSignatureAlgorithmInstance` because the curve - * is not implicitly defined by the type, but rather by the key parameters passed to `init()`. */ class ECDSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { @@ -143,8 +186,24 @@ class ECDSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanc override int getKeySizeFixed() { none() } } +class LMSSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { + LMSSignatureAlgorithmInstance() { + super.getConstructedType() instanceof Signers::Signer and + super.getConstructedType().getName().matches("LMS%") + } + + override string getRawAlgorithmName() { + typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::LMS()) + } +} + /** - * Key generation algorithms where the algorithm is implicitly defined by the type. + * Key generation algorithms where the algorithm is implicitly defined by the + * type. */ abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, KeyGenerationAlgorithmValueConsumer instanceof ClassInstanceExpr @@ -167,41 +226,43 @@ abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgori typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) } - Crypto::ConsumerInputDataFlowNode getAParametersConsumer() { none() } + /** + * Used for data flow from elliptic curve string literals to the algorithm + * instance. + */ + DataFlow::Node getParametersInput() { none() } + + /** + * Used for data flow from elliptic curve string literals to the algorithm + * instance. + */ + DataFlow::Node getEllipticCurveInput() { none() } } -/** - * Key generation algorithms for elliptic curves where the curve is implicitly defined by the type. - */ -abstract private class EllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance, - EllipticCurveAlgorithmInstance -{ } - -class Ed25519KeyGenerationAlgorithmInstance extends EllipticCurveKeyGenerationAlgorithmInstance instanceof ClassInstanceExpr +class Ed25519KeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance, + EllipticCurveConsumingAlgorithmInstance instanceof ClassInstanceExpr { Ed25519KeyGenerationAlgorithmInstance() { super.getConstructedType() instanceof Generators::KeyGenerator and super.getConstructedType().getName().matches("Ed25519%") } - override string getRawEllipticCurveName() { result = "Curve25519" } + override string getFixedEllipticCurveName() { result = "Curve25519" } } -class Ed448KeyGenerationAlgorithmInstance extends EllipticCurveKeyGenerationAlgorithmInstance instanceof ClassInstanceExpr +class Ed448KeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance, + EllipticCurveConsumingAlgorithmInstance instanceof ClassInstanceExpr { Ed448KeyGenerationAlgorithmInstance() { super.getConstructedType() instanceof Generators::KeyGenerator and super.getConstructedType().getName().matches("Ed448%") } - override string getRawEllipticCurveName() { result = "Curve25519" } + override string getFixedEllipticCurveName() { result = "Curve448" } } /** - * Represents a generic `ECKeyPairGenerator` instances. - * - * NOTE: This type does not inherit from `EllipticCurveKeyGenerationAlgorithmInstance` because the curve - * is not implicitly defined by the type, but rather by parameters passed to the constructor. + * Represents a generic `ECKeyPairGenerator` instance. */ class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance instanceof ClassInstanceExpr { @@ -211,9 +272,10 @@ class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAl } override string getRawAlgorithmName() { - // The generator constructs an elliptic curve key pair, but the algorithm is - // not determined at key generation. As an example, the key could be used - // for either ECDSA or ECDH For this reason, we just return "EllipticCurve". + // TODO: The generator constructs an elliptic curve key pair, but the + // algorithm is not determined at key generation. As an example, the key + // could be used for either ECDSA or ECDH. For this reason, we just return + // "EllipticCurve". result = "EllipticCurve" } @@ -223,6 +285,29 @@ class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAl } } +/** + * Represents LMS key generation instances. The algorithm is implicitly defined + * by the type. + * + * TODO: Determine how to represent LMS parameters, such as the hash function + * and the tree height. + */ +class LMSKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance instanceof ClassInstanceExpr +{ + LMSKeyGenerationAlgorithmInstance() { + super.getConstructedType() instanceof Generators::KeyGenerator and + super.getConstructedType().getName().matches("LMS%") + } + + override string getRawAlgorithmName() { + typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::LMS()) + } +} + /** * Private predicates mapping type names to raw names, key sizes and algorithms. */ @@ -239,6 +324,10 @@ private predicate typeNameToRawAlgorithmName(string typeName, string algorithmNa // ECDSA typeName.matches("ECDSA%") and algorithmName = "ECDSA" + or + // LMS + typeName.matches("LMS%") and + algorithmName = "LMS" } private predicate signatureNameToKeySizeAndAlgorithmMapping( diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll index 37f7d7acf17a..0dbbee6ab240 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll @@ -1,5 +1,6 @@ import java import experimental.quantum.Language +import AlgorithmInstances abstract class HashAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } @@ -7,6 +8,18 @@ abstract class CipherAlgorithmValueConsumer extends Crypto::AlgorithmValueConsum abstract class EllipticCurveAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } +class EllipticCurveStringLiteralArg extends EllipticCurveAlgorithmValueConsumer instanceof Expr { + Params::ParametersInstantiation params; + + EllipticCurveStringLiteralArg() { this = params.getAlgorithmArg() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(EllipticCurveStringLiteralInstance).getConsumer() = this + } +} + /** * Signature algorithms are implicitly defined by the constructor. */ diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll index e2ab88acedf2..1d853b6fc77c 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll @@ -1,6 +1,6 @@ import java -import experimental.quantum.Language import semmle.code.java.dataflow.DataFlow +import experimental.quantum.Language import AlgorithmInstances import AlgorithmValueConsumers @@ -12,14 +12,19 @@ signature class NewCallSig instanceof Call { /** * Gets a parameter argument that is used to initialize the object. */ - Crypto::ConsumerInputDataFlowNode getAParametersConsumer(); + DataFlow::Node getParametersInput(); + + /** + * Gets a `ECCurve` argument that is used to initialize the object. + */ + DataFlow::Node getEllipticCurveInput(); } signature class InitCallSig instanceof MethodCall { /** * Gets a parameter argument that is used to initialize the object. */ - Crypto::ConsumerInputDataFlowNode getAParametersConsumer(); + DataFlow::Node getParametersInput(); } signature class UseCallSig instanceof MethodCall { @@ -175,6 +180,21 @@ module NewToInitToUseFlowAnalysis { module ParametersToInitConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source.asExpr() instanceof New } - predicate isSink(DataFlow::Node sink) { - exists(Init init | sink = init.getAParametersConsumer()) - } + predicate isSink(DataFlow::Node sink) { exists(Init init | sink = init.getParametersInput()) } /** * Pass-through for parameters created from other parameters. @@ -206,13 +227,37 @@ module ParametersToInitFlowAnalysis { * ``` * X9ECParameters ecParams = SECNamedCurves.getByName("secp256r1"); * ECDomainParameters domainParams = new ECDomainParameters(ecParams); - * ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, random); + * ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, ...); * ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); * keyPairGenerator.init(keyGenParams); * ``` + * + * We also want to track flow from parameters to the `init()` call + * via a curve instantiation. E.g. via a call to `getCurve()` as follows: + * + * Example: + * ``` + * X9ECParameters ecParams = SECNamedCurves.getByName("secp256r1"); + * ECCurve curve = ecParams.getCurve(); + * ECDomainParameters domainParams = new ECDomainParameters(curve, ...); + * ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, ...); + * ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + * keyPairGenerator.init(keyGenParams); */ predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - node1 = node2.asExpr().(New).getAParametersConsumer() + // Flow from a parameter node to a new parameter node. + node1.asExpr().getType() instanceof Params::Parameters and + node1 = node2.asExpr().(New).getParametersInput() + or + // Flow from a curve node to a parameter node. + node1.asExpr().getType() instanceof Params::Curve and + node1 = node2.asExpr().(New).getEllipticCurveInput() + or + // Flow from a parameter node instance to a curve node. + exists(CurveInstantiation c | + node1 = c.getInputNode() and + node2 = c.getOutputNode() + ) } } @@ -222,7 +267,7 @@ module ParametersToInitFlowAnalysis { Init init, ParametersToInitFlow::PathNode src, ParametersToInitFlow::PathNode sink ) { src.getNode().asExpr() = result and - sink.getNode() = init.getAParametersConsumer() and + sink.getNode() = init.getParametersInput() and ParametersToInitFlow::flowPath(src, sink) } } @@ -306,6 +351,11 @@ module EllipticCurveStringLiteralToConsumer { predicate isSink(DataFlow::Node sink) { exists(EllipticCurveAlgorithmValueConsumer consumer | sink = consumer.getInputNode()) } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + node2.asExpr().(MethodCall).getCallee().getName() = "getCurve" and + node2.asExpr().(MethodCall).getQualifier() = node1.asExpr() + } } module EllipticCurveStringLiteralToAlgorithmValueConsumerFlow = diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll new file mode 100644 index 000000000000..c8b975c7f3c0 --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll @@ -0,0 +1,443 @@ +import java + +module Params { + import FlowAnalysis + import AlgorithmInstances + + /** + * A model of the `Parameters` class in Bouncy Castle. + */ + class Parameters extends RefType { + Parameters() { + // Matches `org.bouncycastle.crypto.params`, `org.bouncycastle.asn1.x9`, etc. + this.getPackage().getName().matches("org.bouncycastle.%") and + this.getName().matches(["%Parameters", "%ParameterSpec"]) + } + } + + class Curve extends RefType { + Curve() { + this.getPackage().getName() = "org.bouncycastle.math.ec" and + this.getName().matches("ECCurve") + } + } + + class KeyParameters extends Parameters { + KeyParameters() { + this.getPackage().getName() = + ["org.bouncycastle.crypto.params", "org.bouncycastle.pqc.crypto.lms"] and + this.getName().matches("%KeyParameters") + } + } + + /** + * Any call that returns a BouncyCastle parameters object. This type is used + * to model data flow to resolve algorithm instances like elliptic curves. + * + * Examples: + * ``` + * curveParams = SECNamedCurves.getByName(...); + * domainParams = new ECDomainParameters(...); + * ``` + */ + class ParametersInstantiation extends Call { + ParametersInstantiation() { + // Class instantiations + this.(ConstructorCall).getConstructedType() instanceof Parameters + or + // (Static) factory methods + this.(MethodCall) + .getCallee() + .getDeclaringType() + .getPackage() + .getName() + .matches("org.bouncycastle.%") and + this.(MethodCall).getType() instanceof Parameters + } + + // Can be overridden by subclasses which take a key size argument. + Expr getKeySizeArg() { none() } + + // Can be overridden by subclasses which take an algorithm argument. + Expr getAlgorithmArg() { none() } + + Expr getAParametersArg() { + result = this.getAnArgument() and + result.getType() instanceof Parameters + } + + Expr getAnEllipticCurveArg() { + result = this.getAnArgument() and + result.getType() instanceof Curve + } + + Crypto::ConsumerInputDataFlowNode getAKeySizeConsumer() { + result.asExpr() = this.getKeySizeArg() + } + + Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result.getInputNode().asExpr() = this.getAlgorithmArg() + } + + DataFlow::Node getParametersInput() { result.asExpr() = this.getAParametersArg() } + + DataFlow::Node getEllipticCurveInput() { result.asExpr() = this.getAnEllipticCurveArg() } + } + + /** + * Models the named elliptic curve passed to `X9ECParameters.getCurve()`. + */ + class X9ECParametersInstantiation extends ParametersInstantiation { + X9ECParametersInstantiation() { this.(Expr).getType().getName() = "X9ECParameters" } + + override Expr getAlgorithmArg() { + this.(MethodCall).getQualifier().getType().getName() = "SECNamedCurves" and + this.(MethodCall).getCallee().getName() = "getByName" and + result = this.getArgument(0) + } + } + + /** + * Models the named elliptic curve passed to `ECNamedCurveTable.getParameterSpec()`. + */ + class ECNamedCurveParameterSpecInstantiation extends ParametersInstantiation { + ECNamedCurveParameterSpecInstantiation() { + this.(Expr).getType().getName() = "ECNamedCurveParameterSpec" + } + + override Expr getAlgorithmArg() { + this.(MethodCall).getQualifier().getType().getName() = "ECNamedCurveTable" and + this.(MethodCall).getCallee().getName() = "getParameterSpec" and + result = this.getArgument(0) + } + } +} + +/** + * Models for the signature algorithms defined by the `org.bouncycastle.crypto.signers` package. + */ +module Signers { + import FlowAnalysis + import AlgorithmInstances + + /** + * A model of the `Signer` class in Bouncy Castle. + * + * This class represents a BouncyCastle signer with a streaming API. For signers + * with a one-shot API, see `OneShotSigner` below. + */ + class Signer extends RefType { + Signer() { + this.getPackage().getName() = + ["org.bouncycastle.crypto.signers", "org.bouncycastle.pqc.crypto.lms"] and + this.getName().matches("%Signer") + } + + MethodCall getAnInitCall() { result = this.getAMethodCall("init") } + + MethodCall getAUseCall() { + result = this.getAMethodCall(["update", "generateSignature", "verifySignature"]) + } + + MethodCall getAMethodCall(string name) { + result.getCallee().hasQualifiedName(this.getPackage().getName(), this.getName(), name) + } + + // Overridden by subclasses to provide the message argument. + Expr getMessageArg(MethodCall call) { + call.getCallee().getName() = "update" and + result = call.getArgument(0) + } + + // Overridden by subclasses to provide the signature argument. + Expr getSignatureArg(MethodCall call) { + call.getCallee().getName() = "verifySignature" and + result = call.getArgument(0) + } + + // Overridden by subclasses to provide the signature output. + Expr getSignatureOutput(MethodCall call) { + call.getCallee().getName() = "generateSignature" and + result = call + } + } + + class OneShotSigner extends Signer { + OneShotSigner() { this.getName().matches(["ECDSA%", "LMS%"]) } + + override Expr getMessageArg(MethodCall call) { + // For ECDSA and LMS, the message is passed directly to `generateSignature()`. + call.getCallee().getName().matches(["generateSignature", "verifySignature"]) and + result = call.getArgument(0) + } + + override Expr getSignatureArg(MethodCall call) { + // For ECDSA, r and s are passed to `verifySignature()` as separate arguments. + call.getCallee().getName() = "verifySignature" and + result = call.getArgument([1, 2]) + } + } + + /** + * BouncyCastle algorithms are instantiated by calling the constructor of the + * corresponding class, which also represents the algorithm instance. + */ + private class SignerNewCall = SignatureAlgorithmInstance; + + /** + * The type is instantiated by a constructor call and initialized by a call to + * `init()` which takes two arguments. The first argument is a flag indicating + * whether the operation is signing data or verifying a signature, and the + * second is the key to use. + */ + private class SignerInitCall extends MethodCall { + SignerInitCall() { this = any(Signer signer).getAnInitCall() } + + Expr getForSigningArg() { result = this.getArgument(0) } + + Expr getKeyArg() { + this.getParametersArg().getType() instanceof Params::KeyParameters and + result = this.getParametersArg() + } + + // The second argument is used to provide parameters (like the key) to the signer. + Expr getParametersArg() { result = this.getArgument(1) } + + DataFlow::Node getParametersInput() { result.asExpr() = this.getParametersArg() } + + // TODO: Support dataflow for the operation sub-type. + Crypto::KeyOperationSubtype getKeyOperationSubtype() { + if this.isOperationSubTypeKnown() + then + this.getForSigningArg().(BooleanLiteral).getBooleanValue() = true and + result = Crypto::TSignMode() + or + this.getForSigningArg().(BooleanLiteral).getBooleanValue() = false and + result = Crypto::TVerifyMode() + else result = Crypto::TUnknownKeyOperationMode() + } + + predicate isOperationSubTypeKnown() { this.getForSigningArg() instanceof BooleanLiteral } + } + + /** + * The `update()` method is used to pass message data to the signer, and the + * `generateSignature()` or `verifySignature()` methods are used to produce or + * verify the signature, respectively. + */ + private class SignerUseCall extends MethodCall { + Signer signer; + + SignerUseCall() { this = signer.getAUseCall() } + + predicate isIntermediate() { this.getCallee().getName() = "update" } + + Expr getMessageInput() { result = signer.getMessageArg(this) } + + Expr getSignatureInput() { result = signer.getSignatureArg(this) } + + Expr getSignatureOutput() { result = signer.getSignatureOutput(this) } + } + + /** + * Instantiate the flow analysis module for the `Signer` class. + */ + private module SignerFlow = + NewToInitToUseFlowAnalysis; + + /** + * A signing operation instance is a call to either `update()`, `generateSignature()`, + * or `verifySignature()` on a `Signer` instance. + */ + class SignatureOperationInstance extends Crypto::SignatureOperationInstance instanceof SignerUseCall + { + SignatureOperationInstance() { not this.isIntermediate() } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result = SignerFlow::getNewFromUse(this, _, _) + } + + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + // This is less expensive and more robust than resolving the subtype using + // dataflow from the `forSigning` argument to `init()`. + if super.getMethod().getName() = "generateSignature" + then result = Crypto::TSignMode() + else + if super.getMethod().getName() = "verifySignature" + then result = Crypto::TVerifyMode() + else result = Crypto::TUnknownKeyOperationMode() + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + result.asExpr() = this.getInitCall().getKeyArg() + } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { none() } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + // Inputs to signers with streaming APIs + result.asExpr() = this.getAnUpdateCall().getMessageInput() + or + // Inputs to signers with one shot APIs + result.asExpr() = super.getMessageInput() + } + + override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { + result.asExpr() = super.getSignatureInput() + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + // Signature output + result.asExpr() = super.getSignatureOutput() + } + + SignerInitCall getInitCall() { result = SignerFlow::getInitFromUse(this, _, _) } + + SignerUseCall getAnUpdateCall() { + result = SignerFlow::getAnIntermediateUseFromFinalUse(this, _, _) + } + } +} + +/** + * Models for the key generation algorithms defined by the `org.bouncycastle.crypto.generators` package. + */ +module Generators { + import FlowAnalysis + import AlgorithmInstances + + /** + * A model of the `KeyGenerator` and `KeyPairGenerator` classes in Bouncy Castle. + */ + class KeyGenerator extends RefType { + Crypto::KeyArtifactType type; + + KeyGenerator() { + this.getPackage().getName() = + ["org.bouncycastle.crypto.generators", "org.bouncycastle.pqc.crypto.lms"] and + ( + this.getName().matches("%KeyGenerator") and type instanceof Crypto::TSymmetricKeyType + or + this.getName().matches("%KeyPairGenerator") and type instanceof Crypto::TAsymmetricKeyType + ) + } + + MethodCall getAnInitCall() { result = this.getAMethodCall("init") } + + MethodCall getAUseCall() { result = this.getAMethodCall(["generateKey", "generateKeyPair"]) } + + MethodCall getAMethodCall(string name) { + result.getCallee().hasQualifiedName(this.getPackage().getName(), this.getName(), name) + } + + Crypto::KeyArtifactType getKeyType() { result = type } + } + + /** + * This type is used to model data flow from a key pair to the private and + * public components of the key pair. + */ + class KeyPair extends RefType { + KeyPair() { + this.getPackage().getName() = "org.bouncycastle.crypto" and + this.getName() = "%KeyPair" // `AsymmetricCipherKeyPair` or `EphemeralKeyPair` + } + + MethodCall getPublicKeyCall() { result = this.getAMethodCall("getPublic") } + + MethodCall getPrivateKeyCall() { result = this.getAMethodCall("getPrivate") } + + MethodCall getAMethodCall(string name) { + result.getCallee().hasQualifiedName("org.bouncycastle.crypto", this.getName(), name) + } + } + + /** + * BouncyCastle algorithms are instantiated by calling the constructor of the + * corresponding class, which also represents the algorithm instance. + */ + private class KeyGeneratorNewCall = KeyGenerationAlgorithmInstance; + + /** + * The type is instantiated by a constructor call and initialized by a call to + * `init()` which takes a single `KeyGenerationParameters` argument. + */ + private class KeyGeneratorInitCall extends MethodCall { + KeyGenerator gen; + + KeyGeneratorInitCall() { this = gen.getAnInitCall() } + + Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + // The `KeyGenerationParameters` argument used to configure the key generator. + DataFlow::Node getParametersInput() { result.asExpr() = this.getArgument(0) } + } + + /** + * The `generateKey()` and `generateKeyPair()` methods are used to generate + * the resulting key, depending on the type of the generator. + */ + private class KeyGeneratorUseCall extends MethodCall { + KeyGenerator gen; + + KeyGeneratorUseCall() { this = gen.getAUseCall() } + + // Since key generators don't have `update()` methods, this is always false. + predicate isIntermediate() { none() } + + Crypto::KeyArtifactType getKeyType() { result = gen.getKeyType() } + + Expr getOutput() { result = this } + } + + private module KeyGeneratorFlow = + NewToInitToUseFlowAnalysis; + + private module ParametersFlow = + ParametersToInitFlowAnalysis; + + Params::ParametersInstantiation getParametersFromInit(KeyGeneratorInitCall init) { + result = ParametersFlow::getParametersFromInit(init, _, _) + } + + // TODO: Remove this. + Params::ParametersInstantiation getParametersFromUse(KeyGeneratorInitCall init) { + result = ParametersFlow::getParametersFromInit(init, _, _) + } + + /** + * A key generation operation instance is a call to `generateKey()` or + * `generateKeyPair()` on a key generator defined under + * `org.bouncycastle.crypto.generators`. + */ + class KeyGenerationOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGeneratorUseCall + { + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + // The algorithm is implicit in the key generator type + result = KeyGeneratorFlow::getNewFromUse(this, _, _) + } + + override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { + result.asExpr() = super.getOutput() + } + + override Crypto::KeyArtifactType getOutputKeyType() { result = super.getKeyType() } + + override int getKeySizeFixed() { + result = KeyGeneratorFlow::getNewFromUse(this, _, _).getKeySizeFixed() + } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { + result = KeyGeneratorFlow::getInitFromUse(this, _, _).getKeySizeConsumer() + } + + Params::ParametersInstantiation getParameters() { + exists(KeyGeneratorInitCall init | + init = KeyGeneratorFlow::getInitFromUse(this, _, _) and + result = ParametersFlow::getParametersFromInit(init, _, _) + ) + } + } +} + +module Modes { } diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index d92e62180bae..e969c0f480a1 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -182,6 +182,13 @@ module CryptographyBase Input> { */ abstract class AlgorithmInstance extends KnownElement { } + abstract class EllipticCurveConsumingAlgorithmInstance extends AlgorithmInstance { + /** + * Gets the elliptic curve used by this algorithm. + */ + abstract AlgorithmValueConsumer getEllipticCurveConsumer(); + } + /** * An element that represents a generic source of data. * @@ -612,6 +619,8 @@ module CryptographyBase Input> { ECDSA() or Ed25519() or Ed448() or + LMS() or + MLDSA() or OtherSignatureAlgorithmType() newtype TKEMAlgorithmType = @@ -896,6 +905,11 @@ module CryptographyBase Input> { * Holds if this algorithm is expected to have a padding scheme specified. */ predicate shouldHavePaddingScheme() { any() } + + /** + * Holds if this algorithm is expected to have an elliptic curve specified. + */ + predicate shouldHaveEllipticCurve() { any() } } newtype TBlockCipherModeOfOperationType = @@ -1423,6 +1437,13 @@ module CryptographyBase Input> { class AssetNode = NodeBase; + predicate isConsumedEllipticCurveNode(EllipticCurveNode node) { + exists(AlgorithmNode other | + other.asElement() instanceof EllipticCurveConsumingAlgorithmInstance and + other.asElement() = node.asElement() + ) + } + /** * A cryptographic operation, such as hashing or encryption. */ @@ -1448,7 +1469,8 @@ module CryptographyBase Input> { AlgorithmNode getAKnownAlgorithm() { result = this.asElement().(OperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() and - this.isCandidateAlgorithmNode(result) + this.isCandidateAlgorithmNode(result) and + not isConsumedEllipticCurveNode(result) } override NodeBase getChild(string edgeName) { @@ -1651,6 +1673,8 @@ module CryptographyBase Input> { */ private class KeyCreationCandidateAlgorithmNode extends TKeyCreationCandidateAlgorithm instanceof AlgorithmNode { + LocatableElement asElement() { result = super.asElement() } + string toString() { result = super.getAlgorithmName() } } @@ -1693,7 +1717,8 @@ module CryptographyBase Input> { KeyCreationCandidateAlgorithmNode getAKnownAlgorithm() { result = - instance.(KeyCreationOperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() + instance.(KeyCreationOperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() and + not isConsumedEllipticCurveNode(result) } override NodeBase getChild(string edgeName) { @@ -2392,6 +2417,26 @@ module CryptographyBase Input> { result.asElement() = instance.asAlg().getPaddingAlgorithm() } + /** + * Gets the elliptic curve used by this algorithm, if applicable. + */ + NodeBase getEllipticCurveOrGenericSource() { + result instanceof EllipticCurveNode and + result = + instance + .asAlg() + .(EllipticCurveConsumingAlgorithmInstance) + .getEllipticCurveConsumer() + .getAKnownSourceNode() + or + result = + instance + .asAlg() + .(EllipticCurveConsumingAlgorithmInstance) + .getEllipticCurveConsumer() + .getAGenericSourceNode() + } + override NodeBase getChild(string edgeName) { result = super.getChild(edgeName) or @@ -2412,6 +2457,15 @@ module CryptographyBase Input> { else result = this ) and instance.asAlg().shouldHavePaddingScheme() + or + // [KNOWN_OR_UNKNOWN] - but only if not suppressed + edgeName = "Curve" and + ( + if exists(this.getEllipticCurveOrGenericSource()) + then result = this.getEllipticCurveOrGenericSource() + else result = this + ) and + instance.asAlg() instanceof EllipticCurveConsumingAlgorithmInstance } override predicate properties(string key, string value, Location location) { From 0406d5c45d5907e49ed1e74be0aefe3dcf5d0345 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Tue, 3 Jun 2025 12:39:28 +0200 Subject: [PATCH 14/32] Added documentation for the isConsumedEllipticCurve workaround --- shared/quantum/codeql/quantum/experimental/Model.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index e969c0f480a1..42ae99064114 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -1437,6 +1437,15 @@ module CryptographyBase Input> { class AssetNode = NodeBase; + /** + * This predicate is used to filter out elliptic curve nodes in cases where + * the algorithm instance and the curve instance are represented by the same + * algorithm value consumer (e.g. in cases where both the algorithm and the + * curve are determined by the same instance). + * + * An alternative way to handle this would be to use separate instances to + * represent the elliptic curve and the algorithm. + */ predicate isConsumedEllipticCurveNode(EllipticCurveNode node) { exists(AlgorithmNode other | other.asElement() instanceof EllipticCurveConsumingAlgorithmInstance and From 8b06c3284e0a1d2b915e37d2007d06ef0a80585d Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Tue, 3 Jun 2025 13:00:52 +0200 Subject: [PATCH 15/32] Added support for EllipticCurveConsumingAlgorithm --- .../BouncyCastle/AlgorithmInstances.qll | 125 ++++++++++-------- .../quantum/BouncyCastle/FlowAnalysis.qll | 10 +- .../BouncyCastle/OperationInstances.qll | 30 +---- 3 files changed, 85 insertions(+), 80 deletions(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index a44e8cbd0b81..49c18d69f545 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -1,20 +1,9 @@ import java import experimental.quantum.Language import AlgorithmValueConsumers +import OperationInstances import FlowAnalysis -/** - * Elliptic curve algorithms. - */ -abstract private class EllipticCurveConsumingAlgorithmInstance extends Crypto::EllipticCurveConsumingAlgorithmInstance -{ - string getFixedEllipticCurveName() { none() } - - override Crypto::AlgorithmValueConsumer getEllipticCurveConsumer() { - result.(ImplicitEllipticCurveInstance).getAlgorithm() = this - } -} - /** * A string literal that represents an elliptic curve name. */ @@ -42,28 +31,12 @@ class EllipticCurveStringLiteralInstance extends Crypto::EllipticCurveInstance i } /** - * Represents an elliptic curve that is implicitly defined by the underlying - * algorithm. In this case, we view the algorithm and elliptic curve as being - * implicitly defined by the constructor call. + * Represents an elliptic curve algorithm where the elliptic curve is implicitly + * defined by the underlying type. */ -class ImplicitEllipticCurveInstance extends Crypto::EllipticCurveInstance, - EllipticCurveAlgorithmValueConsumer instanceof ClassInstanceExpr +abstract class KnownEllipticCurveInstance extends Crypto::EllipticCurveInstance, + Crypto::EllipticCurveConsumingAlgorithmInstance, Crypto::AlgorithmValueConsumer instanceof ClassInstanceExpr { - EllipticCurveConsumingAlgorithmInstance algorithm; - - ImplicitEllipticCurveInstance() { - this = algorithm and - exists(algorithm.getFixedEllipticCurveName()) - } - - EllipticCurveConsumingAlgorithmInstance getAlgorithm() { result = this } - - override string getRawEllipticCurveName() { result = algorithm.getFixedEllipticCurveName() } - - override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } - override Crypto::TEllipticCurveType getEllipticCurveType() { Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName().toUpperCase(), _, result) @@ -73,6 +46,8 @@ class ImplicitEllipticCurveInstance extends Crypto::EllipticCurveInstance, Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName().toUpperCase(), result, _) } + + override Crypto::AlgorithmValueConsumer getEllipticCurveConsumer() { result = this } } /** @@ -112,6 +87,18 @@ abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmI DataFlow::Node getEllipticCurveInput() { none() } } +/** + * Represents an elliptic curve signature algorithm where both the signature + * algorithm and elliptic curve are implicitly defined by the underlying type. + */ +abstract class KnownEllipticCurveSignatureAlgorithmInstance extends KnownEllipticCurveInstance, + SignatureAlgorithmInstance +{ + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } +} + /** * DSA and DSADigest signers. */ @@ -129,22 +116,24 @@ class DSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceo /** * Ed25519, Ed25519ph, and Ed25519ctx signers. */ -class Ed25519SignatureAlgorithmInstance extends SignatureAlgorithmInstance, - EllipticCurveConsumingAlgorithmInstance instanceof ClassInstanceExpr +class Ed25519SignatureAlgorithmInstance extends KnownEllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr { Ed25519SignatureAlgorithmInstance() { super.getConstructedType() instanceof Signers::Signer and super.getConstructedType().getName().matches("Ed25519%") } - override string getFixedEllipticCurveName() { result = "Curve25519" } + override string getRawAlgorithmName() { + typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + } + + override string getRawEllipticCurveName() { result = "Curve25519" } } /** * Ed448 and Ed448ph signers. */ -class Ed448SignatureAlgorithmInstance extends SignatureAlgorithmInstance, - EllipticCurveConsumingAlgorithmInstance instanceof ClassInstanceExpr +class Ed448SignatureAlgorithmInstance extends KnownEllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr { Ed448SignatureAlgorithmInstance() { super.getConstructedType() instanceof Signers::Signer and @@ -155,7 +144,7 @@ class Ed448SignatureAlgorithmInstance extends SignatureAlgorithmInstance, typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) } - override string getFixedEllipticCurveName() { result = "Curve448" } + override string getRawEllipticCurveName() { result = "Curve448" } } /** @@ -171,7 +160,7 @@ class Ed448SignatureAlgorithmInstance extends SignatureAlgorithmInstance, class ECDSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { ECDSASignatureAlgorithmInstance() { - super.getConstructedType() instanceof Signers::Signer and + super.getConstructedType() instanceof Signers::OneShotSigner and super.getConstructedType().getName().matches("ECDSA%") } @@ -186,6 +175,9 @@ class ECDSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanc override int getKeySizeFixed() { none() } } +/** + * LMS signers. + */ class LMSSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { LMSSignatureAlgorithmInstance() { super.getConstructedType() instanceof Signers::Signer and @@ -239,32 +231,44 @@ abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgori DataFlow::Node getEllipticCurveInput() { none() } } -class Ed25519KeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance, - EllipticCurveConsumingAlgorithmInstance instanceof ClassInstanceExpr +/** + * Represents an elliptic curve key generation algorithm where both the key + * generation algorithm and elliptic curve are implicitly defined by the + * underlying type. + */ +abstract class KnownEllipticCurveKeyGenerationAlgorithmInstance extends KnownEllipticCurveInstance, + KeyGenerationAlgorithmInstance +{ + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } +} + +class Ed25519KeyGenerationAlgorithmInstance extends KnownEllipticCurveKeyGenerationAlgorithmInstance instanceof ClassInstanceExpr { Ed25519KeyGenerationAlgorithmInstance() { super.getConstructedType() instanceof Generators::KeyGenerator and super.getConstructedType().getName().matches("Ed25519%") } - override string getFixedEllipticCurveName() { result = "Curve25519" } + override string getRawEllipticCurveName() { result = "Curve25519" } } -class Ed448KeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance, - EllipticCurveConsumingAlgorithmInstance instanceof ClassInstanceExpr +class Ed448KeyGenerationAlgorithmInstance extends KnownEllipticCurveKeyGenerationAlgorithmInstance instanceof ClassInstanceExpr { Ed448KeyGenerationAlgorithmInstance() { super.getConstructedType() instanceof Generators::KeyGenerator and super.getConstructedType().getName().matches("Ed448%") } - override string getFixedEllipticCurveName() { result = "Curve448" } + override string getRawEllipticCurveName() { result = "Curve448" } } /** * Represents a generic `ECKeyPairGenerator` instance. */ -class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance instanceof ClassInstanceExpr +class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance, + Crypto::EllipticCurveConsumingAlgorithmInstance instanceof ClassInstanceExpr { GenericEllipticCurveKeyGenerationAlgorithmInstance() { super.getConstructedType() instanceof Generators::KeyGenerator and @@ -272,17 +276,34 @@ class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAl } override string getRawAlgorithmName() { - // TODO: The generator constructs an elliptic curve key pair, but the - // algorithm is not determined at key generation. As an example, the key - // could be used for either ECDSA or ECDH. For this reason, we just return - // "EllipticCurve". - result = "EllipticCurve" + // TODO: The generator constructs an elliptic curve key pair. The curve used + // is determined using data flow. If this fails we would like to report + // something useful, so we use "UnknownCurve". However, this should probably + // be handled at the node layer. + if exists(this.getConsumedEllipticCurve()) + then result = this.getConsumedEllipticCurve().getRawEllipticCurveName() + else result = "UnknownCurve" } override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - // The algorithm type is not known. See above. + // TODO: There is currently to algorithm type for elliptic curve key + // generation. result = Crypto::KeyOpAlg::TUnknownKeyOperationAlgorithmType() } + + override Crypto::AlgorithmValueConsumer getEllipticCurveConsumer() { + // The elliptic curve is resolved recursively from the parameters passed to + // the `init()` call. + exists(MethodCall init | + init = Generators::KeyGeneratorFlow::getInitFromNew(this, _, _) and + result = + Generators::ParametersFlow::getParametersFromInit(init, _, _).getAnAlgorithmValueConsumer() + ) + } + + Crypto::EllipticCurveInstance getConsumedEllipticCurve() { + result = this.getEllipticCurveConsumer().getAKnownAlgorithmSource() + } } /** diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll index 1d853b6fc77c..9e2092119b9e 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll @@ -161,6 +161,12 @@ module NewToInitToUseFlowAnalysis { - module ParametersToInitConfig implements DataFlow::ConfigSig { + private module ParametersToInitConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source.asExpr() instanceof New } predicate isSink(DataFlow::Node sink) { exists(Init init | sink = init.getParametersInput()) } @@ -261,7 +267,7 @@ module ParametersToInitFlowAnalysis { } } - module ParametersToInitFlow = DataFlow::Global; + private module ParametersToInitFlow = DataFlow::Global; New getParametersFromInit( Init init, ParametersToInitFlow::PathNode src, ParametersToInitFlow::PathNode sink diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll index c8b975c7f3c0..471a5e940fe8 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll @@ -173,6 +173,7 @@ module Signers { override Expr getSignatureArg(MethodCall call) { // For ECDSA, r and s are passed to `verifySignature()` as separate arguments. + // For LMS, the signature is passed as a single argument in position 1. call.getCallee().getName() = "verifySignature" and result = call.getArgument([1, 2]) } @@ -204,20 +205,6 @@ module Signers { Expr getParametersArg() { result = this.getArgument(1) } DataFlow::Node getParametersInput() { result.asExpr() = this.getParametersArg() } - - // TODO: Support dataflow for the operation sub-type. - Crypto::KeyOperationSubtype getKeyOperationSubtype() { - if this.isOperationSubTypeKnown() - then - this.getForSigningArg().(BooleanLiteral).getBooleanValue() = true and - result = Crypto::TSignMode() - or - this.getForSigningArg().(BooleanLiteral).getBooleanValue() = false and - result = Crypto::TVerifyMode() - else result = Crypto::TUnknownKeyOperationMode() - } - - predicate isOperationSubTypeKnown() { this.getForSigningArg() instanceof BooleanLiteral } } /** @@ -390,21 +377,12 @@ module Generators { Expr getOutput() { result = this } } - private module KeyGeneratorFlow = + module KeyGeneratorFlow = NewToInitToUseFlowAnalysis; - private module ParametersFlow = + module ParametersFlow = ParametersToInitFlowAnalysis; - Params::ParametersInstantiation getParametersFromInit(KeyGeneratorInitCall init) { - result = ParametersFlow::getParametersFromInit(init, _, _) - } - - // TODO: Remove this. - Params::ParametersInstantiation getParametersFromUse(KeyGeneratorInitCall init) { - result = ParametersFlow::getParametersFromInit(init, _, _) - } - /** * A key generation operation instance is a call to `generateKey()` or * `generateKeyPair()` on a key generator defined under @@ -413,7 +391,7 @@ module Generators { class KeyGenerationOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGeneratorUseCall { override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - // The algorithm is implicit in the key generator type + // The algorithm is implicitly defined by the key generator type result = KeyGeneratorFlow::getNewFromUse(this, _, _) } From 653ef24f74957ae7b2650b1976d7d5a9ae550412 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Tue, 3 Jun 2025 13:01:24 +0200 Subject: [PATCH 16/32] Added LMS and ML-DSA names to Model.qll --- shared/quantum/codeql/quantum/experimental/Model.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index 42ae99064114..f2ef9495a66b 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -729,6 +729,10 @@ module CryptographyBase Input> { or type = TSignature(Ed448()) and name = "Ed448" or + type = TSignature(LMS()) and name = "LMS" + or + type = TSignature(MLDSA()) and name = "MLDSA" + or type = TSignature(OtherSignatureAlgorithmType()) and name = "UnknownSignature" or // Key Encapsulation Mechanisms From eb89ee1a36675455837cdd8de241dfccf20c14c0 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Tue, 3 Jun 2025 13:17:46 +0200 Subject: [PATCH 17/32] Updated BouncyCastle tests and corresponding stubs --- .../BouncyCastle/ECDSAP256SignAndVerify.java | 128 +++++++++++++----- .../quantum/BouncyCastle/LMSSignature.java | 58 ++++++++ .../BouncyCastle/key_artifacts.expected | 8 +- .../key_generation_operations.expected | 8 +- .../signature_operations.expected | 7 +- .../bouncycastle/jce/ECNamedCurveTable.java | 2 +- .../pqc/crypto/lms/LMOtsParameters.java | 10 ++ .../lms/LMSKeyGenerationParameters.java | 21 +++ .../pqc/crypto/lms/LMSKeyPairGenerator.java | 21 +++ .../pqc/crypto/lms/LMSParameters.java | 19 +++ .../crypto/lms/LMSPrivateKeyParameters.java | 13 ++ .../crypto/lms/LMSPublicKeyParameters.java | 13 ++ .../pqc/crypto/lms/LMSSigner.java | 19 +++ .../pqc/crypto/lms/LMSigParameters.java | 10 ++ 14 files changed, 293 insertions(+), 44 deletions(-) create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/LMSSignature.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMOtsParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSKeyGenerationParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSKeyPairGenerator.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSPrivateKeyParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSPublicKeyParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSSigner.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSigParameters.java diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/ECDSAP256SignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/ECDSAP256SignAndVerify.java index c3bfdcb524e0..1f66d708c3b7 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/ECDSAP256SignAndVerify.java +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/ECDSAP256SignAndVerify.java @@ -1,5 +1,7 @@ import java.security.Security; import java.security.SecureRandom; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.params.ECDomainParameters; @@ -8,54 +10,114 @@ import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.signers.ECDSASigner; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; +import org.bouncycastle.jce.spec.ECNamedCurveSpec; +import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; /** - * Example of using Bouncy Castle's low-level API for ECDSA signing and verification over P-256. + * Test Bouncy Castle's low-level ECDSA API */ public class ECDSAP256SignAndVerify { + public static void main(String[] args) { // Add Bouncy Castle provider Security.addProvider(new BouncyCastleProvider()); try { - // Get P-256 curve parameters using BouncyCastle's SECNamedCurves - String curveName = "secp256r1"; - X9ECParameters ecParams = SECNamedCurves.getByName(curveName); - ECDomainParameters domainParams = new ECDomainParameters(ecParams); - - // Generate a key pair - SecureRandom random = new SecureRandom(); - ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); - ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, random); - keyPairGenerator.init(keyGenParams); - AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair(); - - ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate(); - ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic(); - byte[] message = "Hello, ECDSA P-256 signature!".getBytes("UTF-8"); - // Sign the message - ECDSASigner signer = new ECDSASigner(); - signer.init(true, privateKey); // true for signing - // Note: ECDSA typically signs a hash of the message, not the message directly - // For simplicity, we're signing the message bytes directly here - java.math.BigInteger[] signature = signer.generateSignature(message); - - System.out.println("Signature generated!"); - System.out.println("Signature r: " + signature[0].toString(16)); - System.out.println("Signature s: " + signature[1].toString(16)); - - // Verify the signature - ECDSASigner verifier = new ECDSASigner(); - verifier.init(false, publicKey); // false for verification - boolean verified = verifier.verifySignature(message, signature[0], signature[1]); - - System.out.println("Signature verified: " + verified); + // Test different key generation methods + signWithKeyPair(generateKeyPair1(), message); + signWithKeyPair(generateKeyPair2(), message); + signWithKeyPair(generateKeyPair3(), message); } catch (Exception e) { e.printStackTrace(); } } + + /** + * Method 1: Generate key pair with SECNamedCurves + */ + private static AsymmetricCipherKeyPair generateKeyPair1() throws Exception { + // Get P-256 curve parameters using BouncyCastle's SECNamedCurves + String curveName = "secp256r1"; + X9ECParameters ecParams = SECNamedCurves.getByName(curveName); + ECDomainParameters domainParams = new ECDomainParameters(ecParams); + + // Generate a key pair + SecureRandom random = new SecureRandom(); + ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, random); + keyPairGenerator.init(keyGenParams); + + return keyPairGenerator.generateKeyPair(); + } + + /** + * Method 2: Generate key pair with explicit curve construction + */ + private static AsymmetricCipherKeyPair generateKeyPair2() throws Exception { + // Get the X9.62 parameters and construct domain parameters explicitly + String curveName = "secp256k1"; + X9ECParameters x9Params = SECNamedCurves.getByName(curveName); + ECCurve curve = x9Params.getCurve(); + ECPoint g = x9Params.getG(); + java.math.BigInteger n = x9Params.getN(); + java.math.BigInteger h = x9Params.getH(); + + // Create domain parameters with explicit values + ECDomainParameters domainParams = new ECDomainParameters(curve, g, n, h); + + SecureRandom random = new SecureRandom(); + ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, random); + keyPairGenerator.init(keyGenParams); + + return keyPairGenerator.generateKeyPair(); + } + + + /** + * Method 3: Generate key pair using ECNamedCurveTable + */ + private static AsymmetricCipherKeyPair generateKeyPair3() throws Exception { + // Get curve parameters using ECNamedCurveTable + String curveName = "secp384r1"; + ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(curveName); + ECDomainParameters domainParams = new ECDomainParameters( + ecSpec.getCurve(), + ecSpec.getG(), + ecSpec.getN(), + ecSpec.getH() + ); + + SecureRandom random = new SecureRandom(); + ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, random); + keyPairGenerator.init(keyGenParams); + + return keyPairGenerator.generateKeyPair(); + } + + /** + * Test signing and verification with BouncyCastle low-level key pair + */ + private static void signWithKeyPair(AsymmetricCipherKeyPair keyPair, byte[] message) throws Exception { + ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate(); + ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic(); + + // Sign the message + ECDSASigner signer = new ECDSASigner(); + signer.init(true, privateKey); // true for signing + java.math.BigInteger[] signature = signer.generateSignature(message); + + // Verify the signature + ECDSASigner verifier = new ECDSASigner(); + verifier.init(false, publicKey); // false for verification + boolean verified = verifier.verifySignature(message, signature[0], signature[1]); + } } \ No newline at end of file diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/LMSSignature.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/LMSSignature.java new file mode 100644 index 000000000000..83312cd73dab --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/LMSSignature.java @@ -0,0 +1,58 @@ +import java.security.SecureRandom; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.pqc.crypto.lms.LMSKeyPairGenerator; +import org.bouncycastle.pqc.crypto.lms.LMSKeyGenerationParameters; +import org.bouncycastle.pqc.crypto.lms.LMSParameters; +import org.bouncycastle.pqc.crypto.lms.LMSPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.lms.LMSPublicKeyParameters; +import org.bouncycastle.pqc.crypto.lms.LMSSigner; +import org.bouncycastle.pqc.crypto.lms.LMSigParameters; +import org.bouncycastle.pqc.crypto.lms.LMOtsParameters; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import java.security.Security; + +/** + * Example of the Leighton-Micali Signature (LMS) scheme using Bouncy Castle's + * low-level API. + * + */ +public class LMSSignature { + public static void main(String[] args) { + Security.addProvider(new BouncyCastleProvider()); + + try { + // Set up LMS parameters + LMSParameters lmsParameters = new LMSParameters( + LMSigParameters.lms_sha256_n32_h10, + LMOtsParameters.sha256_n32_w8); + + // Generate key pair + SecureRandom random = new SecureRandom(); + LMSKeyPairGenerator keyPairGen = new LMSKeyPairGenerator(); + keyPairGen.init(new LMSKeyGenerationParameters(lmsParameters, random)); + AsymmetricCipherKeyPair keyPair = keyPairGen.generateKeyPair(); + + LMSPrivateKeyParameters privateKey = (LMSPrivateKeyParameters) keyPair.getPrivate(); + LMSPublicKeyParameters publicKey = (LMSPublicKeyParameters) keyPair.getPublic(); + + byte[] message = "Hello, LMS signature!".getBytes("UTF-8"); + + LMSSigner signer = new LMSSigner(); + + // Sign the message + signer.init(true, privateKey); // true for signing + byte[] signature = signer.generateSignature(message); + + // Verify the signature + // + // TODO: Using the same signer instance for verification causes both + // keys to be reported. This should be handled using dataflow. + signer.init(false, publicKey); + boolean verified = signer.verifySignature(message, signature); + + System.out.println("Signature verified: " + verified); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected index ccc86f795474..8e769550c832 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected @@ -1,6 +1,6 @@ -| ECDSAP256SignAndVerify.java:33:47:33:80 | Key | Unknown | -| ECDSAP256SignAndVerify.java:33:47:33:80 | Key | secp256r1 | -| Ed448SignAndVerify.java:21:47:21:80 | Key | Curve25519 | +| ECDSAP256SignAndVerify.java:57:16:57:49 | Key | Unknown | +| ECDSAP256SignAndVerify.java:80:16:80:49 | Key | Unknown | +| ECDSAP256SignAndVerify.java:103:16:103:49 | Key | Unknown | | Ed448SignAndVerify.java:21:47:21:80 | Key | Ed448 | -| Ed25519SignAndVerify.java:21:47:21:80 | Key | Curve25519 | | Ed25519SignAndVerify.java:21:47:21:80 | Key | Ed25519 | +| LMSSignature.java:33:47:33:74 | Key | LMS | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected index 0f1df1598069..70b5996faabc 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected @@ -1,6 +1,6 @@ -| ECDSAP256SignAndVerify.java:33:47:33:80 | KeyGeneration | ECDSAP256SignAndVerify.java:24:32:24:42 | EllipticCurve | ECDSAP256SignAndVerify.java:33:47:33:80 | Key | -| ECDSAP256SignAndVerify.java:33:47:33:80 | KeyGeneration | ECDSAP256SignAndVerify.java:30:51:30:74 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:33:47:33:80 | Key | -| Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed448SignAndVerify.java:19:54:19:80 | EllipticCurve | Ed448SignAndVerify.java:21:47:21:80 | Key | +| ECDSAP256SignAndVerify.java:57:16:57:49 | KeyGeneration | ECDSAP256SignAndVerify.java:53:47:53:70 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:57:16:57:49 | Key | +| ECDSAP256SignAndVerify.java:80:16:80:49 | KeyGeneration | ECDSAP256SignAndVerify.java:76:47:76:70 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:80:16:80:49 | Key | +| ECDSAP256SignAndVerify.java:103:16:103:49 | KeyGeneration | ECDSAP256SignAndVerify.java:99:47:99:70 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:103:16:103:49 | Key | | Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | Ed448SignAndVerify.java:21:47:21:80 | Key | -| Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed25519SignAndVerify.java:19:56:19:84 | EllipticCurve | Ed25519SignAndVerify.java:21:47:21:80 | Key | | Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:21:47:21:80 | Key | +| LMSSignature.java:33:47:33:74 | KeyGeneration | LMSSignature.java:31:46:31:70 | KeyOperationAlgorithm | LMSSignature.java:33:47:33:74 | Key | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected index 56895dde92f2..e52b43d489f2 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected @@ -1,6 +1,9 @@ -| ECDSAP256SignAndVerify.java:45:48:45:80 | SignOperation | ECDSAP256SignAndVerify.java:41:34:41:50 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:42:31:42:40 | Key | ECDSAP256SignAndVerify.java:45:73:45:79 | Message | | SignatureOutput | -| ECDSAP256SignAndVerify.java:54:32:54:92 | VerifyOperation | ECDSAP256SignAndVerify.java:52:36:52:52 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:53:34:53:42 | Key | ECDSAP256SignAndVerify.java:54:57:54:63 | Message | SignatureInput | | +| ECDSAP256SignAndVerify.java:116:44:116:76 | SignOperation | ECDSAP256SignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:115:27:115:36 | Key | ECDSAP256SignAndVerify.java:116:69:116:75 | Message | | SignatureOutput | +| ECDSAP256SignAndVerify.java:121:28:121:88 | VerifyOperation | ECDSAP256SignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:120:30:120:38 | Key | ECDSAP256SignAndVerify.java:121:53:121:59 | Message | SignatureInput | | | Ed448SignAndVerify.java:32:32:32:57 | SignOperation | Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | Ed448SignAndVerify.java:30:31:30:40 | Key | Ed448SignAndVerify.java:31:27:31:33 | Message | | SignatureOutput | | Ed448SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | Ed448SignAndVerify.java:38:34:38:42 | Key | Ed448SignAndVerify.java:39:29:39:35 | Message | SignatureInput | | | Ed25519SignAndVerify.java:32:32:32:57 | SignOperation | Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:30:31:30:40 | Key | Ed25519SignAndVerify.java:31:27:31:33 | Message | | SignatureOutput | | Ed25519SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:38:34:38:42 | Key | Ed25519SignAndVerify.java:39:29:39:35 | Message | SignatureInput | | +| LMSSignature.java:44:32:44:64 | SignOperation | LMSSignature.java:40:32:40:46 | KeyOperationAlgorithm | LMSSignature.java:43:31:43:40 | Key | LMSSignature.java:44:57:44:63 | Message | | SignatureOutput | +| LMSSignature.java:51:32:51:73 | VerifyOperation | LMSSignature.java:40:32:40:46 | KeyOperationAlgorithm | LMSSignature.java:43:31:43:40 | Key | LMSSignature.java:51:55:51:61 | Message | SignatureInput | | +| LMSSignature.java:51:32:51:73 | VerifyOperation | LMSSignature.java:40:32:40:46 | KeyOperationAlgorithm | LMSSignature.java:50:32:50:40 | Key | LMSSignature.java:51:55:51:61 | Message | SignatureInput | | diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java index 527015011822..f6fbad27e74e 100644 --- a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java @@ -8,6 +8,6 @@ public static ECNamedCurveParameterSpec getParameterSpec(String name) { } public static String[] getNames() { - return new String[] { "secp256r1", "secp384r1", "secp521r1" }; + return new String[] { "secp256r1", "secp256k1", "secp384r1" }; } } diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMOtsParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMOtsParameters.java new file mode 100644 index 000000000000..f05b8d8ce364 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMOtsParameters.java @@ -0,0 +1,10 @@ +package org.bouncycastle.pqc.crypto.lms; + +public class LMOtsParameters { + public static final LMOtsParameters sha256_n32_w1 = new LMOtsParameters(); + public static final LMOtsParameters sha256_n32_w2 = new LMOtsParameters(); + public static final LMOtsParameters sha256_n32_w4 = new LMOtsParameters(); + public static final LMOtsParameters sha256_n32_w8 = new LMOtsParameters(); + + private LMOtsParameters() { } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSKeyGenerationParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSKeyGenerationParameters.java new file mode 100644 index 000000000000..81491fe928aa --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSKeyGenerationParameters.java @@ -0,0 +1,21 @@ +package org.bouncycastle.pqc.crypto.lms; + +import java.security.SecureRandom; + +public class LMSKeyGenerationParameters { + private final LMSParameters lmsParameters; + private final SecureRandom random; + + public LMSKeyGenerationParameters(LMSParameters lmsParameters, SecureRandom random) { + this.lmsParameters = lmsParameters; + this.random = random; + } + + public LMSParameters getLMSParameters() { + return lmsParameters; + } + + public SecureRandom getRandom() { + return random; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSKeyPairGenerator.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSKeyPairGenerator.java new file mode 100644 index 000000000000..92ada895d472 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSKeyPairGenerator.java @@ -0,0 +1,21 @@ +package org.bouncycastle.pqc.crypto.lms; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; + +public class LMSKeyPairGenerator { + private LMSKeyGenerationParameters parameters; + + public void init(LMSKeyGenerationParameters parameters) { + this.parameters = parameters; + } + + public AsymmetricCipherKeyPair generateKeyPair() { + byte[] privateKeyData = new byte[32]; + byte[] publicKeyData = new byte[32]; + + LMSPrivateKeyParameters privateKey = new LMSPrivateKeyParameters(privateKeyData); + LMSPublicKeyParameters publicKey = new LMSPublicKeyParameters(publicKeyData); + + return new AsymmetricCipherKeyPair(publicKey, privateKey); + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSParameters.java new file mode 100644 index 000000000000..6445214dfa84 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSParameters.java @@ -0,0 +1,19 @@ +package org.bouncycastle.pqc.crypto.lms; + +public class LMSParameters { + private final LMSigParameters lmSigParameters; + private final LMOtsParameters lmOtsParameters; + + public LMSParameters(LMSigParameters lmSigParameters, LMOtsParameters lmOtsParameters) { + this.lmSigParameters = lmSigParameters; + this.lmOtsParameters = lmOtsParameters; + } + + public LMSigParameters getLMSigParameters() { + return lmSigParameters; + } + + public LMOtsParameters getLMOtsParameters() { + return lmOtsParameters; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSPrivateKeyParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSPrivateKeyParameters.java new file mode 100644 index 000000000000..39b02857240e --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSPrivateKeyParameters.java @@ -0,0 +1,13 @@ +package org.bouncycastle.pqc.crypto.lms; + +public class LMSPrivateKeyParameters { + private final byte[] keyData; + + public LMSPrivateKeyParameters(byte[] keyData) { + this.keyData = keyData.clone(); + } + + public byte[] getEncoded() { + return keyData.clone(); + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSPublicKeyParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSPublicKeyParameters.java new file mode 100644 index 000000000000..aeaba1cfb4dc --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSPublicKeyParameters.java @@ -0,0 +1,13 @@ +package org.bouncycastle.pqc.crypto.lms; + +public class LMSPublicKeyParameters { + private final byte[] keyData; + + public LMSPublicKeyParameters(byte[] keyData) { + this.keyData = keyData.clone(); + } + + public byte[] getEncoded() { + return keyData.clone(); + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSSigner.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSSigner.java new file mode 100644 index 000000000000..fbdb81ca4fca --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSSigner.java @@ -0,0 +1,19 @@ +package org.bouncycastle.pqc.crypto.lms; + +public class LMSSigner { + private boolean forSigning; + private Object keyParameter; + + public void init(boolean forSigning, Object keyParameter) { + this.forSigning = forSigning; + this.keyParameter = keyParameter; + } + + public byte[] generateSignature(byte[] message) { + return new byte[64]; + } + + public boolean verifySignature(byte[] message, byte[] signature) { + return true; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSigParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSigParameters.java new file mode 100644 index 000000000000..b612b077e984 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/pqc/crypto/lms/LMSigParameters.java @@ -0,0 +1,10 @@ +package org.bouncycastle.pqc.crypto.lms; + +public class LMSigParameters { + public static final LMSigParameters lms_sha256_n32_h10 = new LMSigParameters(); + public static final LMSigParameters lms_sha256_n32_h15 = new LMSigParameters(); + public static final LMSigParameters lms_sha256_n32_h20 = new LMSigParameters(); + public static final LMSigParameters lms_sha256_n32_h25 = new LMSigParameters(); + + private LMSigParameters() { } +} From b64fdc04184e54d213058fae7364e7fef7c58ab7 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Tue, 3 Jun 2025 13:23:57 +0200 Subject: [PATCH 18/32] Added BouncyCastle license file to stubs --- java/ql/test/stubs/bcprov-lts8on-2.73.7/LICENSE.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/LICENSE.md diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/LICENSE.md b/java/ql/test/stubs/bcprov-lts8on-2.73.7/LICENSE.md new file mode 100644 index 000000000000..d12f5623b563 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/LICENSE.md @@ -0,0 +1,3 @@ +Copyright (c) 2000-2024 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org). Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sub license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 3840e934450dfe06f0179699d8fb61fcd5858111 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Tue, 3 Jun 2025 15:05:17 +0200 Subject: [PATCH 19/32] Added support for HSS --- .../BouncyCastle/AlgorithmInstances.qll | 46 +++++++++++-------- .../BouncyCastle/OperationInstances.qll | 6 ++- .../codeql/quantum/experimental/Model.qll | 1 + 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index 49c18d69f545..17820b249e65 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -176,12 +176,13 @@ class ECDSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanc } /** - * LMS signers. + * An LMS or HSS stateful hash-based signer. */ -class LMSSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { - LMSSignatureAlgorithmInstance() { +class StatefulSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr +{ + StatefulSignatureAlgorithmInstance() { super.getConstructedType() instanceof Signers::Signer and - super.getConstructedType().getName().matches("LMS%") + super.getConstructedType().getName().matches(["LMS%", "HSS%"]) } override string getRawAlgorithmName() { @@ -189,7 +190,11 @@ class LMSSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceo } override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + super.getConstructedType().getName().matches("LMS%") and result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::LMS()) + or + super.getConstructedType().getName().matches("HSS%") and + result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::HSS()) } } @@ -307,17 +312,14 @@ class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAl } /** - * Represents LMS key generation instances. The algorithm is implicitly defined - * by the type. - * - * TODO: Determine how to represent LMS parameters, such as the hash function - * and the tree height. + * Represents LMS or HSS key generation instances. The algorithm is implicitly + * defined by the type. */ -class LMSKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance instanceof ClassInstanceExpr +class StatefulSignatureKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance instanceof ClassInstanceExpr { - LMSKeyGenerationAlgorithmInstance() { + StatefulSignatureKeyGenerationAlgorithmInstance() { super.getConstructedType() instanceof Generators::KeyGenerator and - super.getConstructedType().getName().matches("LMS%") + super.getConstructedType().getName().matches(["LMS%", "HSS%"]) } override string getRawAlgorithmName() { @@ -325,7 +327,11 @@ class LMSKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance i } override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + super.getConstructedType().getName().matches("LMS%") and result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::LMS()) + or + super.getConstructedType().getName().matches("HSS%") and + result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::HSS()) } } @@ -336,11 +342,11 @@ bindingset[typeName] private predicate typeNameToRawAlgorithmName(string typeName, string algorithmName) { // Ed25519, Ed25519ph, and Ed25519ctx key generators and signers typeName.matches("Ed25519%") and - algorithmName = "ED25519" + algorithmName = "Ed25519" or // Ed448 and Ed448ph key generators and signers typeName.matches("Ed448%") and - algorithmName = "ED448" + algorithmName = "Ed448" or // ECDSA typeName.matches("ECDSA%") and @@ -349,16 +355,20 @@ private predicate typeNameToRawAlgorithmName(string typeName, string algorithmNa // LMS typeName.matches("LMS%") and algorithmName = "LMS" + or + // HSS + typeName.matches("HSS%") and + algorithmName = "HSS" } private predicate signatureNameToKeySizeAndAlgorithmMapping( string name, int keySize, Crypto::KeyOpAlg::Algorithm algorithm ) { - name = "ED25519" and + name = "Ed25519" and keySize = 256 and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) or - name = "ED448" and + name = "Ed448" and keySize = 448 and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) } @@ -366,11 +376,11 @@ private predicate signatureNameToKeySizeAndAlgorithmMapping( private predicate generatorNameToKeySizeAndAlgorithmMapping( string name, int keySize, Crypto::KeyOpAlg::Algorithm algorithm ) { - name = "ED25519" and + name = "Ed25519" and keySize = 256 and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) or - name = "ED448" and + name = "Ed448" and keySize = 448 and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) } diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll index 471a5e940fe8..9e409f0a58e2 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll @@ -162,8 +162,12 @@ module Signers { } } + /** + * This class represents signers with a one shot API (where the entire message + * is passed to either `generateSignature()` or `verifySignature`.). + */ class OneShotSigner extends Signer { - OneShotSigner() { this.getName().matches(["ECDSA%", "LMS%"]) } + OneShotSigner() { this.getName().matches(["ECDSA%", "LMS%", "HSS%"]) } override Expr getMessageArg(MethodCall call) { // For ECDSA and LMS, the message is passed directly to `generateSignature()`. diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index f2ef9495a66b..e72c76c6c26c 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -620,6 +620,7 @@ module CryptographyBase Input> { Ed25519() or Ed448() or LMS() or + HSS() or MLDSA() or OtherSignatureAlgorithmType() From 1a445b2c7ec4cf2216cfdb37cad1d69a0aeb5a85 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Tue, 3 Jun 2025 16:01:43 +0200 Subject: [PATCH 20/32] Fixed merge conflict resolution issues --- .../codeql/quantum/experimental/Model.qll | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index e72c76c6c26c..1159c77df88a 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -442,17 +442,6 @@ module CryptographyBase Input> { final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } } - final private class SignatureArtifactConsumer extends ArtifactConsumerAndInstance { - ConsumerInputDataFlowNode inputNode; - - SignatureArtifactConsumer() { - exists(SignatureOperationInstance op | inputNode = op.getSignatureConsumer()) and - this = Input::dfn_to_element(inputNode) - } - - final override ConsumerInputDataFlowNode getInputNode() { result = inputNode } - } - /** * An artifact that is produced by an operation, representing a concrete artifact instance rather than a synthetic consumer artifact. */ @@ -489,8 +478,6 @@ module CryptographyBase Input> { override DataFlowNode getOutputNode() { result = creator.getOutputArtifact() } KeyOperationInstance getCreator() { result = creator } - - KeyOperationInstance getCreator() { result = creator } } /** @@ -1597,20 +1584,6 @@ module CryptographyBase Input> { override LocatableElement asElement() { result = instance } } - /** - * A signature input. This may represent a signature, or a signature component - * such as the scalar values r and s in ECDSA. - */ - final class SignatureArtifactNode extends ArtifactNode, TSignatureInput { - SignatureArtifactConsumer instance; - - SignatureArtifactNode() { this = TSignatureInput(instance) } - - final override string getInternalType() { result = "SignatureInput" } - - override LocatableElement asElement() { result = instance } - } - /** * A salt input. */ From 99b4cb1b7da017246de65eb164b2f4288bb2fc0e Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Wed, 4 Jun 2025 10:14:00 +0200 Subject: [PATCH 21/32] Fixed QL for QL findings --- .../lib/experimental/quantum/BouncyCastle.qll | 2 - .../BouncyCastle/AlgorithmInstances.qll | 39 +++++---- .../BouncyCastle/AlgorithmValueConsumers.qll | 13 +-- .../quantum/BouncyCastle/FlowAnalysis.qll | 38 ++++----- .../BouncyCastle/OperationInstances.qll | 81 +++++++++++++++++-- .../codeql/quantum/experimental/Model.qll | 3 + 6 files changed, 123 insertions(+), 53 deletions(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll index 774f82f91c92..d10b0ffde2e7 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -1,4 +1,2 @@ import java -import BouncyCastle.AlgorithmInstances -import BouncyCastle.AlgorithmValueConsumers import BouncyCastle.OperationInstances diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index 17820b249e65..ec2ec4ba204f 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -1,6 +1,4 @@ import java -import experimental.quantum.Language -import AlgorithmValueConsumers import OperationInstances import FlowAnalysis @@ -31,8 +29,8 @@ class EllipticCurveStringLiteralInstance extends Crypto::EllipticCurveInstance i } /** - * Represents an elliptic curve algorithm where the elliptic curve is implicitly - * defined by the underlying type. + * An elliptic curve algorithm where the elliptic curve is implicitly defined by + * the underlying type. */ abstract class KnownEllipticCurveInstance extends Crypto::EllipticCurveInstance, Crypto::EllipticCurveConsumingAlgorithmInstance, Crypto::AlgorithmValueConsumer instanceof ClassInstanceExpr @@ -51,7 +49,7 @@ abstract class KnownEllipticCurveInstance extends Crypto::EllipticCurveInstance, } /** - * Signature algorithms where the algorithm is implicitly defined by the type. + * A signature algorithm where the algorithm is implicitly defined by the type. */ abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr @@ -100,10 +98,10 @@ abstract class KnownEllipticCurveSignatureAlgorithmInstance extends KnownEllipti } /** - * DSA and DSADigest signers. + * A DSA or DSADigest signer. */ -class DSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { - DSASignatureAlgorithmInstance() { +class DsaSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { + DsaSignatureAlgorithmInstance() { super.getConstructedType() instanceof Signers::Signer and super.getConstructedType().getName().matches("DSA%") } @@ -114,7 +112,7 @@ class DSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceo } /** - * Ed25519, Ed25519ph, and Ed25519ctx signers. + * An Ed25519, Ed25519ph, or Ed25519ctx signer. */ class Ed25519SignatureAlgorithmInstance extends KnownEllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr { @@ -131,7 +129,7 @@ class Ed25519SignatureAlgorithmInstance extends KnownEllipticCurveSignatureAlgor } /** - * Ed448 and Ed448ph signers. + * An Ed448 or Ed448ph signer. */ class Ed448SignatureAlgorithmInstance extends KnownEllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr { @@ -148,7 +146,7 @@ class Ed448SignatureAlgorithmInstance extends KnownEllipticCurveSignatureAlgorit } /** - * ECDSA signers. + * An ECDSA signer. * * ECDSA curve parameters can be set in at least five ways: * - By using the `ECDomainParameters` class, which is passed to the constructor of the signer. @@ -157,9 +155,9 @@ class Ed448SignatureAlgorithmInstance extends KnownEllipticCurveSignatureAlgorit * - By using the `ECNamedCurveSpec` class, which is passed to the constructor of the signer. * - By using the `ECParameterSpec` class, which is passed to the constructor of the signer. */ -class ECDSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr +class EcdsaSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { - ECDSASignatureAlgorithmInstance() { + EcdsaSignatureAlgorithmInstance() { super.getConstructedType() instanceof Signers::OneShotSigner and super.getConstructedType().getName().matches("ECDSA%") } @@ -176,7 +174,7 @@ class ECDSASignatureAlgorithmInstance extends SignatureAlgorithmInstance instanc } /** - * An LMS or HSS stateful hash-based signer. + * An LMS or HSS stateful, hash-based signer. */ class StatefulSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { @@ -199,7 +197,7 @@ class StatefulSignatureAlgorithmInstance extends SignatureAlgorithmInstance inst } /** - * Key generation algorithms where the algorithm is implicitly defined by the + * A key generation algorithm where the algorithm is implicitly defined by the * type. */ abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, @@ -237,9 +235,8 @@ abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgori } /** - * Represents an elliptic curve key generation algorithm where both the key - * generation algorithm and elliptic curve are implicitly defined by the - * underlying type. + * An elliptic curve key generation algorithm where both the key generation + * algorithm and elliptic curve are implicitly defined by the underlying type. */ abstract class KnownEllipticCurveKeyGenerationAlgorithmInstance extends KnownEllipticCurveInstance, KeyGenerationAlgorithmInstance @@ -270,7 +267,7 @@ class Ed448KeyGenerationAlgorithmInstance extends KnownEllipticCurveKeyGeneratio } /** - * Represents a generic `ECKeyPairGenerator` instance. + * A generic `ECKeyPairGenerator` instance. */ class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance, Crypto::EllipticCurveConsumingAlgorithmInstance instanceof ClassInstanceExpr @@ -312,8 +309,8 @@ class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAl } /** - * Represents LMS or HSS key generation instances. The algorithm is implicitly - * defined by the type. + * An LMS or HSS key generation instances. The algorithm is implicitly defined + * by the type. */ class StatefulSignatureKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance instanceof ClassInstanceExpr { diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll index 0dbbee6ab240..7f3d49348f1f 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll @@ -1,5 +1,4 @@ import java -import experimental.quantum.Language import AlgorithmInstances abstract class HashAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } @@ -9,9 +8,9 @@ abstract class CipherAlgorithmValueConsumer extends Crypto::AlgorithmValueConsum abstract class EllipticCurveAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } class EllipticCurveStringLiteralArg extends EllipticCurveAlgorithmValueConsumer instanceof Expr { - Params::ParametersInstantiation params; - - EllipticCurveStringLiteralArg() { this = params.getAlgorithmArg() } + EllipticCurveStringLiteralArg() { + this = any(Params::ParametersInstantiation params).getAlgorithmArg() + } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } @@ -21,7 +20,8 @@ class EllipticCurveStringLiteralArg extends EllipticCurveAlgorithmValueConsumer } /** - * Signature algorithms are implicitly defined by the constructor. + * The AVC for a signature algorithm where the algorithm is implicitly defined + * by the constructor. */ abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } @@ -30,7 +30,8 @@ abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueCon } /** - * Key generation algorithms are implicitly defined by the constructor. + * The AVC for a key generation algorithm where the algorithm is implicitly + * defined by the constructor. */ abstract class KeyGenerationAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll index 9e2092119b9e..c280aae43b34 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll @@ -1,7 +1,6 @@ import java import semmle.code.java.dataflow.DataFlow import experimental.quantum.Language -import AlgorithmInstances import AlgorithmValueConsumers /** @@ -223,7 +222,7 @@ module ParametersToInitFlowAnalysis { predicate isSink(DataFlow::Node sink) { exists(Init init | sink = init.getParametersInput()) } /** - * Pass-through for parameters created from other parameters. + * A flow step for parameters created from other parameters. * * As an example, below we want to track the flow from the `X9ECParameters` * constructor call to the `keyPairGenerator.init()` call to be able to @@ -269,6 +268,9 @@ module ParametersToInitFlowAnalysis { private module ParametersToInitFlow = DataFlow::Global; + /** + * Gets a parameter instantiation from a call to `init()`. + */ New getParametersFromInit( Init init, ParametersToInitFlow::PathNode src, ParametersToInitFlow::PathNode sink ) { @@ -279,8 +281,8 @@ module ParametersToInitFlowAnalysis { } /** - * Model data flow from a key pair to the public and private components of the - * key pair. + * A model for data flow from a key pair to the public and private components of + * the key pair. */ class KeyAdditionalFlowSteps extends MethodCall { KeyAdditionalFlowSteps() { @@ -299,30 +301,27 @@ class KeyAdditionalFlowSteps extends MethodCall { } /** - * Model data flow from an ECDSA signature to the scalars r and s passed to - * `verifySignature()`. The ECDSA signature is represented as a `BigInteger` + * A model for data flow from an ECDSA signature to the scalars r and s passed + * to `verifySignature()`. The ECDSA signature is represented as a `BigInteger` * array, where the first element is the scalar r and the second element is the * scalar s. */ -class ECDSASignatureAdditionalFlowSteps extends ArrayAccess { - ECDSASignatureAdditionalFlowSteps() { +class EcdsaSignatureAdditionalFlowSteps extends ArrayAccess { + EcdsaSignatureAdditionalFlowSteps() { this.getArray().getType().getName() = "BigInteger[]" and // It is reasonable to assume that the indices are integer literals this.getIndexExpr().(IntegerLiteral).getValue().toInt() = [0, 1] } - /** - * The input node is the ECDSA signature represented as a `BigInteger` array. - */ DataFlow::Node getInputNode() { - // TODO: This should be the array node `this.getArray()` + // The ECDSA signature is represented as a `BigInteger` array. result.asExpr() = this.getArray() } - /** - * The output node is the `BigInteger` element accessed by this array access. - */ - DataFlow::Node getOutputNode() { result.asExpr() = this } + DataFlow::Node getOutputNode() { + // r or s is the `BigInteger` element accessed by this array access. + result.asExpr() = this + } } predicate additionalFlowSteps(DataFlow::Node node1, DataFlow::Node node2) { @@ -331,12 +330,15 @@ predicate additionalFlowSteps(DataFlow::Node node1, DataFlow::Node node2) { node2 = fs.getOutputNode() ) or - exists(ECDSASignatureAdditionalFlowSteps fs | + exists(EcdsaSignatureAdditionalFlowSteps fs | node1 = fs.getInputNode() and node2 = fs.getOutputNode() ) } +/** + * An additional flow step for a cryptographic artifact. + */ class ArtifactAdditionalFlowStep extends AdditionalFlowInputStep { DataFlow::Node output; @@ -347,7 +349,7 @@ class ArtifactAdditionalFlowStep extends AdditionalFlowInputStep { module EllipticCurveStringLiteralToConsumer { /** - * Flow from a known elliptic curve name to an elliptic curve algorithm consumer. + * A flow from a known elliptic curve name to an elliptic curve algorithm consumer. */ module EllipticCurveStringLiteralToAlgorithmValueConsumerConfig implements DataFlow::ConfigSig { // NOTE: We do not reference EllipticCurveStringLiteralInstance directly diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll index 9e409f0a58e2..8da5d9c634f1 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll @@ -7,7 +7,7 @@ module Params { /** * A model of the `Parameters` class in Bouncy Castle. */ - class Parameters extends RefType { + class Parameters extends Class { Parameters() { // Matches `org.bouncycastle.crypto.params`, `org.bouncycastle.asn1.x9`, etc. this.getPackage().getName().matches("org.bouncycastle.%") and @@ -15,7 +15,7 @@ module Params { } } - class Curve extends RefType { + class Curve extends Class { Curve() { this.getPackage().getName() = "org.bouncycastle.math.ec" and this.getName().matches("ECCurve") @@ -126,7 +126,7 @@ module Signers { * This class represents a BouncyCastle signer with a streaming API. For signers * with a one-shot API, see `OneShotSigner` below. */ - class Signer extends RefType { + class Signer extends Class { Signer() { this.getPackage().getName() = ["org.bouncycastle.crypto.signers", "org.bouncycastle.pqc.crypto.lms"] and @@ -167,7 +167,7 @@ module Signers { * is passed to either `generateSignature()` or `verifySignature`.). */ class OneShotSigner extends Signer { - OneShotSigner() { this.getName().matches(["ECDSA%", "LMS%", "HSS%"]) } + OneShotSigner() { this.getName().matches(["DSASigner", "ECDSA%", "LMS%", "HSS%"]) } override Expr getMessageArg(MethodCall call) { // For ECDSA and LMS, the message is passed directly to `generateSignature()`. @@ -300,7 +300,7 @@ module Generators { /** * A model of the `KeyGenerator` and `KeyPairGenerator` classes in Bouncy Castle. */ - class KeyGenerator extends RefType { + class KeyGenerator extends Class { Crypto::KeyArtifactType type; KeyGenerator() { @@ -422,4 +422,73 @@ module Generators { } } -module Modes { } +module Modes { + import FlowAnalysis + + class BlockCipherMode extends Class { + BlockCipherMode() { + this.getPackage().getName() = "org.bouncycastle.crypto.modes" and + this.getName().matches("%BlockCipher") + } + + MethodCall getAnInitCall() { result = this.getAMethodCall("init") } + + MethodCall getAUseCall() { + result = + this.getAMethodCall([ + "processBlock", "processBlocks", "returnByte", "processBytes", "doFinal" + ]) + } + + MethodCall getAMethodCall(string name) { + result.getCallee().hasQualifiedName(this.getPackage().getName(), this.getName(), name) + } + } + + /** + * A block cipher mode instantiation. + * + * BouncyCastle algorithms are instantiated by calling the constructor of the + * corresponding class, which also represents the algorithm instance. + */ + private class BlockCipherModeNewCall extends ClassInstanceExpr { + BlockCipherModeNewCall() { this.getConstructedType() instanceof BlockCipherMode } + + DataFlow::Node getParametersInput() { none() } + + DataFlow::Node getEllipticCurveInput() { none() } + } + + /** + * A call to a block cipher mode `init()` method. + * + * The type is instantiated by a constructor call and initialized by a call to + * `init()` which takes a single `KeyGenerationParameters` argument. + */ + private class BlockCipherModeInitCall extends MethodCall { + BlockCipherMode mode; + + BlockCipherModeInitCall() { this = mode.getAnInitCall() } + + Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + // The `CipherParameters` argument used to configure the cipher. + DataFlow::Node getParametersInput() { result.asExpr() = this.getArgument(1) } + } + + /** + * A `processX()`, `returnByte()` or `doFinal()` method call to encrypt or + * decrypt data. + */ + private class BlockCipherModeUseCall extends MethodCall { + BlockCipherMode mode; + + BlockCipherModeUseCall() { this = mode.getAUseCall() } + + predicate isIntermediate() { not this.getCallee().getName() = "doFinal" } + } + + module BlockCipherModeFlow = + NewToInitToUseFlowAnalysis; +} diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index 1159c77df88a..325934c0ee3e 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -1430,6 +1430,9 @@ module CryptographyBase Input> { class AssetNode = NodeBase; /** + * Holds if the given elliptic curve is directly consumed by another algorithm + * node. + * * This predicate is used to filter out elliptic curve nodes in cases where * the algorithm instance and the curve instance are represented by the same * algorithm value consumer (e.g. in cases where both the algorithm and the From 2eecda35012195e4a9b7ba51cfee241bc54f18c1 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Wed, 4 Jun 2025 10:36:28 +0200 Subject: [PATCH 22/32] Removed duplicate condition in Model.qll Co-authored-by: Arthur Baars --- shared/quantum/codeql/quantum/experimental/Model.qll | 2 -- 1 file changed, 2 deletions(-) diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index 325934c0ee3e..8e81b6a03e21 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -2704,8 +2704,6 @@ module CryptographyBase Input> { or curveName = "CURVE448" and keySize = 448 and curveFamily = CURVE448() or - curveName = "CURVE448" and keySize = 448 and curveFamily = CURVE448() - or // TODO: separate these into key agreement logic or sign/verify (ECDSA / ECDH) // or // curveName = "X25519" and keySize = 255 and curveFamily = CURVE25519() From 192bb7f9a921a47ec762e148720a4cc1c4f8c19f Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Fri, 6 Jun 2025 18:17:47 +0200 Subject: [PATCH 23/32] Added support for block cipher modes --- .../BouncyCastle/AlgorithmInstances.qll | 217 +++++++++++++-- .../BouncyCastle/AlgorithmValueConsumers.qll | 43 ++- .../quantum/BouncyCastle/FlowAnalysis.qll | 217 ++++++++++++++- .../BouncyCastle/OperationInstances.qll | 253 ++++++++++++++++-- 4 files changed, 664 insertions(+), 66 deletions(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index ec2ec4ba204f..35e8c815e294 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -14,7 +14,7 @@ class EllipticCurveStringLiteralInstance extends Crypto::EllipticCurveInstance i override string getRawEllipticCurveName() { result = super.getValue() } EllipticCurveAlgorithmValueConsumer getConsumer() { - result = EllipticCurveStringLiteralToConsumer::getConsumerFromLiteral(this, _, _) + result = EllipticCurveStringLiteralToConsumerFlow::getConsumerFromLiteral(this, _, _) } override Crypto::TEllipticCurveType getEllipticCurveType() { @@ -69,25 +69,19 @@ abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmI } override string getRawAlgorithmName() { - typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) } - /** - * Used for data flow from elliptic curve string literals to the algorithm - * instance. - */ + // Used for data flow from elliptic curve string literals to the algorithm DataFlow::Node getParametersInput() { none() } - /** - * Used for data flow from elliptic curve string literals to the algorithm - * instance. - */ + // Used for data flow from elliptic curve string literals to the algorithm DataFlow::Node getEllipticCurveInput() { none() } } /** - * Represents an elliptic curve signature algorithm where both the signature - * algorithm and elliptic curve are implicitly defined by the underlying type. + * An elliptic curve signature algorithm where both the signature algorithm and + * elliptic curve are implicitly defined by the underlying type. */ abstract class KnownEllipticCurveSignatureAlgorithmInstance extends KnownEllipticCurveInstance, SignatureAlgorithmInstance @@ -107,7 +101,7 @@ class DsaSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceo } override string getRawAlgorithmName() { - typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) } } @@ -122,7 +116,7 @@ class Ed25519SignatureAlgorithmInstance extends KnownEllipticCurveSignatureAlgor } override string getRawAlgorithmName() { - typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) } override string getRawEllipticCurveName() { result = "Curve25519" } @@ -139,7 +133,7 @@ class Ed448SignatureAlgorithmInstance extends KnownEllipticCurveSignatureAlgorit } override string getRawAlgorithmName() { - typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) } override string getRawEllipticCurveName() { result = "Curve448" } @@ -163,7 +157,7 @@ class EcdsaSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanc } override string getRawAlgorithmName() { - typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) } override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { @@ -184,7 +178,7 @@ class StatefulSignatureAlgorithmInstance extends SignatureAlgorithmInstance inst } override string getRawAlgorithmName() { - typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) } override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { @@ -218,19 +212,15 @@ abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgori } override string getRawAlgorithmName() { - typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) } - /** - * Used for data flow from elliptic curve string literals to the algorithm - * instance. - */ + // Used for data flow from elliptic curve string literals to the algorithm + // instance. DataFlow::Node getParametersInput() { none() } - /** - * Used for data flow from elliptic curve string literals to the algorithm - * instance. - */ + // Used for data flow from elliptic curve string literals to the algorithm + // instance. DataFlow::Node getEllipticCurveInput() { none() } } @@ -320,7 +310,7 @@ class StatefulSignatureKeyGenerationAlgorithmInstance extends KeyGenerationAlgor } override string getRawAlgorithmName() { - typeNameToRawAlgorithmName(super.getConstructedType().getName(), result) + typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) } override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { @@ -332,11 +322,103 @@ class StatefulSignatureKeyGenerationAlgorithmInstance extends KeyGenerationAlgor } } +/** + * A block cipher used in a mode of operation. The algorithm is implicitly + * defined by the type. + */ +class BlockCipherAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance instanceof ClassInstanceExpr +{ + // We track the block cipher mode here to ensure that going from the block + // cipher instance to the block cipher mode instance and back always yields + // the same instance. + // + // Since the block cipher algorithm instance is always resolved using data + // flow from the block cipher mode, we don't loose any information by + // requiring that this flow exists. + BlockCipherModeAlgorithmInstance mode; + + BlockCipherAlgorithmInstance() { + super.getConstructedType() instanceof Modes::BlockCipher and + mode = BlockCipherToBlockCipherModeFlow::getBlockCipherModeFromBlockCipher(this, _, _) + } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + if blockCipherNameToAlgorithmMapping(this.getRawAlgorithmName(), _) + then blockCipherNameToAlgorithmMapping(this.getRawAlgorithmName(), result) + else result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::OtherSymmetricCipherType()) + } + + // TODO: Implement this. + override int getKeySizeFixed() { none() } + + override string getRawAlgorithmName() { + typeNameToRawAlgorithmNameMapping(super.getType().getName(), result) + } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { result = mode } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { + result = BlockCipherToPaddingModeFlow::getPaddingModeFromBlockCipher(this) + } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + // Gets a consumer of this block cipher algorithm instance. + Crypto::AlgorithmValueConsumer getConsumer() { result = mode.getBlockCipherArg() } +} + +/** + * A block cipher mode instance. + */ +class BlockCipherModeAlgorithmInstance extends Crypto::ModeOfOperationAlgorithmInstance, + BlockCipherModeAlgorithmValueConsumer instanceof ClassInstanceExpr +{ + BlockCipherModeAlgorithmInstance() { + super.getConstructedType() instanceof Modes::UnpaddedBlockCipherMode + } + + override string getRawModeAlgorithmName() { + result = super.getConstructedType().getName().splitAt("BlockCipher", 0) + } + + override Crypto::TBlockCipherModeOfOperationType getModeType() { + if modeNameToModeTypeMapping(this.getRawModeAlgorithmName(), _) + then modeNameToModeTypeMapping(this.getRawModeAlgorithmName(), result) + else result = Crypto::OtherMode() + } + + Expr getBlockCipherArg() { + exists(Expr arg | + arg = super.getAnArgument() and + arg.getType() instanceof Modes::BlockCipher and + result = arg + ) + } + + Crypto::AlgorithmValueConsumer getConsumer() { result = this } +} + +/** + * A padding mode instance implicitly determined by the constructor. + */ +class PaddingAlgorithmInstance extends Crypto::PaddingAlgorithmInstance instanceof ClassInstanceExpr +{ + PaddingAlgorithmInstance() { super.getConstructedType() instanceof Modes::PaddingMode } + + override Crypto::TPaddingType getPaddingType() { + paddingNameToTypeMapping(this.getRawPaddingAlgorithmName(), result) + } + + override string getRawPaddingAlgorithmName() { + result = super.getConstructedType().getName().splitAt("Padding", 0) + } +} + /** * Private predicates mapping type names to raw names, key sizes and algorithms. */ bindingset[typeName] -private predicate typeNameToRawAlgorithmName(string typeName, string algorithmName) { +private predicate typeNameToRawAlgorithmNameMapping(string typeName, string algorithmName) { // Ed25519, Ed25519ph, and Ed25519ctx key generators and signers typeName.matches("Ed25519%") and algorithmName = "Ed25519" @@ -356,6 +438,66 @@ private predicate typeNameToRawAlgorithmName(string typeName, string algorithmNa // HSS typeName.matches("HSS%") and algorithmName = "HSS" + or + typeName.matches("AES%") and + algorithmName = "AES" + or + typeName.matches("Aria%") and + algorithmName = "Aria" + or + typeName.matches("Blowfish%") and + algorithmName = "Blowfish" + or + typeName.matches("DES%") and + algorithmName = "DES" + or + typeName.matches("TripleDES%") and + algorithmName = "TripleDES" +} + +private predicate modeNameToModeTypeMapping( + string modeName, Crypto::TBlockCipherModeOfOperationType modeType +) { + modeName = "CBC" and + modeType = Crypto::CBC() + or + modeName = "CCM" and + modeType = Crypto::CCM() + or + modeName = "CFB" and + modeType = Crypto::CFB() + or + modeName = "CTR" and + modeType = Crypto::CTR() + or + modeName = "ECB" and + modeType = Crypto::ECB() + or + modeName = "GCM" and + modeType = Crypto::GCM() + or + modeName = "OCB" and + modeType = Crypto::OCB() + or + modeName = "OFB" and + modeType = Crypto::OFB() + or + modeName = "XTS" and + modeType = Crypto::XTS() +} + +private predicate paddingNameToTypeMapping(string paddingName, Crypto::TPaddingType paddingType) { + paddingName = "NoPadding" and + paddingType = Crypto::NoPadding() + or + paddingName = "PKCS7" and + paddingType = Crypto::PKCS7() + or + paddingName = "ISO10126" and + paddingType = Crypto::OtherPadding() + or + paddingName = "ZeroByte" and + paddingType = Crypto::OtherPadding() } private predicate signatureNameToKeySizeAndAlgorithmMapping( @@ -381,3 +523,22 @@ private predicate generatorNameToKeySizeAndAlgorithmMapping( keySize = 448 and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) } + +private predicate blockCipherNameToAlgorithmMapping( + string name, Crypto::KeyOpAlg::Algorithm algorithm +) { + name = "AES" and + algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES()) + or + name = "Aria" and + algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::ARIA()) + or + name = "Blowfish" and + algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::BLOWFISH()) + or + name = "DES" and + algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES()) + or + name = "TripleDES" and + algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::TripleDES()) +} diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll index 7f3d49348f1f..a1c9f51cd51b 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll @@ -1,12 +1,12 @@ import java import AlgorithmInstances -abstract class HashAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } - -abstract class CipherAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } - abstract class EllipticCurveAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } +/** + * An AVC for an elliptic curve algorithm where the algorithm is defined by an + * elliptic curve string literal. + */ class EllipticCurveStringLiteralArg extends EllipticCurveAlgorithmValueConsumer instanceof Expr { EllipticCurveStringLiteralArg() { this = any(Params::ParametersInstantiation params).getAlgorithmArg() @@ -20,8 +20,8 @@ class EllipticCurveStringLiteralArg extends EllipticCurveAlgorithmValueConsumer } /** - * The AVC for a signature algorithm where the algorithm is implicitly defined - * by the constructor. + * An AVC for a signature algorithm where the algorithm is implicitly defined by + * the constructor. */ abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } @@ -30,7 +30,7 @@ abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueCon } /** - * The AVC for a key generation algorithm where the algorithm is implicitly + * An AVC for a key generation algorithm where the algorithm is implicitly * defined by the constructor. */ abstract class KeyGenerationAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { @@ -38,3 +38,32 @@ abstract class KeyGenerationAlgorithmValueConsumer extends Crypto::AlgorithmValu override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } } + +/** + * A block cipher argument passed to an block cipher mode constructor. + */ +class BlockCipherAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof Expr { + BlockCipherAlgorithmValueConsumer() { + this = any(BlockCipherModeAlgorithmInstance mode).getBlockCipherArg() + } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(BlockCipherAlgorithmInstance).getConsumer() = this + } +} + +/** + * An AVC for a block cipher mode implicitly defined by the constructor. + */ +abstract class BlockCipherModeAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof ClassInstanceExpr +{ + BlockCipherModeAlgorithmValueConsumer() { + this.getType() instanceof Modes::UnpaddedBlockCipherMode + } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } +} diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll index c280aae43b34..08f2cb8a76c1 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll @@ -45,7 +45,7 @@ signature class UseCallSig instanceof MethodCall { * gen.generateKeyPair(...); * ``` */ -module NewToInitToUseFlowAnalysis { +module NewToInitToUseFlow { newtype TFlowState = TUninitialized() or TInitialized(Init call) or @@ -215,7 +215,7 @@ private class CurveInstantiation extends MethodCall { * TODO: Rewrite using stateful data flow to track whether or not the node * represents a parameter object or a curve. */ -module ParametersToInitFlowAnalysis { +module ParametersToInitFlow { private module ParametersToInitConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source.asExpr() instanceof New } @@ -347,11 +347,13 @@ class ArtifactAdditionalFlowStep extends AdditionalFlowInputStep { override DataFlow::Node getOutput() { result = output } } -module EllipticCurveStringLiteralToConsumer { +module EllipticCurveStringLiteralToConsumerFlow { /** * A flow from a known elliptic curve name to an elliptic curve algorithm consumer. */ - module EllipticCurveStringLiteralToAlgorithmValueConsumerConfig implements DataFlow::ConfigSig { + private module EllipticCurveStringLiteralToAlgorithmValueConsumerConfig implements + DataFlow::ConfigSig + { // NOTE: We do not reference EllipticCurveStringLiteralInstance directly // here to avoid non-monotonic recursion. predicate isSource(DataFlow::Node src) { src.asExpr() instanceof StringLiteral } @@ -366,7 +368,7 @@ module EllipticCurveStringLiteralToConsumer { } } - module EllipticCurveStringLiteralToAlgorithmValueConsumerFlow = + private module EllipticCurveStringLiteralToAlgorithmValueConsumerFlow = DataFlow::Global; EllipticCurveAlgorithmValueConsumer getConsumerFromLiteral( @@ -378,3 +380,208 @@ module EllipticCurveStringLiteralToConsumer { EllipticCurveStringLiteralToAlgorithmValueConsumerFlow::flowPath(src, sink) } } + +/** + * A flow analysis module for analyzing data flow from a boolean literal to an + * argument to `init()`. + * + * This is used to determine the boolean value of `forSigning` and `encrypting` + * arguments to `init()`, which in turn determine the key operation sub type for + * the corresponding key operation instances. + */ +module BooleanLiteralToInitFlow { + /** + * A flow from a boolean literal to a method call argument. + */ + private module BooleanLiteralToInitConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof BooleanLiteral } + + predicate isSink(DataFlow::Node sink) { + exists(MethodCall init | sink.asExpr() = init.getAnArgument()) + } + } + + private module BooleanLiteralToInitFlow = DataFlow::Global; + + boolean getBooleanValue( + Expr arg, BooleanLiteralToInitFlow::PathNode src, BooleanLiteralToInitFlow::PathNode sink + ) { + exists(BooleanLiteral lit | + src.getNode().asExpr() = lit and + sink.getNode().asExpr() = arg and + BooleanLiteralToInitFlow::flowPath(src, sink) and + lit.getBooleanValue() = result + ) + } + + predicate hasBooleanValue( + Expr arg, BooleanLiteralToInitFlow::PathNode src, BooleanLiteralToInitFlow::PathNode sink + ) { + exists(getBooleanValue(arg, src, sink)) + } +} + +/** + * A flow analysis module for tracking block cipher modes to block cipher modes + * with padding. + * + * Example: + * ``` + * CBCBlockCipher mode = new CBCBlockCipher(engine); + * PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(mode, ...); + * ``` + */ +module BlockCipherModeToBlockCipherModeFlow { + /** + * A flow from a block cipher mode to a block cipher mode with padding. + */ + private module BlockCipherModeToBlockCipherModeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof + Modes::UnpaddedBlockCipherMode + } + + predicate isSink(DataFlow::Node sink) { + exists(ClassInstanceExpr new | + new.getConstructedType() instanceof Modes::PaddedBlockCipherMode and + sink.asExpr() = new.getAnArgument() + ) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + // Flow from a block cipher mode passed as an argument to another block cipher mode. + node2.asExpr().(ClassInstanceExpr).getConstructedType() instanceof Modes::BlockCipherMode and + node1.asExpr() = node2.asExpr().(ClassInstanceExpr).getAnArgument() + } + } + + private module BlockCipherModeToBlockCipherModeFlow = + DataFlow::Global; + + ClassInstanceExpr getUnpaddedModeFromPaddedMode( + ClassInstanceExpr padded, BlockCipherModeToBlockCipherModeFlow::PathNode src, + BlockCipherModeToBlockCipherModeFlow::PathNode sink + ) { + src.getNode().asExpr() = result and + sink.getNode().asExpr() = padded.getAnArgument() and + BlockCipherModeToBlockCipherModeFlow::flowPath(src, sink) + } + + ClassInstanceExpr getPaddedModeFromUnpaddedMode( + ClassInstanceExpr unpadded, BlockCipherModeToBlockCipherModeFlow::PathNode src, + BlockCipherModeToBlockCipherModeFlow::PathNode sink + ) { + src.getNode().asExpr() = unpadded and + sink.getNode().asExpr() = result.getAnArgument() and + BlockCipherModeToBlockCipherModeFlow::flowPath(src, sink) + } +} + +/** + * A flow analysis module for analyzing data flow from a block cipher instance + * to a corresponding block cipher mode instance. + */ +module BlockCipherToBlockCipherModeFlow { + /** + * A flow from a block cipher instance to a block cipher mode instance. + */ + private module BlockCipherToBlockCipherModeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof Modes::BlockCipher + } + + predicate isSink(DataFlow::Node sink) { + exists(ClassInstanceExpr new | + new.getConstructedType() instanceof Modes::BlockCipherMode and + sink.asExpr() = new.getAnArgument() + ) + } + } + + private module BlockCipherToBlockCipherModeFlow = + DataFlow::Global; + + ClassInstanceExpr getBlockCipherModeFromBlockCipher( + ClassInstanceExpr cipher, BlockCipherToBlockCipherModeFlow::PathNode src, + BlockCipherToBlockCipherModeFlow::PathNode sink + ) { + src.getNode().asExpr() = cipher and + sink.getNode().asExpr() = result.getAnArgument() and + BlockCipherToBlockCipherModeFlow::flowPath(src, sink) + } +} + +/** + * A flow analysis module for analyzing data flow from a block cipher instance + * to a corresponding padding mode instance. + */ +module BlockCipherToPaddingModeFlow { + /** + * A flow from a block cipher instance to a block cipher mode instance. + */ + private module BlockCipherToBlockCipherModeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof Modes::BlockCipher + } + + predicate isSink(DataFlow::Node sink) { + exists(ClassInstanceExpr new | + new.getConstructedType() instanceof Modes::BlockCipherMode and + sink.asExpr() = new.getAnArgument() + ) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + // Flow from a block cipher mode passed as an argument to another block cipher mode. + node2.asExpr().(ClassInstanceExpr).getConstructedType() instanceof Modes::BlockCipherMode and + node1.asExpr() = node2.asExpr().(ClassInstanceExpr).getAnArgument() + } + } + + private module BlockCipherToBlockCipherModeFlow = + DataFlow::Global; + + /** + * A flow from a padding mode instance to a block cipher mode instance. + */ + private module PaddingModeToBlockCipherModeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof Modes::PaddingMode + } + + predicate isSink(DataFlow::Node sink) { + exists(ClassInstanceExpr new | + new.getConstructedType() instanceof Modes::BlockCipherMode and + sink.asExpr() = new.getAnArgument() + ) + } + } + + private module PaddingModeToBlockCipherModeFlow = + DataFlow::Global; + + private ClassInstanceExpr getBlockCipherModeFromBlockCipher( + ClassInstanceExpr cipher, BlockCipherToBlockCipherModeFlow::PathNode src, + BlockCipherToBlockCipherModeFlow::PathNode sink + ) { + src.getNode().asExpr() = cipher and + sink.getNode().asExpr() = result.getAnArgument() and + BlockCipherToBlockCipherModeFlow::flowPath(src, sink) + } + + private ClassInstanceExpr getBlockCipherModeFromPaddingMode( + ClassInstanceExpr padding, PaddingModeToBlockCipherModeFlow::PathNode src, + PaddingModeToBlockCipherModeFlow::PathNode sink + ) { + src.getNode().asExpr() = padding and + sink.getNode().asExpr() = result.getAnArgument() and + PaddingModeToBlockCipherModeFlow::flowPath(src, sink) + } + + ClassInstanceExpr getPaddingModeFromBlockCipher(ClassInstanceExpr cipher) { + exists(ClassInstanceExpr mode | + mode = getBlockCipherModeFromBlockCipher(cipher, _, _) and + mode = getBlockCipherModeFromPaddingMode(result, _, _) + ) + } +} diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll index 8da5d9c634f1..69feb105452b 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll @@ -11,7 +11,7 @@ module Params { Parameters() { // Matches `org.bouncycastle.crypto.params`, `org.bouncycastle.asn1.x9`, etc. this.getPackage().getName().matches("org.bouncycastle.%") and - this.getName().matches(["%Parameters", "%ParameterSpec"]) + this.getName().matches(["%Parameter", "%Parameters", "%ParameterSpec", "ParametersWith%"]) } } @@ -26,12 +26,12 @@ module Params { KeyParameters() { this.getPackage().getName() = ["org.bouncycastle.crypto.params", "org.bouncycastle.pqc.crypto.lms"] and - this.getName().matches("%KeyParameters") + this.getName().matches(["%KeyParameter", "%KeyParameters"]) } } /** - * Any call that returns a BouncyCastle parameters object. This type is used + * A call that returns a BouncyCastle parameters object. This type is used * to model data flow to resolve algorithm instances like elliptic curves. * * Examples: @@ -55,6 +55,12 @@ module Params { this.(MethodCall).getType() instanceof Parameters } + // Can be overridden by subclasses which take a key argument. + Expr getKeyArg() { none() } + + // Can be overridden by subclasses which take a nonce argument. + Expr getNonceArg() { none() } + // Can be overridden by subclasses which take a key size argument. Expr getKeySizeArg() { none() } @@ -71,6 +77,10 @@ module Params { result.getType() instanceof Curve } + Crypto::ConsumerInputDataFlowNode getKeyConsumer() { result.asExpr() = this.getKeyArg() } + + Crypto::ConsumerInputDataFlowNode getNonceConsumer() { result.asExpr() = this.getNonceArg() } + Crypto::ConsumerInputDataFlowNode getAKeySizeConsumer() { result.asExpr() = this.getKeySizeArg() } @@ -85,7 +95,7 @@ module Params { } /** - * Models the named elliptic curve passed to `X9ECParameters.getCurve()`. + * The named elliptic curve passed to `X9ECParameters.getCurve()`. */ class X9ECParametersInstantiation extends ParametersInstantiation { X9ECParametersInstantiation() { this.(Expr).getType().getName() = "X9ECParameters" } @@ -98,7 +108,7 @@ module Params { } /** - * Models the named elliptic curve passed to `ECNamedCurveTable.getParameterSpec()`. + * The named elliptic curve passed to `ECNamedCurveTable.getParameterSpec()`. */ class ECNamedCurveParameterSpecInstantiation extends ParametersInstantiation { ECNamedCurveParameterSpecInstantiation() { @@ -111,6 +121,40 @@ module Params { result = this.getArgument(0) } } + + /** + * An `AEADParameters` instantiation. + * + * This type is used to model data flow from a nonce to a cipher operation. + */ + class AeadParametersInstantiation extends ParametersInstantiation { + AeadParametersInstantiation() { + this.(ConstructorCall).getConstructedType().getName() = "AEADParameters" + } + + override Expr getNonceArg() { result = this.(ConstructorCall).getArgument(2) } + } + + class ParametersWithIvInstantiation extends ParametersInstantiation { + ParametersWithIvInstantiation() { + this.(ConstructorCall).getConstructedType().getName() = "ParametersWithIV" + } + + override Expr getNonceArg() { result = this.(ConstructorCall).getArgument(1) } + } + + /** + * A `KeyParameter` instantiation. + * + * This type is used to model data flow from a key to a cipher operation. + */ + class KeyParameterInstantiation extends ParametersInstantiation { + KeyParameterInstantiation() { + this.(ConstructorCall).getConstructedType().getName() = "KeyParameter" + } + + override Expr getKeyArg() { result = this.(ConstructorCall).getArgument(0) } + } } /** @@ -230,11 +274,8 @@ module Signers { Expr getSignatureOutput() { result = signer.getSignatureOutput(this) } } - /** - * Instantiate the flow analysis module for the `Signer` class. - */ - private module SignerFlow = - NewToInitToUseFlowAnalysis; + // Instantiate the flow analysis module for the `Signer` class. + private module SignerFlow = NewToInitToUseFlow; /** * A signing operation instance is a call to either `update()`, `generateSignature()`, @@ -365,8 +406,7 @@ module Generators { } /** - * The `generateKey()` and `generateKeyPair()` methods are used to generate - * the resulting key, depending on the type of the generator. + * A call to either `generateKey()` and `generateKeyPair()`. */ private class KeyGeneratorUseCall extends MethodCall { KeyGenerator gen; @@ -382,10 +422,10 @@ module Generators { } module KeyGeneratorFlow = - NewToInitToUseFlowAnalysis; + NewToInitToUseFlow; module ParametersFlow = - ParametersToInitFlowAnalysis; + ParametersToInitFlow; /** * A key generation operation instance is a call to `generateKey()` or @@ -425,15 +465,24 @@ module Generators { module Modes { import FlowAnalysis - class BlockCipherMode extends Class { - BlockCipherMode() { + abstract class BlockCipherMode extends Class { + abstract MethodCall getAnInitCall(); + + abstract MethodCall getAUseCall(); + } + + /** + * An unpadded block cipher mode like CTR or GCM. + */ + class UnpaddedBlockCipherMode extends BlockCipherMode { + UnpaddedBlockCipherMode() { this.getPackage().getName() = "org.bouncycastle.crypto.modes" and this.getName().matches("%BlockCipher") } - MethodCall getAnInitCall() { result = this.getAMethodCall("init") } + override MethodCall getAnInitCall() { result = this.getAMethodCall("init") } - MethodCall getAUseCall() { + override MethodCall getAUseCall() { result = this.getAMethodCall([ "processBlock", "processBlocks", "returnByte", "processBytes", "doFinal" @@ -441,18 +490,82 @@ module Modes { } MethodCall getAMethodCall(string name) { - result.getCallee().hasQualifiedName(this.getPackage().getName(), this.getName(), name) + result.getQualifier().getType() instanceof UnpaddedBlockCipherMode and + result.getMethod().getName() = name + } + } + + /** + * A block cipher padding mode, like PKCS7. + */ + class PaddingMode extends Class { + PaddingMode() { + this.getPackage().getName() = "org.bouncycastle.crypto.paddings" and + this.getName().matches("%Padding") + } + } + + /** + * A block cipher mode that uses a padding scheme, like CBC. + */ + class PaddedBlockCipherMode extends BlockCipherMode { + PaddedBlockCipherMode() { + this.getPackage().getName() = "org.bouncycastle.crypto.paddings" and + this.getName() = "PaddedBufferedBlockCipher" + } + + override MethodCall getAnInitCall() { result = this.getAMethodCall("init") } + + override MethodCall getAUseCall() { + result = + this.getAMethodCall([ + "processBlock", "processBlocks", "returnByte", "processBytes", "doFinal" + ]) + } + + MethodCall getAMethodCall(string name) { + result.getQualifier().getType() instanceof PaddedBlockCipherMode and + result.getMethod().getName() = name + } + } + + class BlockCipher extends Class { + BlockCipher() { + this.getPackage().getName() = "org.bouncycastle.crypto.engines" and + this.getName().matches("%Engine") } } /** * A block cipher mode instantiation. * - * BouncyCastle algorithms are instantiated by calling the constructor of the - * corresponding class, which also represents the algorithm instance. + * This class represents both unpadded block cipher mode instantiations (like + * `GCMBlockCipher` and `CBCBlockCipher`), as well as padded block cipher mode + * instantiations (like `PaddedBufferedBlockCipher`). Both can be used to + * encrypt and decrypt data. */ private class BlockCipherModeNewCall extends ClassInstanceExpr { - BlockCipherModeNewCall() { this.getConstructedType() instanceof BlockCipherMode } + BlockCipherModeNewCall() { this.getType() instanceof BlockCipherMode } + + Crypto::AlgorithmValueConsumer getBlockCipherConsumer() { + result = this.getUnpaddedBlockCipherMode().getBlockCipherArg() + } + + BlockCipherModeNewCall getUnpaddedBlockCipherMode() { + this.getConstructedType() instanceof UnpaddedBlockCipherMode and + result = this + or + this.getConstructedType() instanceof PaddedBlockCipherMode and + result = BlockCipherModeToBlockCipherModeFlow::getUnpaddedModeFromPaddedMode(this, _, _) + } + + Expr getBlockCipherArg() { + exists(Expr arg | + arg = this.getAnArgument() and + arg.getType() instanceof Modes::BlockCipher and + result = arg + ) + } DataFlow::Node getParametersInput() { none() } @@ -466,9 +579,20 @@ module Modes { * `init()` which takes a single `KeyGenerationParameters` argument. */ private class BlockCipherModeInitCall extends MethodCall { - BlockCipherMode mode; + BlockCipherModeInitCall() { this = any(BlockCipherMode mode).getAnInitCall() } + + // This is true if the mode is being initialized for encryption. + Expr getEncryptingArg() { result = this.getArgument(0) } - BlockCipherModeInitCall() { this = mode.getAnInitCall() } + Crypto::KeyOperationSubtype getKeyOperationSubtype() { + // The key operation sub-type is determined by the `encrypting` argument to `init()`. + if BooleanLiteralToInitFlow::hasBooleanValue(this.getEncryptingArg(), _, _) + then + if BooleanLiteralToInitFlow::getBooleanValue(this.getEncryptingArg(), _, _) = true + then result = Crypto::TEncryptMode() + else result = Crypto::TDecryptMode() + else result = Crypto::TUnknownKeyOperationMode() + } Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } @@ -486,9 +610,86 @@ module Modes { BlockCipherModeUseCall() { this = mode.getAUseCall() } predicate isIntermediate() { not this.getCallee().getName() = "doFinal" } + + Expr getInput() { result = this.getArgument(0) } + + Expr getOutput() { + this.getCallee().getName() = "processBlock" and + result = this.getArgument(2) // The `out` byte array argument. + or + this.getCallee().getName() = "processBlocks" and + result = this.getArgument(3) // The `out` byte array argument. + or + this.getCallee().getName() = "processBytes" and + result = this.getArgument(3) // The `out` byte array argument. + or + this.getCallee().getName() = "returnByte" and + result = this // The return value. + } } module BlockCipherModeFlow = - NewToInitToUseFlowAnalysis; + NewToInitToUseFlow; + + module ParametersFlow = + ParametersToInitFlow; + + /** + * A block cipher mode operation is a call to a finalizing method (like + * `doFinal()`) on the block cipher mode instance. The encryption algorithm and + * padding mode are determined from the parameters passed to `init()`. + */ + class BlockCipherModeOperationInstance extends Crypto::KeyOperationInstance instanceof BlockCipherModeUseCall + { + BlockCipherModeOperationInstance() { not this.isIntermediate() } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + // The class instantiation (returned by `getNewFromUse()`) represents the + // block cipher *mode* algorithm instance. Here, we need to return the + // block cipher algorithm instance which is resolved from the algorithm + // mode using data flow (in `getBlockCipherAlgorithmConsumer()`). + result = this.getNewCall().getBlockCipherConsumer() + } + + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + // The key operation sub-type is determined by the `encrypting` argument to `init()`. + exists(this.getInitCall()) and + result = this.getInitCall().getKeyOperationSubtype() + or + not exists(this.getInitCall()) and + result = Crypto::TUnknownKeyOperationMode() + } + + BlockCipherModeNewCall getNewCall() { result = BlockCipherModeFlow::getNewFromUse(this, _, _) } + + BlockCipherModeInitCall getInitCall() { + result = BlockCipherModeFlow::getInitFromUse(this, _, _) + } + + BlockCipherModeUseCall getAUseCall() { + result = BlockCipherModeFlow::getAnIntermediateUseFromFinalUse(this, _, _) + or + result = this + } + + Params::ParametersInstantiation getParameters() { + result = ParametersFlow::getParametersFromInit(this.getInitCall(), _, _) + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result.asExpr() = this.getAUseCall().getOutput() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result.asExpr() = this.getAUseCall().getInput() + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + result = this.getParameters().getKeyConsumer() + } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { + result = this.getParameters().getNonceConsumer() + } + } } From f97be14fe7cd3e64098f95805081a9bcd136958d Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Thu, 12 Jun 2025 13:40:51 +0200 Subject: [PATCH 24/32] Fixed argument to block cipher mode method being tagged as input --- .../experimental/quantum/BouncyCastle/OperationInstances.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll index 69feb105452b..9826068493f9 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll @@ -611,7 +611,7 @@ module Modes { predicate isIntermediate() { not this.getCallee().getName() = "doFinal" } - Expr getInput() { result = this.getArgument(0) } + Expr getInput() { result = this.getArgument(0) and this.isIntermediate() } Expr getOutput() { this.getCallee().getName() = "processBlock" and @@ -625,6 +625,9 @@ module Modes { or this.getCallee().getName() = "returnByte" and result = this // The return value. + or + this.getCallee().getName() = "doFinal" and + result = this.getArgument(0) // The `out` byte array argument. } } From 7969bdf8ae859ac8a4a10e87217c2cff36c0ecbc Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Thu, 12 Jun 2025 13:42:06 +0200 Subject: [PATCH 25/32] Added test cases for Bouncy Castle block cipher modes This commit also reorganizes the Bouncy Castle test cases into separate sub-directories for signature and cipher modes. --- .../BouncyCastle/modes/AESCBCEncryption.java | 71 +++++++++++++++++++ .../BouncyCastle/modes/AESGCMEncryption.java | 70 ++++++++++++++++++ .../modes/cipher_operations.expected | 8 +++ .../BouncyCastle/modes/cipher_operations.ql | 6 ++ .../BouncyCastle/modes/key_artifacts.expected | 4 ++ .../quantum/BouncyCastle/{ => modes}/options | 2 +- .../modes/random_artifacts.expected | 8 +++ .../BouncyCastle/modes/random_artifacts.ql | 6 ++ .../{ => signers}/ECDSAP256SignAndVerify.java | 0 .../{ => signers}/Ed25519SignAndVerify.java | 0 .../{ => signers}/Ed448SignAndVerify.java | 0 .../{ => signers}/LMSSignature.java | 0 .../{ => signers}/key_artifacts.expected | 0 .../{ => signers}/key_artifacts.ql | 0 .../key_generation_operations.expected | 0 .../key_generation_operations.ql | 0 .../quantum/BouncyCastle/signers/options | 1 + .../signature_operations.expected | 0 .../{ => signers}/signature_operations.ql | 0 .../crypto/engines/AESEngine.java | 17 +++++ .../crypto/modes/CBCBlockCipher.java | 21 ++++++ .../crypto/modes/GCMBlockCipher.java | 29 ++++++++ .../crypto/paddings/PKCS7Padding.java | 19 +++++ .../paddings/PaddedBufferedBlockCipher.java | 40 +++++++++++ .../crypto/params/AEADParameters.java | 35 +++++++++ .../crypto/params/KeyParameter.java | 13 ++++ .../crypto/params/ParametersWithIV.java | 19 +++++ 27 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/AESCBCEncryption.java create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/AESGCMEncryption.java create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.ql create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/key_artifacts.expected rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/{ => modes}/options (65%) create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/{ => signers}/ECDSAP256SignAndVerify.java (100%) rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/{ => signers}/Ed25519SignAndVerify.java (100%) rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/{ => signers}/Ed448SignAndVerify.java (100%) rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/{ => signers}/LMSSignature.java (100%) rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/{ => signers}/key_artifacts.expected (100%) rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/{ => signers}/key_artifacts.ql (100%) rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/{ => signers}/key_generation_operations.expected (100%) rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/{ => signers}/key_generation_operations.ql (100%) create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/options rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/{ => signers}/signature_operations.expected (100%) rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/{ => signers}/signature_operations.ql (100%) create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/engines/AESEngine.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/modes/CBCBlockCipher.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/modes/GCMBlockCipher.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PKCS7Padding.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/AEADParameters.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/KeyParameter.java create mode 100644 java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ParametersWithIV.java diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/AESCBCEncryption.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/AESCBCEncryption.java new file mode 100644 index 000000000000..4e315a0dfa90 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/AESCBCEncryption.java @@ -0,0 +1,71 @@ +import java.security.SecureRandom; +import java.security.Security; +import java.util.Arrays; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.paddings.PKCS7Padding; +import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** + * Example of AES-CBC encryption and decryption using Bouncy Castle's low-level API. + */ +public class AESCBCEncryption { + byte[] encrypt(byte[] plaintext, byte[] key, byte[] iv) throws Exception { + AESEngine engine = new AESEngine(); + CBCBlockCipher mode = new CBCBlockCipher(engine); + PKCS7Padding padding = new PKCS7Padding(); + PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(mode, padding); + + KeyParameter keyParam = new KeyParameter(key); + ParametersWithIV params = new ParametersWithIV(keyParam, iv); + + cipher.init(true, params); // true for encryption + byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)]; + int outputLen = cipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0); + outputLen += cipher.doFinal(ciphertext, outputLen); + + if (outputLen != ciphertext.length) { + ciphertext = Arrays.copyOf(ciphertext, outputLen); + } + return ciphertext; + } + byte[] decrypt(byte[] ciphertext, byte[] key, byte[] iv) throws Exception { + AESEngine engine = new AESEngine(); + CBCBlockCipher mode = new CBCBlockCipher(engine); + PKCS7Padding padding = new PKCS7Padding(); + PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(mode, padding); + + KeyParameter keyParam = new KeyParameter(key); + ParametersWithIV params = new ParametersWithIV(keyParam, iv); + + cipher.init(false, params); // false for decryption + byte[] plaintext = new byte[cipher.getOutputSize(ciphertext.length)]; + int outputLen = cipher.processBytes(ciphertext, 0, ciphertext.length, plaintext, 0); + outputLen += cipher.doFinal(plaintext, outputLen); + + if (outputLen != plaintext.length) { + plaintext = Arrays.copyOf(plaintext, outputLen); + } + return plaintext; + } + public static void main(String[] args) { + Security.addProvider(new BouncyCastleProvider()); + + try { + SecureRandom random = new SecureRandom(); + byte[] key = new byte[32]; + random.nextBytes(key); + byte[] iv = new byte[16]; + random.nextBytes(iv); + + byte[] message = "Hello AES-CBC mode!".getBytes("UTF-8"); + byte[] ciphertext = new AESCBCEncryption().encrypt(message, key, iv); + byte[] plaintext = new AESCBCEncryption().decrypt(ciphertext, key, iv); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/AESGCMEncryption.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/AESGCMEncryption.java new file mode 100644 index 000000000000..b804171f2d4c --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/AESGCMEncryption.java @@ -0,0 +1,70 @@ +import java.security.SecureRandom; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import java.security.Security; +import java.util.Arrays; + +/** + * Example of AES-GCM encryption and decryption using Bouncy Castle's low-level API. + */ +public class AESGCMEncryption { + byte[] encrypt(byte[] plaintext, byte[] key, byte[] nonce, byte[] aad) throws Exception { + AESEngine engine = new AESEngine(); + GCMBlockCipher cipher = new GCMBlockCipher(engine); + AEADParameters params = new AEADParameters( + new KeyParameter(key), + 128, // Authentication tag size in bits + nonce, + aad); + + cipher.init(true, params); // true for encryption + byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)]; + int outputLen = cipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0); + outputLen += cipher.doFinal(ciphertext, outputLen); + + if (outputLen != ciphertext.length) { + ciphertext = Arrays.copyOf(ciphertext, outputLen); + } + return ciphertext; + } + byte[] decrypt(byte[] ciphertext, byte[] key, byte[] nonce, byte[] aad) throws Exception { + AESEngine engine = new AESEngine(); + GCMBlockCipher cipher = new GCMBlockCipher(engine); + AEADParameters params = new AEADParameters( + new KeyParameter(key), + 128, // Authentication tag size in bits + nonce, + aad); + + cipher.init(false, params); // false for decryption + byte[] plaintext = new byte[cipher.getOutputSize(ciphertext.length)]; + int outputLen = cipher.processBytes(ciphertext, 0, ciphertext.length, plaintext, 0); + outputLen += cipher.doFinal(plaintext, outputLen); + + if (outputLen != plaintext.length) { + plaintext = Arrays.copyOf(plaintext, outputLen); + } + return plaintext; + } + public static void main(String[] args) { + Security.addProvider(new BouncyCastleProvider()); + + try { + SecureRandom random = new SecureRandom(); + byte[] key = new byte[32]; + random.nextBytes(key); + byte[] nonce = new byte[12]; + random.nextBytes(nonce); + + byte[] message = "This is a message to be encrypted.".getBytes("UTF-8"); + byte[] aad = "This is additional authenticated data".getBytes("UTF-8"); + byte[] ciphertext = new AESGCMEncryption().encrypt(message, key, nonce, aad); + byte[] plaintext = new AESGCMEncryption().decrypt(ciphertext, key, nonce, aad); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected new file mode 100644 index 000000000000..452ef87f83d3 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected @@ -0,0 +1,8 @@ +| AESCBCEncryption.java:28:22:28:58 | EncryptOperation | AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | AESCBCEncryption.java:22:50:22:52 | Key | AESCBCEncryption.java:23:66:23:67 | Nonce | AESCBCEncryption.java:27:45:27:53 | Message | AESCBCEncryption.java:27:77:27:86 | KeyOperationOutput | +| AESCBCEncryption.java:28:22:28:58 | EncryptOperation | AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | AESCBCEncryption.java:22:50:22:52 | Key | AESCBCEncryption.java:23:66:23:67 | Nonce | AESCBCEncryption.java:27:45:27:53 | Message | AESCBCEncryption.java:28:37:28:46 | KeyOperationOutput | +| AESCBCEncryption.java:47:22:47:57 | DecryptOperation | AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | AESCBCEncryption.java:41:50:41:52 | Key | AESCBCEncryption.java:42:66:42:67 | Nonce | AESCBCEncryption.java:46:45:46:54 | Message | AESCBCEncryption.java:46:79:46:87 | KeyOperationOutput | +| AESCBCEncryption.java:47:22:47:57 | DecryptOperation | AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | AESCBCEncryption.java:41:50:41:52 | Key | AESCBCEncryption.java:42:66:42:67 | Nonce | AESCBCEncryption.java:46:45:46:54 | Message | AESCBCEncryption.java:47:37:47:45 | KeyOperationOutput | +| AESGCMEncryption.java:26:22:26:58 | EncryptOperation | AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | AESGCMEncryption.java:18:34:18:36 | Key | AESGCMEncryption.java:20:17:20:21 | Nonce | AESGCMEncryption.java:25:45:25:53 | Message | AESGCMEncryption.java:25:77:25:86 | KeyOperationOutput | +| AESGCMEncryption.java:26:22:26:58 | EncryptOperation | AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | AESGCMEncryption.java:18:34:18:36 | Key | AESGCMEncryption.java:20:17:20:21 | Nonce | AESGCMEncryption.java:25:45:25:53 | Message | AESGCMEncryption.java:26:37:26:46 | KeyOperationOutput | +| AESGCMEncryption.java:45:22:45:57 | DecryptOperation | AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | AESGCMEncryption.java:37:34:37:36 | Key | AESGCMEncryption.java:39:17:39:21 | Nonce | AESGCMEncryption.java:44:45:44:54 | Message | AESGCMEncryption.java:44:79:44:87 | KeyOperationOutput | +| AESGCMEncryption.java:45:22:45:57 | DecryptOperation | AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | AESGCMEncryption.java:37:34:37:36 | Key | AESGCMEncryption.java:39:17:39:21 | Nonce | AESGCMEncryption.java:44:45:44:54 | Message | AESGCMEncryption.java:45:37:45:45 | KeyOperationOutput | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.ql new file mode 100644 index 000000000000..404acd0071f0 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.ql @@ -0,0 +1,6 @@ +import java +import experimental.quantum.Language + +from Crypto::CipherOperationNode n +select n, n.getAKnownAlgorithm(), n.getAKey(), n.getANonce(), n.getAnInputArtifact(), + n.getAnOutputArtifact() diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/key_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/key_artifacts.expected new file mode 100644 index 000000000000..edb586232564 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/key_artifacts.expected @@ -0,0 +1,4 @@ +| AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | AESCBCEncryption.java:22:50:22:52 | Key | +| AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | AESCBCEncryption.java:41:50:41:52 | Key | +| AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | AESGCMEncryption.java:18:34:18:36 | Key | +| AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | AESGCMEncryption.java:37:34:37:36 | Key | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/options b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/options similarity index 65% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/options rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/options index cb978c1dc068..1f8370de49a0 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/options +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/bcprov-lts8on-2.73.7 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/bcprov-lts8on-2.73.7 diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected new file mode 100644 index 000000000000..45fb4a35d175 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected @@ -0,0 +1,8 @@ +| AESCBCEncryption.java:22:50:22:52 | Key | +| AESCBCEncryption.java:23:66:23:67 | Nonce | +| AESCBCEncryption.java:41:50:41:52 | Key | +| AESCBCEncryption.java:42:66:42:67 | Nonce | +| AESGCMEncryption.java:18:34:18:36 | Key | +| AESGCMEncryption.java:20:17:20:21 | Nonce | +| AESGCMEncryption.java:37:34:37:36 | Key | +| AESGCMEncryption.java:39:17:39:21 | Nonce | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql new file mode 100644 index 000000000000..fe7ab5e433ed --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql @@ -0,0 +1,6 @@ +import java +import experimental.quantum.Language + +from Crypto::ArtifactNode n +where any(SecureRandomnessInstance rng).flowsTo(n.asElement()) +select n diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/ECDSAP256SignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/ECDSAP256SignAndVerify.java similarity index 100% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/ECDSAP256SignAndVerify.java rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/ECDSAP256SignAndVerify.java diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed25519SignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/Ed25519SignAndVerify.java similarity index 100% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed25519SignAndVerify.java rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/Ed25519SignAndVerify.java diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed448SignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/Ed448SignAndVerify.java similarity index 100% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/Ed448SignAndVerify.java rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/Ed448SignAndVerify.java diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/LMSSignature.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/LMSSignature.java similarity index 100% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/LMSSignature.java rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/LMSSignature.java diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected similarity index 100% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.expected rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql similarity index 100% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_artifacts.ql rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected similarity index 100% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.expected rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql similarity index 100% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/key_generation_operations.ql rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/options b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/options new file mode 100644 index 000000000000..1f8370de49a0 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/bcprov-lts8on-2.73.7 diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected similarity index 100% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.expected rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.ql similarity index 100% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/signature_operations.ql rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.ql diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/engines/AESEngine.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/engines/AESEngine.java new file mode 100644 index 000000000000..74b61b2db590 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/engines/AESEngine.java @@ -0,0 +1,17 @@ +package org.bouncycastle.crypto.engines; + +public class AESEngine { + public AESEngine() { } + + public void init(boolean forEncryption, Object params) { } + + public int getBlockSize() { + return 16; // AES block size is 16 bytes + } + + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) { + return getBlockSize(); + } + + public void reset() { } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/modes/CBCBlockCipher.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/modes/CBCBlockCipher.java new file mode 100644 index 000000000000..ea39e5e93cc8 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/modes/CBCBlockCipher.java @@ -0,0 +1,21 @@ +package org.bouncycastle.crypto.modes; + +public class CBCBlockCipher { + private Object cipher; + + public CBCBlockCipher(Object cipher) { + this.cipher = cipher; + } + + public void init(boolean forEncryption, Object params) { } + + public int getBlockSize() { + return 16; // AES block size + } + + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) { + return getBlockSize(); + } + + public void reset() { } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/modes/GCMBlockCipher.java new file mode 100644 index 000000000000..a2a729f73394 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/modes/GCMBlockCipher.java @@ -0,0 +1,29 @@ +package org.bouncycastle.crypto.modes; + +public class GCMBlockCipher { + private Object engine; + + public GCMBlockCipher(Object engine) { + this.engine = engine; + } + + public void init(boolean forEncryption, Object params) { } + + public int getOutputSize(int len) { + return len + 16; // Add space for authentication tag + } + + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) { + return len; + } + + public int doFinal(byte[] out, int outOff) { + return 16; // Return authentication tag size + } + + public void reset() { } + + public int getBlockSize() { + return 16; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PKCS7Padding.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PKCS7Padding.java new file mode 100644 index 000000000000..c342092314f3 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PKCS7Padding.java @@ -0,0 +1,19 @@ +package org.bouncycastle.crypto.paddings; + +public class PKCS7Padding { + public PKCS7Padding() { } + + public void init(Object random) { } + + public String getPaddingName() { + return "PKCS7"; + } + + public int addPadding(byte[] in, int inOff) { + return 0; // Dummy implementation + } + + public int padCount(byte[] in) { + return 0; // Dummy implementation + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java new file mode 100644 index 000000000000..667d5417fcad --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java @@ -0,0 +1,40 @@ +package org.bouncycastle.crypto.paddings; + +public class PaddedBufferedBlockCipher { + private Object cipher; + private Object padding; + + public PaddedBufferedBlockCipher(Object cipher) { + this.cipher = cipher; + this.padding = new PKCS7Padding(); + } + + public PaddedBufferedBlockCipher(Object cipher, Object padding) { + this.cipher = cipher; + this.padding = padding; + } + + public void init(boolean forEncryption, Object params) { } + + public int getBlockSize() { + return 16; + } + + public int getUpdateOutputSize(int len) { + return len + getBlockSize(); + } + + public int getOutputSize(int len) { + return len + getBlockSize(); + } + + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) { + return len; + } + + public int doFinal(byte[] out, int outOff) { + return getBlockSize(); + } + + public void reset() { } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/AEADParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/AEADParameters.java new file mode 100644 index 000000000000..8216a63f5091 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/AEADParameters.java @@ -0,0 +1,35 @@ +package org.bouncycastle.crypto.params; + +public class AEADParameters { + private final KeyParameter key; + private final int macSize; + private final byte[] nonce; + private final byte[] associatedText; + + public AEADParameters(KeyParameter key, int macSize, byte[] nonce) { + this(key, macSize, nonce, null); + } + + public AEADParameters(KeyParameter key, int macSize, byte[] nonce, byte[] associatedText) { + this.key = key; + this.macSize = macSize; + this.nonce = nonce.clone(); + this.associatedText = associatedText != null ? associatedText.clone() : null; + } + + public KeyParameter getKey() { + return key; + } + + public int getMacSize() { + return macSize; + } + + public byte[] getNonce() { + return nonce.clone(); + } + + public byte[] getAssociatedText() { + return associatedText != null ? associatedText.clone() : null; + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/KeyParameter.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/KeyParameter.java new file mode 100644 index 000000000000..53c282c9046f --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/KeyParameter.java @@ -0,0 +1,13 @@ +package org.bouncycastle.crypto.params; + +public class KeyParameter { + private final byte[] key; + + public KeyParameter(byte[] key) { + this.key = key.clone(); + } + + public byte[] getKey() { + return key.clone(); + } +} diff --git a/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ParametersWithIV.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ParametersWithIV.java new file mode 100644 index 000000000000..02391bfb4f06 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ParametersWithIV.java @@ -0,0 +1,19 @@ +package org.bouncycastle.crypto.params; + +public class ParametersWithIV { + private final Object parameters; + private final byte[] iv; + + public ParametersWithIV(Object parameters, byte[] iv) { + this.parameters = parameters; + this.iv = iv.clone(); + } + + public Object getParameters() { + return parameters; + } + + public byte[] getIV() { + return iv.clone(); + } +} From 80a29f91d3c171528b8905232b961e3cdb92299c Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Thu, 12 Jun 2025 17:05:22 +0200 Subject: [PATCH 26/32] Fixed QL for QL findings --- .../lib/experimental/quantum/BouncyCastle.qll | 3 +- .../BouncyCastle/AlgorithmInstances.qll | 64 ++++--------------- .../BouncyCastle/AlgorithmValueConsumers.qll | 6 +- .../quantum/BouncyCastle/FlowAnalysis.qll | 33 +++++----- .../BouncyCastle/OperationInstances.qll | 53 +++++++-------- 5 files changed, 66 insertions(+), 93 deletions(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll index d10b0ffde2e7..66ae785a8275 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -1,2 +1,3 @@ -import java import BouncyCastle.OperationInstances +import BouncyCastle.AlgorithmInstances +import BouncyCastle.AlgorithmValueConsumers diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index 35e8c815e294..33afe330446c 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -1,6 +1,8 @@ -import java -import OperationInstances -import FlowAnalysis +private import java +private import experimental.quantum.Language +private import AlgorithmValueConsumers +private import OperationInstances +private import FlowAnalysis /** * A string literal that represents an elliptic curve name. @@ -194,27 +196,9 @@ class StatefulSignatureAlgorithmInstance extends SignatureAlgorithmInstance inst * A key generation algorithm where the algorithm is implicitly defined by the * type. */ -abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, +abstract class KeyGenerationAlgorithmInstance extends Crypto::AlgorithmInstance, KeyGenerationAlgorithmValueConsumer instanceof ClassInstanceExpr { - override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } - - override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } - - override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } - - override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - generatorNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result) - } - - override int getKeySizeFixed() { - generatorNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) - } - - override string getRawAlgorithmName() { - typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) - } - // Used for data flow from elliptic curve string literals to the algorithm // instance. DataFlow::Node getParametersInput() { none() } @@ -222,6 +206,14 @@ abstract class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgori // Used for data flow from elliptic curve string literals to the algorithm // instance. DataFlow::Node getEllipticCurveInput() { none() } + + string getRawAlgorithmName() { + typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) + } + + int getKeySizeFixed() { + generatorNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) + } } /** @@ -267,22 +259,6 @@ class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAl super.getConstructedType().getName().matches("EC%") } - override string getRawAlgorithmName() { - // TODO: The generator constructs an elliptic curve key pair. The curve used - // is determined using data flow. If this fails we would like to report - // something useful, so we use "UnknownCurve". However, this should probably - // be handled at the node layer. - if exists(this.getConsumedEllipticCurve()) - then result = this.getConsumedEllipticCurve().getRawEllipticCurveName() - else result = "UnknownCurve" - } - - override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - // TODO: There is currently to algorithm type for elliptic curve key - // generation. - result = Crypto::KeyOpAlg::TUnknownKeyOperationAlgorithmType() - } - override Crypto::AlgorithmValueConsumer getEllipticCurveConsumer() { // The elliptic curve is resolved recursively from the parameters passed to // the `init()` call. @@ -308,18 +284,6 @@ class StatefulSignatureKeyGenerationAlgorithmInstance extends KeyGenerationAlgor super.getConstructedType() instanceof Generators::KeyGenerator and super.getConstructedType().getName().matches(["LMS%", "HSS%"]) } - - override string getRawAlgorithmName() { - typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) - } - - override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - super.getConstructedType().getName().matches("LMS%") and - result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::LMS()) - or - super.getConstructedType().getName().matches("HSS%") and - result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::HSS()) - } } /** diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll index a1c9f51cd51b..60d354c6abf9 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll @@ -1,5 +1,7 @@ -import java -import AlgorithmInstances +private import java +private import experimental.quantum.Language +private import AlgorithmValueConsumers +private import AlgorithmInstances abstract class EllipticCurveAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll index 08f2cb8a76c1..5373f10d03a5 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll @@ -1,7 +1,7 @@ -import java -import semmle.code.java.dataflow.DataFlow -import experimental.quantum.Language -import AlgorithmValueConsumers +private import java +private import semmle.code.java.dataflow.DataFlow +private import experimental.quantum.Language +private import AlgorithmValueConsumers /** * A signature for the `getInstance()` method calls used in JCA, or direct @@ -46,24 +46,25 @@ signature class UseCallSig instanceof MethodCall { * ``` */ module NewToInitToUseFlow { - newtype TFlowState = + private newtype TFlowState = TUninitialized() or TInitialized(Init call) or TIntermediateUse(Use call) - abstract class InitFlowState extends TFlowState { + abstract private class InitFlowState extends TFlowState { string toString() { this = TUninitialized() and result = "Uninitialized" or this = TInitialized(_) and result = "Initialized" - // TODO: add intermediate use + or + this = TIntermediateUse(_) and result = "Intermediate" } } // The flow state is uninitialized if the `init` call is not yet made. - class UninitializedFlowState extends InitFlowState, TUninitialized { } + private class UninitializedFlowState extends InitFlowState, TUninitialized { } - class InitializedFlowState extends InitFlowState, TInitialized { + private class InitializedFlowState extends InitFlowState, TInitialized { Init call; DataFlow::Node node1; DataFlow::Node node2; // The receiver of the `init` call @@ -82,7 +83,7 @@ module NewToInitToUseFlow { DataFlow::Node getSndNode() { result = node2 } } - class IntermediateUseState extends InitFlowState, TIntermediateUse { + private class IntermediateUseState extends InitFlowState, TIntermediateUse { Use call; DataFlow::Node node1; // The receiver of the method call DataFlow::Node node2; @@ -101,19 +102,21 @@ module NewToInitToUseFlow { DataFlow::Node getSndNode() { result = node2 } } - module NewToInitToUseConfig implements DataFlow::StateConfigSig { + private module NewToInitToUseConfig implements DataFlow::StateConfigSig { class FlowState = InitFlowState; predicate isSource(DataFlow::Node src, FlowState state) { state instanceof UninitializedFlowState and src.asExpr() instanceof New or + // Needed to determine the init call from a (final) use. src = state.(InitializedFlowState).getSndNode() or + // Needed to determine all intermediate uses from a (final) use. src = state.(IntermediateUseState).getSndNode() } - // TODO: document this, but this is intentional (avoid cross products?) + // TODO: Document this, but this is intentional (to avoid cross products). predicate isSink(DataFlow::Node sink, FlowState state) { none() } predicate isSink(DataFlow::Node sink) { @@ -138,7 +141,7 @@ module NewToInitToUseFlow { predicate isBarrier(DataFlow::Node node, FlowState state) { exists(Init call | node.asExpr() = call.(MethodCall).getQualifier() | // Ensures that the receiver of a call to `init` is tracked as initialized. - state instanceof UninitializedFlowState + not state instanceof InitializedFlowState or // Ensures that call tracked by the state is the last call to `init`. state.(InitializedFlowState).getInitCall() != call @@ -146,7 +149,7 @@ module NewToInitToUseFlow { } } - module NewToInitToUseFlow = DataFlow::GlobalWithState; + private module NewToInitToUseFlow = DataFlow::GlobalWithState; New getNewFromUse(Use use, NewToInitToUseFlow::PathNode src, NewToInitToUseFlow::PathNode sink) { src.getNode().asExpr() = result and @@ -222,7 +225,7 @@ module ParametersToInitFlow { predicate isSink(DataFlow::Node sink) { exists(Init init | sink = init.getParametersInput()) } /** - * A flow step for parameters created from other parameters. + * Holds for parameters created from other parameters. * * As an example, below we want to track the flow from the `X9ECParameters` * constructor call to the `keyPairGenerator.init()` call to be able to diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll index 9826068493f9..c30024b1ddd4 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll @@ -1,9 +1,9 @@ -import java +private import java +private import experimental.quantum.Language +private import FlowAnalysis +private import AlgorithmInstances module Params { - import FlowAnalysis - import AlgorithmInstances - /** * A model of the `Parameters` class in Bouncy Castle. */ @@ -17,15 +17,15 @@ module Params { class Curve extends Class { Curve() { - this.getPackage().getName() = "org.bouncycastle.math.ec" and - this.getName().matches("ECCurve") + this.getPackage().hasName("org.bouncycastle.math.ec") and + this.hasName("ECCurve") } } class KeyParameters extends Parameters { KeyParameters() { - this.getPackage().getName() = - ["org.bouncycastle.crypto.params", "org.bouncycastle.pqc.crypto.lms"] and + this.getPackage() + .hasName(["org.bouncycastle.crypto.params", "org.bouncycastle.pqc.crypto.lms"]) and this.getName().matches(["%KeyParameter", "%KeyParameters"]) } } @@ -95,7 +95,7 @@ module Params { } /** - * The named elliptic curve passed to `X9ECParameters.getCurve()`. + * A named elliptic curve passed to `X9ECParameters.getCurve()`. */ class X9ECParametersInstantiation extends ParametersInstantiation { X9ECParametersInstantiation() { this.(Expr).getType().getName() = "X9ECParameters" } @@ -108,7 +108,7 @@ module Params { } /** - * The named elliptic curve passed to `ECNamedCurveTable.getParameterSpec()`. + * A named elliptic curve passed to `ECNamedCurveTable.getParameterSpec()`. */ class ECNamedCurveParameterSpecInstantiation extends ParametersInstantiation { ECNamedCurveParameterSpecInstantiation() { @@ -135,6 +135,11 @@ module Params { override Expr getNonceArg() { result = this.(ConstructorCall).getArgument(2) } } + /** + * A `ParametersWithIV` instantiation. + * + * This type is used to model data flow from a nonce to a cipher operation. + */ class ParametersWithIvInstantiation extends ParametersInstantiation { ParametersWithIvInstantiation() { this.(ConstructorCall).getConstructedType().getName() = "ParametersWithIV" @@ -161,9 +166,6 @@ module Params { * Models for the signature algorithms defined by the `org.bouncycastle.crypto.signers` package. */ module Signers { - import FlowAnalysis - import AlgorithmInstances - /** * A model of the `Signer` class in Bouncy Castle. * @@ -207,8 +209,8 @@ module Signers { } /** - * This class represents signers with a one shot API (where the entire message - * is passed to either `generateSignature()` or `verifySignature`.). + * A signer with a one shot API (where the entire message is passed to either + * `generateSignature()` or `verifySignature`.). */ class OneShotSigner extends Signer { OneShotSigner() { this.getName().matches(["DSASigner", "ECDSA%", "LMS%", "HSS%"]) } @@ -335,9 +337,6 @@ module Signers { * Models for the key generation algorithms defined by the `org.bouncycastle.crypto.generators` package. */ module Generators { - import FlowAnalysis - import AlgorithmInstances - /** * A model of the `KeyGenerator` and `KeyPairGenerator` classes in Bouncy Castle. */ @@ -366,6 +365,8 @@ module Generators { } /** + * An asymmetric key pair. + * * This type is used to model data flow from a key pair to the private and * public components of the key pair. */ @@ -391,13 +392,13 @@ module Generators { private class KeyGeneratorNewCall = KeyGenerationAlgorithmInstance; /** + * A call to a key generator `init()` method. + * * The type is instantiated by a constructor call and initialized by a call to * `init()` which takes a single `KeyGenerationParameters` argument. */ private class KeyGeneratorInitCall extends MethodCall { - KeyGenerator gen; - - KeyGeneratorInitCall() { this = gen.getAnInitCall() } + KeyGeneratorInitCall() { this = any(KeyGenerator gen).getAnInitCall() } Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } @@ -435,7 +436,8 @@ module Generators { class KeyGenerationOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGeneratorUseCall { override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - // The algorithm is implicitly defined by the key generator type + // The algorithm is implicitly defined by the key generator type, which is + // determined by the constructor call. result = KeyGeneratorFlow::getNewFromUse(this, _, _) } @@ -529,6 +531,9 @@ module Modes { } } + /** + * A block cipher engine, like `AESEngine`. + */ class BlockCipher extends Class { BlockCipher() { this.getPackage().getName() = "org.bouncycastle.crypto.engines" and @@ -605,9 +610,7 @@ module Modes { * decrypt data. */ private class BlockCipherModeUseCall extends MethodCall { - BlockCipherMode mode; - - BlockCipherModeUseCall() { this = mode.getAUseCall() } + BlockCipherModeUseCall() { this = any(BlockCipherMode mode).getAUseCall() } predicate isIntermediate() { not this.getCallee().getName() = "doFinal" } From 1e5bb5ff14fb18a169f7306ac3a356332a1b9210 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Mon, 16 Jun 2025 11:34:01 +0200 Subject: [PATCH 27/32] Updated tests to pass with new key generation modeling --- .../modes/random_artifacts.expected | 16 ++++++++-------- .../BouncyCastle/modes/random_artifacts.ql | 4 ++-- .../signers/key_artifacts.expected | 18 ++++++++++++------ .../BouncyCastle/signers/key_artifacts.ql | 2 +- .../signers/key_generation_operations.expected | 12 ++++++------ .../signers/key_generation_operations.ql | 2 +- 6 files changed, 30 insertions(+), 24 deletions(-) diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected index 45fb4a35d175..873907aec4a9 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected @@ -1,8 +1,8 @@ -| AESCBCEncryption.java:22:50:22:52 | Key | -| AESCBCEncryption.java:23:66:23:67 | Nonce | -| AESCBCEncryption.java:41:50:41:52 | Key | -| AESCBCEncryption.java:42:66:42:67 | Nonce | -| AESGCMEncryption.java:18:34:18:36 | Key | -| AESGCMEncryption.java:20:17:20:21 | Nonce | -| AESGCMEncryption.java:37:34:37:36 | Key | -| AESGCMEncryption.java:39:17:39:21 | Nonce | +| AESCBCEncryption.java:22:50:22:52 | Key | AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | +| AESCBCEncryption.java:23:66:23:67 | Nonce | AESCBCEncryption.java:62:30:62:31 | RandomNumberGeneration | +| AESCBCEncryption.java:41:50:41:52 | Key | AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | +| AESCBCEncryption.java:42:66:42:67 | Nonce | AESCBCEncryption.java:62:30:62:31 | RandomNumberGeneration | +| AESGCMEncryption.java:18:34:18:36 | Key | AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | +| AESGCMEncryption.java:20:17:20:21 | Nonce | AESGCMEncryption.java:60:30:60:34 | RandomNumberGeneration | +| AESGCMEncryption.java:37:34:37:36 | Key | AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | +| AESGCMEncryption.java:39:17:39:21 | Nonce | AESGCMEncryption.java:60:30:60:34 | RandomNumberGeneration | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql index fe7ab5e433ed..3f27ed82e7a5 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql @@ -2,5 +2,5 @@ import java import experimental.quantum.Language from Crypto::ArtifactNode n -where any(SecureRandomnessInstance rng).flowsTo(n.asElement()) -select n +where n.getSourceNode() instanceof Crypto::RandomNumberGenerationNode +select n, n.getSourceNode() diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected index 8e769550c832..32991463ae7c 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected @@ -1,6 +1,12 @@ -| ECDSAP256SignAndVerify.java:57:16:57:49 | Key | Unknown | -| ECDSAP256SignAndVerify.java:80:16:80:49 | Key | Unknown | -| ECDSAP256SignAndVerify.java:103:16:103:49 | Key | Unknown | -| Ed448SignAndVerify.java:21:47:21:80 | Key | Ed448 | -| Ed25519SignAndVerify.java:21:47:21:80 | Key | Ed25519 | -| LMSSignature.java:33:47:33:74 | Key | LMS | +| ECDSAP256SignAndVerify.java:115:27:115:36 | Key | ECDSAP256SignAndVerify.java:57:16:57:49 | Key | +| ECDSAP256SignAndVerify.java:115:27:115:36 | Key | ECDSAP256SignAndVerify.java:80:16:80:49 | Key | +| ECDSAP256SignAndVerify.java:115:27:115:36 | Key | ECDSAP256SignAndVerify.java:103:16:103:49 | Key | +| ECDSAP256SignAndVerify.java:120:30:120:38 | Key | ECDSAP256SignAndVerify.java:57:16:57:49 | Key | +| ECDSAP256SignAndVerify.java:120:30:120:38 | Key | ECDSAP256SignAndVerify.java:80:16:80:49 | Key | +| ECDSAP256SignAndVerify.java:120:30:120:38 | Key | ECDSAP256SignAndVerify.java:103:16:103:49 | Key | +| Ed448SignAndVerify.java:30:31:30:40 | Key | Ed448SignAndVerify.java:21:47:21:80 | Key | +| Ed448SignAndVerify.java:38:34:38:42 | Key | Ed448SignAndVerify.java:21:47:21:80 | Key | +| Ed25519SignAndVerify.java:30:31:30:40 | Key | Ed25519SignAndVerify.java:21:47:21:80 | Key | +| Ed25519SignAndVerify.java:38:34:38:42 | Key | Ed25519SignAndVerify.java:21:47:21:80 | Key | +| LMSSignature.java:43:31:43:40 | Key | LMSSignature.java:33:47:33:74 | Key | +| LMSSignature.java:50:32:50:40 | Key | LMSSignature.java:33:47:33:74 | Key | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql index 394c82d70fce..13afa649dd7e 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql @@ -2,4 +2,4 @@ import java import experimental.quantum.Language from Crypto::KeyArtifactNode n -select n, n.getAKnownAlgorithm() +select n, n.getSourceNode() diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected index 70b5996faabc..1915c8ed102b 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected @@ -1,6 +1,6 @@ -| ECDSAP256SignAndVerify.java:57:16:57:49 | KeyGeneration | ECDSAP256SignAndVerify.java:53:47:53:70 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:57:16:57:49 | Key | -| ECDSAP256SignAndVerify.java:80:16:80:49 | KeyGeneration | ECDSAP256SignAndVerify.java:76:47:76:70 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:80:16:80:49 | Key | -| ECDSAP256SignAndVerify.java:103:16:103:49 | KeyGeneration | ECDSAP256SignAndVerify.java:99:47:99:70 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:103:16:103:49 | Key | -| Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | Ed448SignAndVerify.java:21:47:21:80 | Key | -| Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:21:47:21:80 | Key | -| LMSSignature.java:33:47:33:74 | KeyGeneration | LMSSignature.java:31:46:31:70 | KeyOperationAlgorithm | LMSSignature.java:33:47:33:74 | Key | +| ECDSAP256SignAndVerify.java:57:16:57:49 | KeyGeneration | ECDSAP256SignAndVerify.java:57:16:57:49 | Key | +| ECDSAP256SignAndVerify.java:80:16:80:49 | KeyGeneration | ECDSAP256SignAndVerify.java:80:16:80:49 | Key | +| ECDSAP256SignAndVerify.java:103:16:103:49 | KeyGeneration | ECDSAP256SignAndVerify.java:103:16:103:49 | Key | +| Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed448SignAndVerify.java:21:47:21:80 | Key | +| Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed25519SignAndVerify.java:21:47:21:80 | Key | +| LMSSignature.java:33:47:33:74 | KeyGeneration | LMSSignature.java:33:47:33:74 | Key | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql index 30447906aed0..e361b2dc2b35 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql @@ -2,4 +2,4 @@ import java import experimental.quantum.Language from Crypto::KeyGenerationOperationNode n -select n, n.getAKnownAlgorithm(), n.getOutputKeyArtifact() +select n, n.getOutputKeyArtifact() From 357ae9206add4093c484c02ca9cefdebdc5cd0b6 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Thu, 19 Jun 2025 10:27:51 +0200 Subject: [PATCH 28/32] Updated Bouncy Castle algorithm instances - Signature operations are now handled by a single algorithm instance - All key generation operations except generic EC key generation operations are now handled by a single algorithm instance - Ed25519 and Ed448 key generation have the algorithm set to Ed25519 and Ed448 respectively - For generic EC key generation operations the algorithm is given by the corresponding curve (since these could be used for either ECDSA or ECDH) --- .../BouncyCastle/AlgorithmInstances.qll | 316 +++++------------- .../BouncyCastle/AlgorithmValueConsumers.qll | 36 +- .../quantum/BouncyCastle/FlowAnalysis.qll | 4 +- .../BouncyCastle/OperationInstances.qll | 57 +++- ...AndVerify.java => ECDSASignAndVerify.java} | 4 +- ...MSSignature.java => LMSSignAndVerify.java} | 13 +- .../signers/key_artifacts.expected | 17 +- .../BouncyCastle/signers/key_artifacts.ql | 2 +- .../key_generation_operations.expected | 11 +- .../signers/key_generation_operations.ql | 2 +- .../signers/signature_operations.expected | 9 +- 11 files changed, 163 insertions(+), 308 deletions(-) rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/{ECDSAP256SignAndVerify.java => ECDSASignAndVerify.java} (97%) rename java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/{LMSSignature.java => LMSSignAndVerify.java} (86%) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll index 33afe330446c..cac8ff02afa2 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -10,12 +10,12 @@ private import FlowAnalysis class EllipticCurveStringLiteralInstance extends Crypto::EllipticCurveInstance instanceof StringLiteral { EllipticCurveStringLiteralInstance() { - Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getValue().toUpperCase(), _, _) + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(super.getValue().toUpperCase(), _, _) } override string getRawEllipticCurveName() { result = super.getValue() } - EllipticCurveAlgorithmValueConsumer getConsumer() { + Crypto::AlgorithmValueConsumer getConsumer() { result = EllipticCurveStringLiteralToConsumerFlow::getConsumerFromLiteral(this, _, _) } @@ -31,31 +31,14 @@ class EllipticCurveStringLiteralInstance extends Crypto::EllipticCurveInstance i } /** - * An elliptic curve algorithm where the elliptic curve is implicitly defined by - * the underlying type. + * A signature algorithm instance where the algorithm is implicitly defined by + * the constructed type. */ -abstract class KnownEllipticCurveInstance extends Crypto::EllipticCurveInstance, - Crypto::EllipticCurveConsumingAlgorithmInstance, Crypto::AlgorithmValueConsumer instanceof ClassInstanceExpr +class ImplicitSignatureClassInstanceExpr extends Crypto::KeyOperationAlgorithmInstance, + ImplicitAlgorithmValueConsumer instanceof ClassInstanceExpr { - override Crypto::TEllipticCurveType getEllipticCurveType() { - Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName().toUpperCase(), - _, result) - } - - override int getKeySize() { - Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName().toUpperCase(), - result, _) - } - - override Crypto::AlgorithmValueConsumer getEllipticCurveConsumer() { result = this } -} + ImplicitSignatureClassInstanceExpr() { super.getConstructedType() instanceof Signers::Signer } -/** - * A signature algorithm where the algorithm is implicitly defined by the type. - */ -abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, - SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr -{ override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } @@ -63,11 +46,11 @@ abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmI override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - signatureNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result) + signatureNameToAlgorithmMapping(this.getRawAlgorithmName(), result) } override int getKeySizeFixed() { - signatureNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) + signatureNameToKeySizeMapping(this.getRawAlgorithmName(), result) } override string getRawAlgorithmName() { @@ -75,130 +58,44 @@ abstract class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmI } // Used for data flow from elliptic curve string literals to the algorithm + // instance. DataFlow::Node getParametersInput() { none() } // Used for data flow from elliptic curve string literals to the algorithm + // instance. DataFlow::Node getEllipticCurveInput() { none() } } /** - * An elliptic curve signature algorithm where both the signature algorithm and - * elliptic curve are implicitly defined by the underlying type. + * A key generation algorithm instance where algorithm is a key operation (e.g. + * a signature algorithm) implicitly defined by the constructed type. */ -abstract class KnownEllipticCurveSignatureAlgorithmInstance extends KnownEllipticCurveInstance, - SignatureAlgorithmInstance +class ImplicitKeyGenerationClassInstanceExpr extends Crypto::KeyOperationAlgorithmInstance, + ImplicitAlgorithmValueConsumer instanceof ClassInstanceExpr { - override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } -} - -/** - * A DSA or DSADigest signer. - */ -class DsaSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr { - DsaSignatureAlgorithmInstance() { - super.getConstructedType() instanceof Signers::Signer and - super.getConstructedType().getName().matches("DSA%") - } - - override string getRawAlgorithmName() { - typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) - } -} - -/** - * An Ed25519, Ed25519ph, or Ed25519ctx signer. - */ -class Ed25519SignatureAlgorithmInstance extends KnownEllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr -{ - Ed25519SignatureAlgorithmInstance() { - super.getConstructedType() instanceof Signers::Signer and - super.getConstructedType().getName().matches("Ed25519%") - } - - override string getRawAlgorithmName() { - typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) - } - - override string getRawEllipticCurveName() { result = "Curve25519" } -} - -/** - * An Ed448 or Ed448ph signer. - */ -class Ed448SignatureAlgorithmInstance extends KnownEllipticCurveSignatureAlgorithmInstance instanceof ClassInstanceExpr -{ - Ed448SignatureAlgorithmInstance() { - super.getConstructedType() instanceof Signers::Signer and - super.getConstructedType().getName().matches("Ed448%") + ImplicitKeyGenerationClassInstanceExpr() { + super.getConstructedType() instanceof Generators::KeyGenerator and + super.getConstructedType().getName().matches(["Ed25519%", "Ed448%", "LMS%", "HSS%"]) } override string getRawAlgorithmName() { typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) } - override string getRawEllipticCurveName() { result = "Curve448" } -} + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } -/** - * An ECDSA signer. - * - * ECDSA curve parameters can be set in at least five ways: - * - By using the `ECDomainParameters` class, which is passed to the constructor of the signer. - * - By using the `ECNamedDomainParameters` class, which is passed to the constructor of the signer. - * - By using the `ECNamedCurveTable` class, which is used to obtain the curve parameters. - * - By using the `ECNamedCurveSpec` class, which is passed to the constructor of the signer. - * - By using the `ECParameterSpec` class, which is passed to the constructor of the signer. - */ -class EcdsaSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr -{ - EcdsaSignatureAlgorithmInstance() { - super.getConstructedType() instanceof Signers::OneShotSigner and - super.getConstructedType().getName().matches("ECDSA%") - } + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } - override string getRawAlgorithmName() { - typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) - } + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::ECDSA()) - } - - override int getKeySizeFixed() { none() } -} - -/** - * An LMS or HSS stateful, hash-based signer. - */ -class StatefulSignatureAlgorithmInstance extends SignatureAlgorithmInstance instanceof ClassInstanceExpr -{ - StatefulSignatureAlgorithmInstance() { - super.getConstructedType() instanceof Signers::Signer and - super.getConstructedType().getName().matches(["LMS%", "HSS%"]) - } - - override string getRawAlgorithmName() { - typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) + generatorNameToAlgorithmMapping(this.getRawAlgorithmName(), result) } - override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { - super.getConstructedType().getName().matches("LMS%") and - result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::LMS()) - or - super.getConstructedType().getName().matches("HSS%") and - result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::HSS()) + override int getKeySizeFixed() { + generatorNameToKeySizeMapping(this.getRawAlgorithmName(), result) } -} -/** - * A key generation algorithm where the algorithm is implicitly defined by the - * type. - */ -abstract class KeyGenerationAlgorithmInstance extends Crypto::AlgorithmInstance, - KeyGenerationAlgorithmValueConsumer instanceof ClassInstanceExpr -{ // Used for data flow from elliptic curve string literals to the algorithm // instance. DataFlow::Node getParametersInput() { none() } @@ -206,84 +103,6 @@ abstract class KeyGenerationAlgorithmInstance extends Crypto::AlgorithmInstance, // Used for data flow from elliptic curve string literals to the algorithm // instance. DataFlow::Node getEllipticCurveInput() { none() } - - string getRawAlgorithmName() { - typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) - } - - int getKeySizeFixed() { - generatorNameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _) - } -} - -/** - * An elliptic curve key generation algorithm where both the key generation - * algorithm and elliptic curve are implicitly defined by the underlying type. - */ -abstract class KnownEllipticCurveKeyGenerationAlgorithmInstance extends KnownEllipticCurveInstance, - KeyGenerationAlgorithmInstance -{ - override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } - - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } -} - -class Ed25519KeyGenerationAlgorithmInstance extends KnownEllipticCurveKeyGenerationAlgorithmInstance instanceof ClassInstanceExpr -{ - Ed25519KeyGenerationAlgorithmInstance() { - super.getConstructedType() instanceof Generators::KeyGenerator and - super.getConstructedType().getName().matches("Ed25519%") - } - - override string getRawEllipticCurveName() { result = "Curve25519" } -} - -class Ed448KeyGenerationAlgorithmInstance extends KnownEllipticCurveKeyGenerationAlgorithmInstance instanceof ClassInstanceExpr -{ - Ed448KeyGenerationAlgorithmInstance() { - super.getConstructedType() instanceof Generators::KeyGenerator and - super.getConstructedType().getName().matches("Ed448%") - } - - override string getRawEllipticCurveName() { result = "Curve448" } -} - -/** - * A generic `ECKeyPairGenerator` instance. - */ -class GenericEllipticCurveKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance, - Crypto::EllipticCurveConsumingAlgorithmInstance instanceof ClassInstanceExpr -{ - GenericEllipticCurveKeyGenerationAlgorithmInstance() { - super.getConstructedType() instanceof Generators::KeyGenerator and - super.getConstructedType().getName().matches("EC%") - } - - override Crypto::AlgorithmValueConsumer getEllipticCurveConsumer() { - // The elliptic curve is resolved recursively from the parameters passed to - // the `init()` call. - exists(MethodCall init | - init = Generators::KeyGeneratorFlow::getInitFromNew(this, _, _) and - result = - Generators::ParametersFlow::getParametersFromInit(init, _, _).getAnAlgorithmValueConsumer() - ) - } - - Crypto::EllipticCurveInstance getConsumedEllipticCurve() { - result = this.getEllipticCurveConsumer().getAKnownAlgorithmSource() - } -} - -/** - * An LMS or HSS key generation instances. The algorithm is implicitly defined - * by the type. - */ -class StatefulSignatureKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance instanceof ClassInstanceExpr -{ - StatefulSignatureKeyGenerationAlgorithmInstance() { - super.getConstructedType() instanceof Generators::KeyGenerator and - super.getConstructedType().getName().matches(["LMS%", "HSS%"]) - } } /** @@ -335,7 +154,7 @@ class BlockCipherAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance * A block cipher mode instance. */ class BlockCipherModeAlgorithmInstance extends Crypto::ModeOfOperationAlgorithmInstance, - BlockCipherModeAlgorithmValueConsumer instanceof ClassInstanceExpr + ImplicitAlgorithmValueConsumer instanceof ClassInstanceExpr { BlockCipherModeAlgorithmInstance() { super.getConstructedType() instanceof Modes::UnpaddedBlockCipherMode @@ -395,6 +214,10 @@ private predicate typeNameToRawAlgorithmNameMapping(string typeName, string algo typeName.matches("ECDSA%") and algorithmName = "ECDSA" or + // DSA + typeName.matches("DSA%") and + algorithmName = "DSA" + or // LMS typeName.matches("LMS%") and algorithmName = "LMS" @@ -464,45 +287,72 @@ private predicate paddingNameToTypeMapping(string paddingName, Crypto::TPaddingT paddingType = Crypto::OtherPadding() } -private predicate signatureNameToKeySizeAndAlgorithmMapping( - string name, int keySize, Crypto::KeyOpAlg::Algorithm algorithm +private predicate signatureNameToAlgorithmMapping( + string signatureName, Crypto::KeyOpAlg::Algorithm algorithmType ) { - name = "Ed25519" and - keySize = 256 and - algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) + signatureName = "Ed25519" and + algorithmType = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) or - name = "Ed448" and - keySize = 448 and - algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) + signatureName = "Ed448" and + algorithmType = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) + or + signatureName = "ECDSA" and + algorithmType = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::ECDSA()) + or + signatureName = "LMS" and + algorithmType = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::LMS()) + or + signatureName = "HSS" and + algorithmType = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::HSS()) } -private predicate generatorNameToKeySizeAndAlgorithmMapping( - string name, int keySize, Crypto::KeyOpAlg::Algorithm algorithm +private predicate signatureNameToKeySizeMapping(string signatureName, int keySize) { + signatureName = "Ed25519" and + keySize = 256 + or + signatureName = "Ed448" and + keySize = 448 +} + +private predicate generatorNameToAlgorithmMapping( + string generatorName, Crypto::KeyOpAlg::Algorithm algorithmType ) { - name = "Ed25519" and - keySize = 256 and - algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) + generatorName = "Ed25519" and + algorithmType = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) + or + generatorName = "Ed448" and + algorithmType = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) + or + generatorName = "LMS" and + algorithmType = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::LMS()) + or + generatorName = "HSS" and + algorithmType = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::HSS()) +} + +private predicate generatorNameToKeySizeMapping(string generatorName, int keySize) { + generatorName = "Ed25519" and + keySize = 256 or - name = "Ed448" and - keySize = 448 and - algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448()) + generatorName = "Ed448" and + keySize = 448 } private predicate blockCipherNameToAlgorithmMapping( - string name, Crypto::KeyOpAlg::Algorithm algorithm + string cipherName, Crypto::KeyOpAlg::Algorithm algorithmType ) { - name = "AES" and - algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES()) + cipherName = "AES" and + algorithmType = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES()) or - name = "Aria" and - algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::ARIA()) + cipherName = "Aria" and + algorithmType = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::ARIA()) or - name = "Blowfish" and - algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::BLOWFISH()) + cipherName = "Blowfish" and + algorithmType = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::BLOWFISH()) or - name = "DES" and - algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES()) + cipherName = "DES" and + algorithmType = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES()) or - name = "TripleDES" and - algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::TripleDES()) + cipherName = "TripleDES" and + algorithmType = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::TripleDES()) } diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll index 60d354c6abf9..b8bc22b6dfac 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll @@ -22,30 +22,11 @@ class EllipticCurveStringLiteralArg extends EllipticCurveAlgorithmValueConsumer } /** - * An AVC for a signature algorithm where the algorithm is implicitly defined by - * the constructor. + * An AVC representing the block cipher argument passed to an block cipher mode + * constructor. */ -abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } - - override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } -} - -/** - * An AVC for a key generation algorithm where the algorithm is implicitly - * defined by the constructor. - */ -abstract class KeyGenerationAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { - override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } - - override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } -} - -/** - * A block cipher argument passed to an block cipher mode constructor. - */ -class BlockCipherAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof Expr { - BlockCipherAlgorithmValueConsumer() { +class BlockCipherAlgorithmArg extends Crypto::AlgorithmValueConsumer instanceof Expr { + BlockCipherAlgorithmArg() { this = any(BlockCipherModeAlgorithmInstance mode).getBlockCipherArg() } @@ -57,14 +38,9 @@ class BlockCipherAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer i } /** - * An AVC for a block cipher mode implicitly defined by the constructor. + * An AVC for an algorithm that is implicitly defined by the instance. */ -abstract class BlockCipherModeAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof ClassInstanceExpr -{ - BlockCipherModeAlgorithmValueConsumer() { - this.getType() instanceof Modes::UnpaddedBlockCipherMode - } - +abstract class ImplicitAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll index 5373f10d03a5..40865ac601c5 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll @@ -362,7 +362,7 @@ module EllipticCurveStringLiteralToConsumerFlow { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof StringLiteral } predicate isSink(DataFlow::Node sink) { - exists(EllipticCurveAlgorithmValueConsumer consumer | sink = consumer.getInputNode()) + exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode()) } predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { @@ -374,7 +374,7 @@ module EllipticCurveStringLiteralToConsumerFlow { private module EllipticCurveStringLiteralToAlgorithmValueConsumerFlow = DataFlow::Global; - EllipticCurveAlgorithmValueConsumer getConsumerFromLiteral( + Crypto::AlgorithmValueConsumer getConsumerFromLiteral( StringLiteral literal, EllipticCurveStringLiteralToAlgorithmValueConsumerFlow::PathNode src, EllipticCurveStringLiteralToAlgorithmValueConsumerFlow::PathNode sink ) { diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll index c30024b1ddd4..16aa89c79293 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll @@ -233,7 +233,7 @@ module Signers { * BouncyCastle algorithms are instantiated by calling the constructor of the * corresponding class, which also represents the algorithm instance. */ - private class SignerNewCall = SignatureAlgorithmInstance; + private class SignerNewCall = ImplicitSignatureClassInstanceExpr; /** * The type is instantiated by a constructor call and initialized by a call to @@ -389,7 +389,17 @@ module Generators { * BouncyCastle algorithms are instantiated by calling the constructor of the * corresponding class, which also represents the algorithm instance. */ - private class KeyGeneratorNewCall = KeyGenerationAlgorithmInstance; + private class KeyGeneratorNewCall extends ClassInstanceExpr { + KeyGeneratorNewCall() { this.getConstructedType() instanceof KeyGenerator } + + // Used for data flow from elliptic curve string literals to the algorithm + // instance. + DataFlow::Node getParametersInput() { none() } + + // Used for data flow from elliptic curve string literals to the algorithm + // instance. + DataFlow::Node getEllipticCurveInput() { none() } + } /** * A call to a key generator `init()` method. @@ -414,7 +424,8 @@ module Generators { KeyGeneratorUseCall() { this = gen.getAUseCall() } - // Since key generators don't have `update()` methods, this is always false. + // This is used to define the `NewToInitToUse` data flow. Since key + // generators don't have `update()` methods, this is always false. predicate isIntermediate() { none() } Crypto::KeyArtifactType getKeyType() { result = gen.getKeyType() } @@ -422,10 +433,10 @@ module Generators { Expr getOutput() { result = this } } - module KeyGeneratorFlow = + private module KeyGeneratorFlow = NewToInitToUseFlow; - module ParametersFlow = + private module ParametersFlow = ParametersToInitFlow; /** @@ -436,8 +447,12 @@ module Generators { class KeyGenerationOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGeneratorUseCall { override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - // The algorithm is implicitly defined by the key generator type, which is - // determined by the constructor call. + // For most Bouncy Castle key generators, the algorithm is implicitly + // defined by the type, and so we use the constructor call to represent + // the AVC. + // + // TODO: Avoid using data flow for this and use the operation instance to + // represent the AVC and algorithm instance whenever it makes sense. result = KeyGeneratorFlow::getNewFromUse(this, _, _) } @@ -448,7 +463,14 @@ module Generators { override Crypto::KeyArtifactType getOutputKeyType() { result = super.getKeyType() } override int getKeySizeFixed() { - result = KeyGeneratorFlow::getNewFromUse(this, _, _).getKeySizeFixed() + // Either the algorithm is a key operation algorithm or an elliptic curve. + // For elliptic curve, the key size depends on the curve, so we don't + // return anything. + result = + this.getAnAlgorithmValueConsumer() + .getAKnownAlgorithmSource() + .(Crypto::KeyOperationAlgorithmInstance) + .getKeySizeFixed() } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { @@ -462,6 +484,25 @@ module Generators { ) } } + + /** + * A generic elliptic curve key generation operation using `ECKeyPairGenerator`. + */ + class EllipticCurveKeyGenerationOperationInstance extends KeyGenerationOperationInstance instanceof KeyGeneratorUseCall + { + EllipticCurveKeyGenerationOperationInstance() { + super.getQualifier().getType().getName().matches("EC%") + } + + // We attempt to resolve the elliptic curve algorithm from the + // `KeyGenerationParameters` argument to `init()`. + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + exists(KeyGeneratorInitCall init | + init = KeyGeneratorFlow::getInitFromUse(this, _, _) and + result = ParametersFlow::getParametersFromInit(init, _, _).getAnAlgorithmValueConsumer() + ) + } + } } module Modes { diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/ECDSAP256SignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/ECDSASignAndVerify.java similarity index 97% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/ECDSAP256SignAndVerify.java rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/ECDSASignAndVerify.java index 1f66d708c3b7..79c0c99a20de 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/ECDSAP256SignAndVerify.java +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/ECDSASignAndVerify.java @@ -21,14 +21,14 @@ /** * Test Bouncy Castle's low-level ECDSA API */ -public class ECDSAP256SignAndVerify { +public class ECDSASignAndVerify { public static void main(String[] args) { // Add Bouncy Castle provider Security.addProvider(new BouncyCastleProvider()); try { - byte[] message = "Hello, ECDSA P-256 signature!".getBytes("UTF-8"); + byte[] message = "Hello, ECDSA signature!".getBytes("UTF-8"); // Test different key generation methods signWithKeyPair(generateKeyPair1(), message); diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/LMSSignature.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/LMSSignAndVerify.java similarity index 86% rename from java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/LMSSignature.java rename to java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/LMSSignAndVerify.java index 83312cd73dab..a99c4516aab5 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/LMSSignature.java +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/LMSSignAndVerify.java @@ -16,7 +16,7 @@ * low-level API. * */ -public class LMSSignature { +public class LMSSignAndVerify { public static void main(String[] args) { Security.addProvider(new BouncyCastleProvider()); @@ -37,18 +37,15 @@ public static void main(String[] args) { byte[] message = "Hello, LMS signature!".getBytes("UTF-8"); - LMSSigner signer = new LMSSigner(); - // Sign the message + LMSSigner signer = new LMSSigner(); signer.init(true, privateKey); // true for signing byte[] signature = signer.generateSignature(message); // Verify the signature - // - // TODO: Using the same signer instance for verification causes both - // keys to be reported. This should be handled using dataflow. - signer.init(false, publicKey); - boolean verified = signer.verifySignature(message, signature); + LMSSigner verifier = new LMSSigner(); + verifier.init(false, publicKey); + boolean verified = verifier.verifySignature(message, signature); System.out.println("Signature verified: " + verified); } catch (Exception e) { diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected index 32991463ae7c..213d8d1baaed 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected @@ -1,12 +1,5 @@ -| ECDSAP256SignAndVerify.java:115:27:115:36 | Key | ECDSAP256SignAndVerify.java:57:16:57:49 | Key | -| ECDSAP256SignAndVerify.java:115:27:115:36 | Key | ECDSAP256SignAndVerify.java:80:16:80:49 | Key | -| ECDSAP256SignAndVerify.java:115:27:115:36 | Key | ECDSAP256SignAndVerify.java:103:16:103:49 | Key | -| ECDSAP256SignAndVerify.java:120:30:120:38 | Key | ECDSAP256SignAndVerify.java:57:16:57:49 | Key | -| ECDSAP256SignAndVerify.java:120:30:120:38 | Key | ECDSAP256SignAndVerify.java:80:16:80:49 | Key | -| ECDSAP256SignAndVerify.java:120:30:120:38 | Key | ECDSAP256SignAndVerify.java:103:16:103:49 | Key | -| Ed448SignAndVerify.java:30:31:30:40 | Key | Ed448SignAndVerify.java:21:47:21:80 | Key | -| Ed448SignAndVerify.java:38:34:38:42 | Key | Ed448SignAndVerify.java:21:47:21:80 | Key | -| Ed25519SignAndVerify.java:30:31:30:40 | Key | Ed25519SignAndVerify.java:21:47:21:80 | Key | -| Ed25519SignAndVerify.java:38:34:38:42 | Key | Ed25519SignAndVerify.java:21:47:21:80 | Key | -| LMSSignature.java:43:31:43:40 | Key | LMSSignature.java:33:47:33:74 | Key | -| LMSSignature.java:50:32:50:40 | Key | LMSSignature.java:33:47:33:74 | Key | +| ECDSASignAndVerify.java:57:16:57:49 | Key | secp256r1 | +| ECDSASignAndVerify.java:80:16:80:49 | Key | secp256k1 | +| Ed448SignAndVerify.java:21:47:21:80 | Key | Ed448 | +| Ed25519SignAndVerify.java:21:47:21:80 | Key | Ed25519 | +| LMSSignAndVerify.java:33:47:33:74 | Key | LMS | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql index 13afa649dd7e..394c82d70fce 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql @@ -2,4 +2,4 @@ import java import experimental.quantum.Language from Crypto::KeyArtifactNode n -select n, n.getSourceNode() +select n, n.getAKnownAlgorithm() diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected index 1915c8ed102b..a07e0f8d8abb 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected @@ -1,6 +1,5 @@ -| ECDSAP256SignAndVerify.java:57:16:57:49 | KeyGeneration | ECDSAP256SignAndVerify.java:57:16:57:49 | Key | -| ECDSAP256SignAndVerify.java:80:16:80:49 | KeyGeneration | ECDSAP256SignAndVerify.java:80:16:80:49 | Key | -| ECDSAP256SignAndVerify.java:103:16:103:49 | KeyGeneration | ECDSAP256SignAndVerify.java:103:16:103:49 | Key | -| Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed448SignAndVerify.java:21:47:21:80 | Key | -| Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed25519SignAndVerify.java:21:47:21:80 | Key | -| LMSSignature.java:33:47:33:74 | KeyGeneration | LMSSignature.java:33:47:33:74 | Key | +| ECDSASignAndVerify.java:57:16:57:49 | KeyGeneration | ECDSASignAndVerify.java:47:28:47:38 | EllipticCurve | ECDSASignAndVerify.java:57:16:57:49 | Key | +| ECDSASignAndVerify.java:80:16:80:49 | KeyGeneration | ECDSASignAndVerify.java:65:28:65:38 | EllipticCurve | ECDSASignAndVerify.java:80:16:80:49 | Key | +| Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | Ed448SignAndVerify.java:21:47:21:80 | Key | +| Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:21:47:21:80 | Key | +| LMSSignAndVerify.java:33:47:33:74 | KeyGeneration | LMSSignAndVerify.java:31:46:31:70 | KeyOperationAlgorithm | LMSSignAndVerify.java:33:47:33:74 | Key | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql index e361b2dc2b35..30447906aed0 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql @@ -2,4 +2,4 @@ import java import experimental.quantum.Language from Crypto::KeyGenerationOperationNode n -select n, n.getOutputKeyArtifact() +select n, n.getAKnownAlgorithm(), n.getOutputKeyArtifact() diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected index e52b43d489f2..86d161c64887 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected @@ -1,9 +1,8 @@ -| ECDSAP256SignAndVerify.java:116:44:116:76 | SignOperation | ECDSAP256SignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:115:27:115:36 | Key | ECDSAP256SignAndVerify.java:116:69:116:75 | Message | | SignatureOutput | -| ECDSAP256SignAndVerify.java:121:28:121:88 | VerifyOperation | ECDSAP256SignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | ECDSAP256SignAndVerify.java:120:30:120:38 | Key | ECDSAP256SignAndVerify.java:121:53:121:59 | Message | SignatureInput | | +| ECDSASignAndVerify.java:116:44:116:76 | SignOperation | ECDSASignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | ECDSASignAndVerify.java:115:27:115:36 | Key | ECDSASignAndVerify.java:116:69:116:75 | Message | | SignatureOutput | +| ECDSASignAndVerify.java:121:28:121:88 | VerifyOperation | ECDSASignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | ECDSASignAndVerify.java:120:30:120:38 | Key | ECDSASignAndVerify.java:121:53:121:59 | Message | SignatureInput | | | Ed448SignAndVerify.java:32:32:32:57 | SignOperation | Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | Ed448SignAndVerify.java:30:31:30:40 | Key | Ed448SignAndVerify.java:31:27:31:33 | Message | | SignatureOutput | | Ed448SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | Ed448SignAndVerify.java:38:34:38:42 | Key | Ed448SignAndVerify.java:39:29:39:35 | Message | SignatureInput | | | Ed25519SignAndVerify.java:32:32:32:57 | SignOperation | Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:30:31:30:40 | Key | Ed25519SignAndVerify.java:31:27:31:33 | Message | | SignatureOutput | | Ed25519SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:38:34:38:42 | Key | Ed25519SignAndVerify.java:39:29:39:35 | Message | SignatureInput | | -| LMSSignature.java:44:32:44:64 | SignOperation | LMSSignature.java:40:32:40:46 | KeyOperationAlgorithm | LMSSignature.java:43:31:43:40 | Key | LMSSignature.java:44:57:44:63 | Message | | SignatureOutput | -| LMSSignature.java:51:32:51:73 | VerifyOperation | LMSSignature.java:40:32:40:46 | KeyOperationAlgorithm | LMSSignature.java:43:31:43:40 | Key | LMSSignature.java:51:55:51:61 | Message | SignatureInput | | -| LMSSignature.java:51:32:51:73 | VerifyOperation | LMSSignature.java:40:32:40:46 | KeyOperationAlgorithm | LMSSignature.java:50:32:50:40 | Key | LMSSignature.java:51:55:51:61 | Message | SignatureInput | | +| LMSSignAndVerify.java:43:32:43:64 | SignOperation | LMSSignAndVerify.java:41:32:41:46 | KeyOperationAlgorithm | LMSSignAndVerify.java:42:31:42:40 | Key | LMSSignAndVerify.java:43:57:43:63 | Message | | SignatureOutput | +| LMSSignAndVerify.java:48:32:48:75 | VerifyOperation | LMSSignAndVerify.java:46:34:46:48 | KeyOperationAlgorithm | LMSSignAndVerify.java:47:34:47:42 | Key | LMSSignAndVerify.java:48:57:48:63 | Message | SignatureInput | | From cdb8f91c869ac72e8ba82124611dbff34dd524ad Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Fri, 27 Jun 2025 16:36:38 +0200 Subject: [PATCH 29/32] Removed transient output artifact instances --- .../experimental/quantum/BouncyCastle/OperationInstances.qll | 2 +- .../quantum/BouncyCastle/modes/cipher_operations.expected | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll index 16aa89c79293..87d3e3bcfff0 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll @@ -724,7 +724,7 @@ module Modes { } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result.asExpr() = this.getAUseCall().getOutput() + result.asExpr() = super.getOutput() } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected index 452ef87f83d3..fd2b9d4294e6 100644 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected @@ -1,8 +1,4 @@ -| AESCBCEncryption.java:28:22:28:58 | EncryptOperation | AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | AESCBCEncryption.java:22:50:22:52 | Key | AESCBCEncryption.java:23:66:23:67 | Nonce | AESCBCEncryption.java:27:45:27:53 | Message | AESCBCEncryption.java:27:77:27:86 | KeyOperationOutput | | AESCBCEncryption.java:28:22:28:58 | EncryptOperation | AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | AESCBCEncryption.java:22:50:22:52 | Key | AESCBCEncryption.java:23:66:23:67 | Nonce | AESCBCEncryption.java:27:45:27:53 | Message | AESCBCEncryption.java:28:37:28:46 | KeyOperationOutput | -| AESCBCEncryption.java:47:22:47:57 | DecryptOperation | AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | AESCBCEncryption.java:41:50:41:52 | Key | AESCBCEncryption.java:42:66:42:67 | Nonce | AESCBCEncryption.java:46:45:46:54 | Message | AESCBCEncryption.java:46:79:46:87 | KeyOperationOutput | | AESCBCEncryption.java:47:22:47:57 | DecryptOperation | AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | AESCBCEncryption.java:41:50:41:52 | Key | AESCBCEncryption.java:42:66:42:67 | Nonce | AESCBCEncryption.java:46:45:46:54 | Message | AESCBCEncryption.java:47:37:47:45 | KeyOperationOutput | -| AESGCMEncryption.java:26:22:26:58 | EncryptOperation | AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | AESGCMEncryption.java:18:34:18:36 | Key | AESGCMEncryption.java:20:17:20:21 | Nonce | AESGCMEncryption.java:25:45:25:53 | Message | AESGCMEncryption.java:25:77:25:86 | KeyOperationOutput | | AESGCMEncryption.java:26:22:26:58 | EncryptOperation | AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | AESGCMEncryption.java:18:34:18:36 | Key | AESGCMEncryption.java:20:17:20:21 | Nonce | AESGCMEncryption.java:25:45:25:53 | Message | AESGCMEncryption.java:26:37:26:46 | KeyOperationOutput | -| AESGCMEncryption.java:45:22:45:57 | DecryptOperation | AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | AESGCMEncryption.java:37:34:37:36 | Key | AESGCMEncryption.java:39:17:39:21 | Nonce | AESGCMEncryption.java:44:45:44:54 | Message | AESGCMEncryption.java:44:79:44:87 | KeyOperationOutput | | AESGCMEncryption.java:45:22:45:57 | DecryptOperation | AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | AESGCMEncryption.java:37:34:37:36 | Key | AESGCMEncryption.java:39:17:39:21 | Nonce | AESGCMEncryption.java:44:45:44:54 | Message | AESGCMEncryption.java:45:37:45:45 | KeyOperationOutput | From bcbd29b8e33e26bab37a92f76b567669c9a4c978 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Wed, 2 Jul 2025 16:36:53 +0200 Subject: [PATCH 30/32] Updated unit tests to list graph nodes, edges, and properties --- .../modes/cipher_operations.expected | 4 -- .../BouncyCastle/modes/cipher_operations.ql | 6 --- .../BouncyCastle/modes/key_artifacts.expected | 4 -- .../BouncyCastle/modes/node_edges.expected | 40 +++++++++++++++++++ .../quantum/BouncyCastle/modes/node_edges.ql | 5 +++ .../modes/node_properties.expected | 36 +++++++++++++++++ .../BouncyCastle/modes/node_properties.ql | 6 +++ .../quantum/BouncyCastle/modes/nodes.expected | 37 +++++++++++++++++ .../quantum/BouncyCastle/modes/nodes.ql | 5 +++ .../modes/random_artifacts.expected | 8 ---- .../BouncyCastle/modes/random_artifacts.ql | 6 --- 11 files changed, 129 insertions(+), 28 deletions(-) delete mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected delete mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.ql delete mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/key_artifacts.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_edges.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_edges.ql create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_properties.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_properties.ql create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/nodes.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/nodes.ql delete mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected delete mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected deleted file mode 100644 index fd2b9d4294e6..000000000000 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.expected +++ /dev/null @@ -1,4 +0,0 @@ -| AESCBCEncryption.java:28:22:28:58 | EncryptOperation | AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | AESCBCEncryption.java:22:50:22:52 | Key | AESCBCEncryption.java:23:66:23:67 | Nonce | AESCBCEncryption.java:27:45:27:53 | Message | AESCBCEncryption.java:28:37:28:46 | KeyOperationOutput | -| AESCBCEncryption.java:47:22:47:57 | DecryptOperation | AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | AESCBCEncryption.java:41:50:41:52 | Key | AESCBCEncryption.java:42:66:42:67 | Nonce | AESCBCEncryption.java:46:45:46:54 | Message | AESCBCEncryption.java:47:37:47:45 | KeyOperationOutput | -| AESGCMEncryption.java:26:22:26:58 | EncryptOperation | AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | AESGCMEncryption.java:18:34:18:36 | Key | AESGCMEncryption.java:20:17:20:21 | Nonce | AESGCMEncryption.java:25:45:25:53 | Message | AESGCMEncryption.java:26:37:26:46 | KeyOperationOutput | -| AESGCMEncryption.java:45:22:45:57 | DecryptOperation | AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | AESGCMEncryption.java:37:34:37:36 | Key | AESGCMEncryption.java:39:17:39:21 | Nonce | AESGCMEncryption.java:44:45:44:54 | Message | AESGCMEncryption.java:45:37:45:45 | KeyOperationOutput | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.ql deleted file mode 100644 index 404acd0071f0..000000000000 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/cipher_operations.ql +++ /dev/null @@ -1,6 +0,0 @@ -import java -import experimental.quantum.Language - -from Crypto::CipherOperationNode n -select n, n.getAKnownAlgorithm(), n.getAKey(), n.getANonce(), n.getAnInputArtifact(), - n.getAnOutputArtifact() diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/key_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/key_artifacts.expected deleted file mode 100644 index edb586232564..000000000000 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/key_artifacts.expected +++ /dev/null @@ -1,4 +0,0 @@ -| AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | AESCBCEncryption.java:22:50:22:52 | Key | -| AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | AESCBCEncryption.java:41:50:41:52 | Key | -| AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | AESGCMEncryption.java:18:34:18:36 | Key | -| AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | AESGCMEncryption.java:37:34:37:36 | Key | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_edges.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_edges.expected new file mode 100644 index 000000000000..47e17b583318 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_edges.expected @@ -0,0 +1,40 @@ +| AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | Mode | AESCBCEncryption.java:18:31:18:56 | ModeOfOperation | +| AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | Padding | AESCBCEncryption.java:19:32:19:49 | PaddingAlgorithm | +| AESCBCEncryption.java:22:50:22:52 | Key | Source | AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | +| AESCBCEncryption.java:23:66:23:67 | Nonce | Source | AESCBCEncryption.java:62:30:62:31 | RandomNumberGeneration | +| AESCBCEncryption.java:27:45:27:53 | Message | Source | AESCBCEncryption.java:64:30:64:50 | Constant | +| AESCBCEncryption.java:28:22:28:58 | EncryptOperation | Algorithm | AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | +| AESCBCEncryption.java:28:22:28:58 | EncryptOperation | Input | AESCBCEncryption.java:27:45:27:53 | Message | +| AESCBCEncryption.java:28:22:28:58 | EncryptOperation | Key | AESCBCEncryption.java:22:50:22:52 | Key | +| AESCBCEncryption.java:28:22:28:58 | EncryptOperation | Nonce | AESCBCEncryption.java:23:66:23:67 | Nonce | +| AESCBCEncryption.java:28:22:28:58 | EncryptOperation | Output | AESCBCEncryption.java:28:37:28:46 | KeyOperationOutput | +| AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | Mode | AESCBCEncryption.java:37:31:37:56 | ModeOfOperation | +| AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | Padding | AESCBCEncryption.java:38:32:38:49 | PaddingAlgorithm | +| AESCBCEncryption.java:41:50:41:52 | Key | Source | AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | +| AESCBCEncryption.java:42:66:42:67 | Nonce | Source | AESCBCEncryption.java:62:30:62:31 | RandomNumberGeneration | +| AESCBCEncryption.java:46:45:46:54 | Message | Source | AESCBCEncryption.java:28:37:28:46 | KeyOperationOutput | +| AESCBCEncryption.java:47:22:47:57 | DecryptOperation | Algorithm | AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | +| AESCBCEncryption.java:47:22:47:57 | DecryptOperation | Input | AESCBCEncryption.java:46:45:46:54 | Message | +| AESCBCEncryption.java:47:22:47:57 | DecryptOperation | Key | AESCBCEncryption.java:41:50:41:52 | Key | +| AESCBCEncryption.java:47:22:47:57 | DecryptOperation | Nonce | AESCBCEncryption.java:42:66:42:67 | Nonce | +| AESCBCEncryption.java:47:22:47:57 | DecryptOperation | Output | AESCBCEncryption.java:47:37:47:45 | KeyOperationOutput | +| AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | Mode | AESGCMEncryption.java:16:33:16:58 | ModeOfOperation | +| AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | Padding | AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | +| AESGCMEncryption.java:18:34:18:36 | Key | Source | AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | +| AESGCMEncryption.java:20:17:20:21 | Nonce | Source | AESGCMEncryption.java:60:30:60:34 | RandomNumberGeneration | +| AESGCMEncryption.java:25:45:25:53 | Message | Source | AESGCMEncryption.java:62:30:62:65 | Constant | +| AESGCMEncryption.java:26:22:26:58 | EncryptOperation | Algorithm | AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | +| AESGCMEncryption.java:26:22:26:58 | EncryptOperation | Input | AESGCMEncryption.java:25:45:25:53 | Message | +| AESGCMEncryption.java:26:22:26:58 | EncryptOperation | Key | AESGCMEncryption.java:18:34:18:36 | Key | +| AESGCMEncryption.java:26:22:26:58 | EncryptOperation | Nonce | AESGCMEncryption.java:20:17:20:21 | Nonce | +| AESGCMEncryption.java:26:22:26:58 | EncryptOperation | Output | AESGCMEncryption.java:26:37:26:46 | KeyOperationOutput | +| AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | Mode | AESGCMEncryption.java:35:33:35:58 | ModeOfOperation | +| AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | Padding | AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | +| AESGCMEncryption.java:37:34:37:36 | Key | Source | AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | +| AESGCMEncryption.java:39:17:39:21 | Nonce | Source | AESGCMEncryption.java:60:30:60:34 | RandomNumberGeneration | +| AESGCMEncryption.java:44:45:44:54 | Message | Source | AESGCMEncryption.java:26:37:26:46 | KeyOperationOutput | +| AESGCMEncryption.java:45:22:45:57 | DecryptOperation | Algorithm | AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | +| AESGCMEncryption.java:45:22:45:57 | DecryptOperation | Input | AESGCMEncryption.java:44:45:44:54 | Message | +| AESGCMEncryption.java:45:22:45:57 | DecryptOperation | Key | AESGCMEncryption.java:37:34:37:36 | Key | +| AESGCMEncryption.java:45:22:45:57 | DecryptOperation | Nonce | AESGCMEncryption.java:39:17:39:21 | Nonce | +| AESGCMEncryption.java:45:22:45:57 | DecryptOperation | Output | AESGCMEncryption.java:45:37:45:45 | KeyOperationOutput | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_edges.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_edges.ql new file mode 100644 index 000000000000..4c9afb6c8ff5 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_edges.ql @@ -0,0 +1,5 @@ +import java +import experimental.quantum.Language + +from Crypto::NodeBase n, string key +select n, key, n.getChild(key) diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_properties.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_properties.expected new file mode 100644 index 000000000000..31a272293278 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_properties.expected @@ -0,0 +1,36 @@ +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java:9:24:9:41 | PaddingAlgorithm | Name | PKCS7 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java:9:24:9:41 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java:9:24:9:41 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java:9:24:9:41 | PaddingAlgorithm | RawName | PKCS7 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java:9:24:9:41 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java:9:24:9:41 | +| AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | Name | AES | AESCBCEncryption.java:17:28:17:42 | AESCBCEncryption.java:17:28:17:42 | +| AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | RawName | AES | AESCBCEncryption.java:17:28:17:42 | AESCBCEncryption.java:17:28:17:42 | +| AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | Structure | Block | AESCBCEncryption.java:17:28:17:42 | AESCBCEncryption.java:17:28:17:42 | +| AESCBCEncryption.java:18:31:18:56 | ModeOfOperation | Name | CBC | AESCBCEncryption.java:18:31:18:56 | AESCBCEncryption.java:18:31:18:56 | +| AESCBCEncryption.java:18:31:18:56 | ModeOfOperation | RawName | CBC | AESCBCEncryption.java:18:31:18:56 | AESCBCEncryption.java:18:31:18:56 | +| AESCBCEncryption.java:19:32:19:49 | PaddingAlgorithm | Name | PKCS7 | AESCBCEncryption.java:19:32:19:49 | AESCBCEncryption.java:19:32:19:49 | +| AESCBCEncryption.java:19:32:19:49 | PaddingAlgorithm | RawName | PKCS7 | AESCBCEncryption.java:19:32:19:49 | AESCBCEncryption.java:19:32:19:49 | +| AESCBCEncryption.java:22:50:22:52 | Key | KeyType | Unknown | AESCBCEncryption.java:22:50:22:52 | AESCBCEncryption.java:22:50:22:52 | +| AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | Name | AES | AESCBCEncryption.java:36:28:36:42 | AESCBCEncryption.java:36:28:36:42 | +| AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | RawName | AES | AESCBCEncryption.java:36:28:36:42 | AESCBCEncryption.java:36:28:36:42 | +| AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | Structure | Block | AESCBCEncryption.java:36:28:36:42 | AESCBCEncryption.java:36:28:36:42 | +| AESCBCEncryption.java:37:31:37:56 | ModeOfOperation | Name | CBC | AESCBCEncryption.java:37:31:37:56 | AESCBCEncryption.java:37:31:37:56 | +| AESCBCEncryption.java:37:31:37:56 | ModeOfOperation | RawName | CBC | AESCBCEncryption.java:37:31:37:56 | AESCBCEncryption.java:37:31:37:56 | +| AESCBCEncryption.java:38:32:38:49 | PaddingAlgorithm | Name | PKCS7 | AESCBCEncryption.java:38:32:38:49 | AESCBCEncryption.java:38:32:38:49 | +| AESCBCEncryption.java:38:32:38:49 | PaddingAlgorithm | RawName | PKCS7 | AESCBCEncryption.java:38:32:38:49 | AESCBCEncryption.java:38:32:38:49 | +| AESCBCEncryption.java:41:50:41:52 | Key | KeyType | Unknown | AESCBCEncryption.java:41:50:41:52 | AESCBCEncryption.java:41:50:41:52 | +| AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | Description | java.security.SecureRandom | AESCBCEncryption.java:60:30:60:32 | AESCBCEncryption.java:60:30:60:32 | +| AESCBCEncryption.java:62:30:62:31 | RandomNumberGeneration | Description | java.security.SecureRandom | AESCBCEncryption.java:62:30:62:31 | AESCBCEncryption.java:62:30:62:31 | +| AESCBCEncryption.java:64:30:64:50 | Constant | Description | "Hello AES-CBC mode!" | AESCBCEncryption.java:64:30:64:50 | AESCBCEncryption.java:64:30:64:50 | +| AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | Name | AES | AESGCMEncryption.java:15:28:15:42 | AESGCMEncryption.java:15:28:15:42 | +| AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | RawName | AES | AESGCMEncryption.java:15:28:15:42 | AESGCMEncryption.java:15:28:15:42 | +| AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | Structure | Block | AESGCMEncryption.java:15:28:15:42 | AESGCMEncryption.java:15:28:15:42 | +| AESGCMEncryption.java:16:33:16:58 | ModeOfOperation | Name | GCM | AESGCMEncryption.java:16:33:16:58 | AESGCMEncryption.java:16:33:16:58 | +| AESGCMEncryption.java:16:33:16:58 | ModeOfOperation | RawName | GCM | AESGCMEncryption.java:16:33:16:58 | AESGCMEncryption.java:16:33:16:58 | +| AESGCMEncryption.java:18:34:18:36 | Key | KeyType | Unknown | AESGCMEncryption.java:18:34:18:36 | AESGCMEncryption.java:18:34:18:36 | +| AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | Name | AES | AESGCMEncryption.java:34:28:34:42 | AESGCMEncryption.java:34:28:34:42 | +| AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | RawName | AES | AESGCMEncryption.java:34:28:34:42 | AESGCMEncryption.java:34:28:34:42 | +| AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | Structure | Block | AESGCMEncryption.java:34:28:34:42 | AESGCMEncryption.java:34:28:34:42 | +| AESGCMEncryption.java:35:33:35:58 | ModeOfOperation | Name | GCM | AESGCMEncryption.java:35:33:35:58 | AESGCMEncryption.java:35:33:35:58 | +| AESGCMEncryption.java:35:33:35:58 | ModeOfOperation | RawName | GCM | AESGCMEncryption.java:35:33:35:58 | AESGCMEncryption.java:35:33:35:58 | +| AESGCMEncryption.java:37:34:37:36 | Key | KeyType | Unknown | AESGCMEncryption.java:37:34:37:36 | AESGCMEncryption.java:37:34:37:36 | +| AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | Description | java.security.SecureRandom | AESGCMEncryption.java:58:30:58:32 | AESGCMEncryption.java:58:30:58:32 | +| AESGCMEncryption.java:60:30:60:34 | RandomNumberGeneration | Description | java.security.SecureRandom | AESGCMEncryption.java:60:30:60:34 | AESGCMEncryption.java:60:30:60:34 | +| AESGCMEncryption.java:62:30:62:65 | Constant | Description | "This is a message to be encrypted." | AESGCMEncryption.java:62:30:62:65 | AESGCMEncryption.java:62:30:62:65 | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_properties.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_properties.ql new file mode 100644 index 000000000000..79514611e679 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/node_properties.ql @@ -0,0 +1,6 @@ +import java +import experimental.quantum.Language + +from Crypto::NodeBase n, string key, string value, Location location +where n.properties(key, value, location) +select n, key, value, location diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/nodes.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/nodes.expected new file mode 100644 index 000000000000..59d4150ebeb6 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/nodes.expected @@ -0,0 +1,37 @@ +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java:9:24:9:41 | PaddingAlgorithm | +| AESCBCEncryption.java:17:28:17:42 | KeyOperationAlgorithm | +| AESCBCEncryption.java:18:31:18:56 | ModeOfOperation | +| AESCBCEncryption.java:19:32:19:49 | PaddingAlgorithm | +| AESCBCEncryption.java:22:50:22:52 | Key | +| AESCBCEncryption.java:23:66:23:67 | Nonce | +| AESCBCEncryption.java:27:45:27:53 | Message | +| AESCBCEncryption.java:28:22:28:58 | EncryptOperation | +| AESCBCEncryption.java:28:37:28:46 | KeyOperationOutput | +| AESCBCEncryption.java:36:28:36:42 | KeyOperationAlgorithm | +| AESCBCEncryption.java:37:31:37:56 | ModeOfOperation | +| AESCBCEncryption.java:38:32:38:49 | PaddingAlgorithm | +| AESCBCEncryption.java:41:50:41:52 | Key | +| AESCBCEncryption.java:42:66:42:67 | Nonce | +| AESCBCEncryption.java:46:45:46:54 | Message | +| AESCBCEncryption.java:47:22:47:57 | DecryptOperation | +| AESCBCEncryption.java:47:37:47:45 | KeyOperationOutput | +| AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | +| AESCBCEncryption.java:62:30:62:31 | RandomNumberGeneration | +| AESCBCEncryption.java:64:30:64:50 | Constant | +| AESGCMEncryption.java:15:28:15:42 | KeyOperationAlgorithm | +| AESGCMEncryption.java:16:33:16:58 | ModeOfOperation | +| AESGCMEncryption.java:18:34:18:36 | Key | +| AESGCMEncryption.java:20:17:20:21 | Nonce | +| AESGCMEncryption.java:25:45:25:53 | Message | +| AESGCMEncryption.java:26:22:26:58 | EncryptOperation | +| AESGCMEncryption.java:26:37:26:46 | KeyOperationOutput | +| AESGCMEncryption.java:34:28:34:42 | KeyOperationAlgorithm | +| AESGCMEncryption.java:35:33:35:58 | ModeOfOperation | +| AESGCMEncryption.java:37:34:37:36 | Key | +| AESGCMEncryption.java:39:17:39:21 | Nonce | +| AESGCMEncryption.java:44:45:44:54 | Message | +| AESGCMEncryption.java:45:22:45:57 | DecryptOperation | +| AESGCMEncryption.java:45:37:45:45 | KeyOperationOutput | +| AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | +| AESGCMEncryption.java:60:30:60:34 | RandomNumberGeneration | +| AESGCMEncryption.java:62:30:62:65 | Constant | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/nodes.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/nodes.ql new file mode 100644 index 000000000000..e080ce7297ac --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/nodes.ql @@ -0,0 +1,5 @@ +import java +import experimental.quantum.Language + +from Crypto::NodeBase n +select n diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected deleted file mode 100644 index 873907aec4a9..000000000000 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected +++ /dev/null @@ -1,8 +0,0 @@ -| AESCBCEncryption.java:22:50:22:52 | Key | AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | -| AESCBCEncryption.java:23:66:23:67 | Nonce | AESCBCEncryption.java:62:30:62:31 | RandomNumberGeneration | -| AESCBCEncryption.java:41:50:41:52 | Key | AESCBCEncryption.java:60:30:60:32 | RandomNumberGeneration | -| AESCBCEncryption.java:42:66:42:67 | Nonce | AESCBCEncryption.java:62:30:62:31 | RandomNumberGeneration | -| AESGCMEncryption.java:18:34:18:36 | Key | AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | -| AESGCMEncryption.java:20:17:20:21 | Nonce | AESGCMEncryption.java:60:30:60:34 | RandomNumberGeneration | -| AESGCMEncryption.java:37:34:37:36 | Key | AESGCMEncryption.java:58:30:58:32 | RandomNumberGeneration | -| AESGCMEncryption.java:39:17:39:21 | Nonce | AESGCMEncryption.java:60:30:60:34 | RandomNumberGeneration | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql deleted file mode 100644 index 3f27ed82e7a5..000000000000 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.ql +++ /dev/null @@ -1,6 +0,0 @@ -import java -import experimental.quantum.Language - -from Crypto::ArtifactNode n -where n.getSourceNode() instanceof Crypto::RandomNumberGenerationNode -select n, n.getSourceNode() From 2098a64c2ac519ffdf95ae5c04ed415c4ce88c83 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Fri, 4 Jul 2025 10:18:57 +0200 Subject: [PATCH 31/32] Fixed QL for QL recommendation --- .../lib/experimental/quantum/BouncyCastle/OperationInstances.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll index 87d3e3bcfff0..9e00be821531 100644 --- a/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll +++ b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll @@ -700,7 +700,6 @@ module Modes { override Crypto::KeyOperationSubtype getKeyOperationSubtype() { // The key operation sub-type is determined by the `encrypting` argument to `init()`. - exists(this.getInitCall()) and result = this.getInitCall().getKeyOperationSubtype() or not exists(this.getInitCall()) and From a50a92673cc5110497c5f9a36af6d23948b3cf6c Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Fri, 4 Jul 2025 10:20:02 +0200 Subject: [PATCH 32/32] Updated Bouncy Castle signer unit tests --- .../signers/key_artifacts.expected | 5 - .../BouncyCastle/signers/key_artifacts.ql | 5 - .../key_generation_operations.expected | 5 - .../signers/key_generation_operations.ql | 5 - .../BouncyCastle/signers/node_edges.expected | 98 +++++++++++++++++++ .../BouncyCastle/signers/node_edges.ql | 5 + .../signers/node_properties.expected | 74 ++++++++++++++ .../BouncyCastle/signers/node_properties.ql | 6 ++ .../BouncyCastle/signers/nodes.expected | 67 +++++++++++++ .../quantum/BouncyCastle/signers/nodes.ql | 5 + .../signers/signature_operations.expected | 8 -- .../signers/signature_operations.ql | 22 ----- 12 files changed, 255 insertions(+), 50 deletions(-) delete mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected delete mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql delete mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected delete mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_edges.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_edges.ql create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_properties.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_properties.ql create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/nodes.expected create mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/nodes.ql delete mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected delete mode 100644 java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.ql diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected deleted file mode 100644 index 213d8d1baaed..000000000000 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected +++ /dev/null @@ -1,5 +0,0 @@ -| ECDSASignAndVerify.java:57:16:57:49 | Key | secp256r1 | -| ECDSASignAndVerify.java:80:16:80:49 | Key | secp256k1 | -| Ed448SignAndVerify.java:21:47:21:80 | Key | Ed448 | -| Ed25519SignAndVerify.java:21:47:21:80 | Key | Ed25519 | -| LMSSignAndVerify.java:33:47:33:74 | Key | LMS | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql deleted file mode 100644 index 394c82d70fce..000000000000 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.ql +++ /dev/null @@ -1,5 +0,0 @@ -import java -import experimental.quantum.Language - -from Crypto::KeyArtifactNode n -select n, n.getAKnownAlgorithm() diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected deleted file mode 100644 index a07e0f8d8abb..000000000000 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected +++ /dev/null @@ -1,5 +0,0 @@ -| ECDSASignAndVerify.java:57:16:57:49 | KeyGeneration | ECDSASignAndVerify.java:47:28:47:38 | EllipticCurve | ECDSASignAndVerify.java:57:16:57:49 | Key | -| ECDSASignAndVerify.java:80:16:80:49 | KeyGeneration | ECDSASignAndVerify.java:65:28:65:38 | EllipticCurve | ECDSASignAndVerify.java:80:16:80:49 | Key | -| Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | Ed448SignAndVerify.java:21:47:21:80 | Key | -| Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:21:47:21:80 | Key | -| LMSSignAndVerify.java:33:47:33:74 | KeyGeneration | LMSSignAndVerify.java:31:46:31:70 | KeyOperationAlgorithm | LMSSignAndVerify.java:33:47:33:74 | Key | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql deleted file mode 100644 index 30447906aed0..000000000000 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.ql +++ /dev/null @@ -1,5 +0,0 @@ -import java -import experimental.quantum.Language - -from Crypto::KeyGenerationOperationNode n -select n, n.getAKnownAlgorithm(), n.getOutputKeyArtifact() diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_edges.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_edges.expected new file mode 100644 index 000000000000..ca46ef4ee5d1 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_edges.expected @@ -0,0 +1,98 @@ +| ECDSASignAndVerify.java:57:16:57:49 | Key | Algorithm | ECDSASignAndVerify.java:47:28:47:38 | EllipticCurve | +| ECDSASignAndVerify.java:57:16:57:49 | KeyGeneration | Algorithm | ECDSASignAndVerify.java:47:28:47:38 | EllipticCurve | +| ECDSASignAndVerify.java:57:16:57:49 | KeyGeneration | Output | ECDSASignAndVerify.java:57:16:57:49 | Key | +| ECDSASignAndVerify.java:80:16:80:49 | Key | Algorithm | ECDSASignAndVerify.java:65:28:65:38 | EllipticCurve | +| ECDSASignAndVerify.java:80:16:80:49 | KeyGeneration | Algorithm | ECDSASignAndVerify.java:65:28:65:38 | EllipticCurve | +| ECDSASignAndVerify.java:80:16:80:49 | KeyGeneration | Output | ECDSASignAndVerify.java:80:16:80:49 | Key | +| ECDSASignAndVerify.java:103:16:103:49 | Key | Algorithm | ECDSASignAndVerify.java:103:16:103:49 | Key | +| ECDSASignAndVerify.java:103:16:103:49 | KeyGeneration | Algorithm | ECDSASignAndVerify.java:103:16:103:49 | KeyGeneration | +| ECDSASignAndVerify.java:103:16:103:49 | KeyGeneration | Output | ECDSASignAndVerify.java:103:16:103:49 | Key | +| ECDSASignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | Mode | ECDSASignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | +| ECDSASignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | Padding | ECDSASignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | +| ECDSASignAndVerify.java:115:27:115:36 | Key | Source | ECDSASignAndVerify.java:57:16:57:49 | Key | +| ECDSASignAndVerify.java:115:27:115:36 | Key | Source | ECDSASignAndVerify.java:80:16:80:49 | Key | +| ECDSASignAndVerify.java:115:27:115:36 | Key | Source | ECDSASignAndVerify.java:103:16:103:49 | Key | +| ECDSASignAndVerify.java:116:44:116:76 | SignOperation | Algorithm | ECDSASignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | +| ECDSASignAndVerify.java:116:44:116:76 | SignOperation | Input | ECDSASignAndVerify.java:116:69:116:75 | Message | +| ECDSASignAndVerify.java:116:44:116:76 | SignOperation | Key | ECDSASignAndVerify.java:115:27:115:36 | Key | +| ECDSASignAndVerify.java:116:44:116:76 | SignOperation | Output | ECDSASignAndVerify.java:116:44:116:76 | SignatureOutput | +| ECDSASignAndVerify.java:116:69:116:75 | Message | Source | ECDSASignAndVerify.java:31:30:31:54 | Constant | +| ECDSASignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | Mode | ECDSASignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | +| ECDSASignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | Padding | ECDSASignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | +| ECDSASignAndVerify.java:120:30:120:38 | Key | Source | ECDSASignAndVerify.java:57:16:57:49 | Key | +| ECDSASignAndVerify.java:120:30:120:38 | Key | Source | ECDSASignAndVerify.java:80:16:80:49 | Key | +| ECDSASignAndVerify.java:120:30:120:38 | Key | Source | ECDSASignAndVerify.java:103:16:103:49 | Key | +| ECDSASignAndVerify.java:121:28:121:88 | VerifyOperation | Algorithm | ECDSASignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | +| ECDSASignAndVerify.java:121:28:121:88 | VerifyOperation | Input | ECDSASignAndVerify.java:121:53:121:59 | Message | +| ECDSASignAndVerify.java:121:28:121:88 | VerifyOperation | Key | ECDSASignAndVerify.java:120:30:120:38 | Key | +| ECDSASignAndVerify.java:121:28:121:88 | VerifyOperation | Signature | ECDSASignAndVerify.java:121:62:121:73 | SignatureInput | +| ECDSASignAndVerify.java:121:28:121:88 | VerifyOperation | Signature | ECDSASignAndVerify.java:121:76:121:87 | SignatureInput | +| ECDSASignAndVerify.java:121:53:121:59 | Message | Source | ECDSASignAndVerify.java:121:53:121:59 | Message | +| ECDSASignAndVerify.java:121:62:121:73 | SignatureInput | Source | ECDSASignAndVerify.java:116:44:116:76 | SignatureOutput | +| ECDSASignAndVerify.java:121:76:121:87 | SignatureInput | Source | ECDSASignAndVerify.java:116:44:116:76 | SignatureOutput | +| Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | Mode | Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | Padding | Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:21:47:21:80 | Key | Algorithm | Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Algorithm | Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | Output | Ed448SignAndVerify.java:21:47:21:80 | Key | +| Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | Mode | Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | Padding | Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:30:31:30:40 | Key | Source | Ed448SignAndVerify.java:21:47:21:80 | Key | +| Ed448SignAndVerify.java:31:27:31:33 | Message | Source | Ed448SignAndVerify.java:26:30:26:54 | Constant | +| Ed448SignAndVerify.java:32:32:32:57 | SignOperation | Algorithm | Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:32:32:32:57 | SignOperation | Input | Ed448SignAndVerify.java:31:27:31:33 | Message | +| Ed448SignAndVerify.java:32:32:32:57 | SignOperation | Key | Ed448SignAndVerify.java:30:31:30:40 | Key | +| Ed448SignAndVerify.java:32:32:32:57 | SignOperation | Output | Ed448SignAndVerify.java:32:32:32:57 | SignatureOutput | +| Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | Mode | Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | Padding | Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:38:34:38:42 | Key | Source | Ed448SignAndVerify.java:21:47:21:80 | Key | +| Ed448SignAndVerify.java:39:29:39:35 | Message | Source | Ed448SignAndVerify.java:39:29:39:35 | Message | +| Ed448SignAndVerify.java:40:32:40:66 | VerifyOperation | Algorithm | Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:40:32:40:66 | VerifyOperation | Input | Ed448SignAndVerify.java:39:29:39:35 | Message | +| Ed448SignAndVerify.java:40:32:40:66 | VerifyOperation | Key | Ed448SignAndVerify.java:38:34:38:42 | Key | +| Ed448SignAndVerify.java:40:32:40:66 | VerifyOperation | Signature | Ed448SignAndVerify.java:40:57:40:65 | SignatureInput | +| Ed448SignAndVerify.java:40:57:40:65 | SignatureInput | Source | Ed448SignAndVerify.java:32:32:32:57 | SignatureOutput | +| Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | Mode | Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | Padding | Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:21:47:21:80 | Key | Algorithm | Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Algorithm | Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | Output | Ed25519SignAndVerify.java:21:47:21:80 | Key | +| Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | Mode | Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | Padding | Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:30:31:30:40 | Key | Source | Ed25519SignAndVerify.java:21:47:21:80 | Key | +| Ed25519SignAndVerify.java:31:27:31:33 | Message | Source | Ed25519SignAndVerify.java:26:30:26:56 | Constant | +| Ed25519SignAndVerify.java:32:32:32:57 | SignOperation | Algorithm | Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:32:32:32:57 | SignOperation | Input | Ed25519SignAndVerify.java:31:27:31:33 | Message | +| Ed25519SignAndVerify.java:32:32:32:57 | SignOperation | Key | Ed25519SignAndVerify.java:30:31:30:40 | Key | +| Ed25519SignAndVerify.java:32:32:32:57 | SignOperation | Output | Ed25519SignAndVerify.java:32:32:32:57 | SignatureOutput | +| Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | Mode | Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | Padding | Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:38:34:38:42 | Key | Source | Ed25519SignAndVerify.java:21:47:21:80 | Key | +| Ed25519SignAndVerify.java:39:29:39:35 | Message | Source | Ed25519SignAndVerify.java:39:29:39:35 | Message | +| Ed25519SignAndVerify.java:40:32:40:66 | VerifyOperation | Algorithm | Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:40:32:40:66 | VerifyOperation | Input | Ed25519SignAndVerify.java:39:29:39:35 | Message | +| Ed25519SignAndVerify.java:40:32:40:66 | VerifyOperation | Key | Ed25519SignAndVerify.java:38:34:38:42 | Key | +| Ed25519SignAndVerify.java:40:32:40:66 | VerifyOperation | Signature | Ed25519SignAndVerify.java:40:57:40:65 | SignatureInput | +| Ed25519SignAndVerify.java:40:57:40:65 | SignatureInput | Source | Ed25519SignAndVerify.java:32:32:32:57 | SignatureOutput | +| LMSSignAndVerify.java:31:46:31:70 | KeyOperationAlgorithm | Mode | LMSSignAndVerify.java:31:46:31:70 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:31:46:31:70 | KeyOperationAlgorithm | Padding | LMSSignAndVerify.java:31:46:31:70 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:33:47:33:74 | Key | Algorithm | LMSSignAndVerify.java:31:46:31:70 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:33:47:33:74 | KeyGeneration | Algorithm | LMSSignAndVerify.java:31:46:31:70 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:33:47:33:74 | KeyGeneration | Output | LMSSignAndVerify.java:33:47:33:74 | Key | +| LMSSignAndVerify.java:41:32:41:46 | KeyOperationAlgorithm | Mode | LMSSignAndVerify.java:41:32:41:46 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:41:32:41:46 | KeyOperationAlgorithm | Padding | LMSSignAndVerify.java:41:32:41:46 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:42:31:42:40 | Key | Source | LMSSignAndVerify.java:33:47:33:74 | Key | +| LMSSignAndVerify.java:43:32:43:64 | SignOperation | Algorithm | LMSSignAndVerify.java:41:32:41:46 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:43:32:43:64 | SignOperation | Input | LMSSignAndVerify.java:43:57:43:63 | Message | +| LMSSignAndVerify.java:43:32:43:64 | SignOperation | Key | LMSSignAndVerify.java:42:31:42:40 | Key | +| LMSSignAndVerify.java:43:32:43:64 | SignOperation | Output | LMSSignAndVerify.java:43:32:43:64 | SignatureOutput | +| LMSSignAndVerify.java:43:57:43:63 | Message | Source | LMSSignAndVerify.java:38:30:38:52 | Constant | +| LMSSignAndVerify.java:46:34:46:48 | KeyOperationAlgorithm | Mode | LMSSignAndVerify.java:46:34:46:48 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:46:34:46:48 | KeyOperationAlgorithm | Padding | LMSSignAndVerify.java:46:34:46:48 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:47:34:47:42 | Key | Source | LMSSignAndVerify.java:33:47:33:74 | Key | +| LMSSignAndVerify.java:48:32:48:75 | VerifyOperation | Algorithm | LMSSignAndVerify.java:46:34:46:48 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:48:32:48:75 | VerifyOperation | Input | LMSSignAndVerify.java:48:57:48:63 | Message | +| LMSSignAndVerify.java:48:32:48:75 | VerifyOperation | Key | LMSSignAndVerify.java:47:34:47:42 | Key | +| LMSSignAndVerify.java:48:32:48:75 | VerifyOperation | Signature | LMSSignAndVerify.java:48:66:48:74 | SignatureInput | +| LMSSignAndVerify.java:48:57:48:63 | Message | Source | LMSSignAndVerify.java:48:57:48:63 | Message | +| LMSSignAndVerify.java:48:66:48:74 | SignatureInput | Source | LMSSignAndVerify.java:43:32:43:64 | SignatureOutput | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_edges.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_edges.ql new file mode 100644 index 000000000000..4c9afb6c8ff5 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_edges.ql @@ -0,0 +1,5 @@ +import java +import experimental.quantum.Language + +from Crypto::NodeBase n, string key +select n, key, n.getChild(key) diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_properties.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_properties.expected new file mode 100644 index 000000000000..d8d019717b46 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_properties.expected @@ -0,0 +1,74 @@ +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | EllipticCurve | KeySize | 256 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | EllipticCurve | Name | secp256r1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | EllipticCurve | ParsedName | secp256r1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | EllipticCurve | RawName | secp256r1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | EllipticCurve | KeySize | 256 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | EllipticCurve | Name | secp256r1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | EllipticCurve | ParsedName | secp256r1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | EllipticCurve | RawName | secp256r1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | EllipticCurve | KeySize | 256 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | EllipticCurve | Name | secp256k1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | EllipticCurve | ParsedName | secp256k1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | EllipticCurve | RawName | secp256k1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | EllipticCurve | KeySize | 384 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | EllipticCurve | Name | secp384r1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | EllipticCurve | ParsedName | secp384r1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | EllipticCurve | RawName | secp384r1 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | +| ECDSASignAndVerify.java:31:30:31:54 | Constant | Description | "Hello, ECDSA signature!" | ECDSASignAndVerify.java:31:30:31:54 | ECDSASignAndVerify.java:31:30:31:54 | +| ECDSASignAndVerify.java:47:28:47:38 | EllipticCurve | KeySize | 256 | ECDSASignAndVerify.java:47:28:47:38 | ECDSASignAndVerify.java:47:28:47:38 | +| ECDSASignAndVerify.java:47:28:47:38 | EllipticCurve | Name | secp256r1 | ECDSASignAndVerify.java:47:28:47:38 | ECDSASignAndVerify.java:47:28:47:38 | +| ECDSASignAndVerify.java:47:28:47:38 | EllipticCurve | ParsedName | secp256r1 | ECDSASignAndVerify.java:47:28:47:38 | ECDSASignAndVerify.java:47:28:47:38 | +| ECDSASignAndVerify.java:47:28:47:38 | EllipticCurve | RawName | secp256r1 | ECDSASignAndVerify.java:47:28:47:38 | ECDSASignAndVerify.java:47:28:47:38 | +| ECDSASignAndVerify.java:57:16:57:49 | Key | KeyType | Asymmetric | ECDSASignAndVerify.java:57:16:57:49 | ECDSASignAndVerify.java:57:16:57:49 | +| ECDSASignAndVerify.java:65:28:65:38 | EllipticCurve | KeySize | 256 | ECDSASignAndVerify.java:65:28:65:38 | ECDSASignAndVerify.java:65:28:65:38 | +| ECDSASignAndVerify.java:65:28:65:38 | EllipticCurve | Name | secp256k1 | ECDSASignAndVerify.java:65:28:65:38 | ECDSASignAndVerify.java:65:28:65:38 | +| ECDSASignAndVerify.java:65:28:65:38 | EllipticCurve | ParsedName | secp256k1 | ECDSASignAndVerify.java:65:28:65:38 | ECDSASignAndVerify.java:65:28:65:38 | +| ECDSASignAndVerify.java:65:28:65:38 | EllipticCurve | RawName | secp256k1 | ECDSASignAndVerify.java:65:28:65:38 | ECDSASignAndVerify.java:65:28:65:38 | +| ECDSASignAndVerify.java:80:16:80:49 | Key | KeyType | Asymmetric | ECDSASignAndVerify.java:80:16:80:49 | ECDSASignAndVerify.java:80:16:80:49 | +| ECDSASignAndVerify.java:89:28:89:38 | EllipticCurve | KeySize | 384 | ECDSASignAndVerify.java:89:28:89:38 | ECDSASignAndVerify.java:89:28:89:38 | +| ECDSASignAndVerify.java:89:28:89:38 | EllipticCurve | Name | secp384r1 | ECDSASignAndVerify.java:89:28:89:38 | ECDSASignAndVerify.java:89:28:89:38 | +| ECDSASignAndVerify.java:89:28:89:38 | EllipticCurve | ParsedName | secp384r1 | ECDSASignAndVerify.java:89:28:89:38 | ECDSASignAndVerify.java:89:28:89:38 | +| ECDSASignAndVerify.java:89:28:89:38 | EllipticCurve | RawName | secp384r1 | ECDSASignAndVerify.java:89:28:89:38 | ECDSASignAndVerify.java:89:28:89:38 | +| ECDSASignAndVerify.java:103:16:103:49 | Key | KeyType | Asymmetric | ECDSASignAndVerify.java:103:16:103:49 | ECDSASignAndVerify.java:103:16:103:49 | +| ECDSASignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | Name | ECDSA | ECDSASignAndVerify.java:114:30:114:46 | ECDSASignAndVerify.java:114:30:114:46 | +| ECDSASignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | RawName | ECDSA | ECDSASignAndVerify.java:114:30:114:46 | ECDSASignAndVerify.java:114:30:114:46 | +| ECDSASignAndVerify.java:115:27:115:36 | Key | KeyType | Unknown | ECDSASignAndVerify.java:115:27:115:36 | ECDSASignAndVerify.java:115:27:115:36 | +| ECDSASignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | Name | ECDSA | ECDSASignAndVerify.java:119:32:119:48 | ECDSASignAndVerify.java:119:32:119:48 | +| ECDSASignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | RawName | ECDSA | ECDSASignAndVerify.java:119:32:119:48 | ECDSASignAndVerify.java:119:32:119:48 | +| ECDSASignAndVerify.java:120:30:120:38 | Key | KeyType | Unknown | ECDSASignAndVerify.java:120:30:120:38 | ECDSASignAndVerify.java:120:30:120:38 | +| Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | KeySize | 448 | Ed448SignAndVerify.java:19:54:19:80 | Ed448SignAndVerify.java:19:54:19:80 | +| Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | Name | Ed448 | Ed448SignAndVerify.java:19:54:19:80 | Ed448SignAndVerify.java:19:54:19:80 | +| Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | RawName | Ed448 | Ed448SignAndVerify.java:19:54:19:80 | Ed448SignAndVerify.java:19:54:19:80 | +| Ed448SignAndVerify.java:21:47:21:80 | Key | KeyType | Asymmetric | Ed448SignAndVerify.java:21:47:21:80 | Ed448SignAndVerify.java:21:47:21:80 | +| Ed448SignAndVerify.java:26:30:26:54 | Constant | Description | "Hello, Ed448 signature!" | Ed448SignAndVerify.java:26:30:26:54 | Ed448SignAndVerify.java:26:30:26:54 | +| Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | KeySize | 448 | Ed448SignAndVerify.java:29:34:29:70 | Ed448SignAndVerify.java:29:34:29:70 | +| Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | Name | Ed448 | Ed448SignAndVerify.java:29:34:29:70 | Ed448SignAndVerify.java:29:34:29:70 | +| Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | RawName | Ed448 | Ed448SignAndVerify.java:29:34:29:70 | Ed448SignAndVerify.java:29:34:29:70 | +| Ed448SignAndVerify.java:30:31:30:40 | Key | KeyType | Unknown | Ed448SignAndVerify.java:30:31:30:40 | Ed448SignAndVerify.java:30:31:30:40 | +| Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | KeySize | 448 | Ed448SignAndVerify.java:37:36:37:72 | Ed448SignAndVerify.java:37:36:37:72 | +| Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | Name | Ed448 | Ed448SignAndVerify.java:37:36:37:72 | Ed448SignAndVerify.java:37:36:37:72 | +| Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | RawName | Ed448 | Ed448SignAndVerify.java:37:36:37:72 | Ed448SignAndVerify.java:37:36:37:72 | +| Ed448SignAndVerify.java:38:34:38:42 | Key | KeyType | Unknown | Ed448SignAndVerify.java:38:34:38:42 | Ed448SignAndVerify.java:38:34:38:42 | +| Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | KeySize | 256 | Ed25519SignAndVerify.java:19:56:19:84 | Ed25519SignAndVerify.java:19:56:19:84 | +| Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | Name | Ed25519 | Ed25519SignAndVerify.java:19:56:19:84 | Ed25519SignAndVerify.java:19:56:19:84 | +| Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | RawName | Ed25519 | Ed25519SignAndVerify.java:19:56:19:84 | Ed25519SignAndVerify.java:19:56:19:84 | +| Ed25519SignAndVerify.java:21:47:21:80 | Key | KeyType | Asymmetric | Ed25519SignAndVerify.java:21:47:21:80 | Ed25519SignAndVerify.java:21:47:21:80 | +| Ed25519SignAndVerify.java:26:30:26:56 | Constant | Description | "Hello, Ed25519 signature!" | Ed25519SignAndVerify.java:26:30:26:56 | Ed25519SignAndVerify.java:26:30:26:56 | +| Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | KeySize | 256 | Ed25519SignAndVerify.java:29:36:29:54 | Ed25519SignAndVerify.java:29:36:29:54 | +| Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | Name | Ed25519 | Ed25519SignAndVerify.java:29:36:29:54 | Ed25519SignAndVerify.java:29:36:29:54 | +| Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | RawName | Ed25519 | Ed25519SignAndVerify.java:29:36:29:54 | Ed25519SignAndVerify.java:29:36:29:54 | +| Ed25519SignAndVerify.java:30:31:30:40 | Key | KeyType | Unknown | Ed25519SignAndVerify.java:30:31:30:40 | Ed25519SignAndVerify.java:30:31:30:40 | +| Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | KeySize | 256 | Ed25519SignAndVerify.java:37:38:37:56 | Ed25519SignAndVerify.java:37:38:37:56 | +| Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | Name | Ed25519 | Ed25519SignAndVerify.java:37:38:37:56 | Ed25519SignAndVerify.java:37:38:37:56 | +| Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | RawName | Ed25519 | Ed25519SignAndVerify.java:37:38:37:56 | Ed25519SignAndVerify.java:37:38:37:56 | +| Ed25519SignAndVerify.java:38:34:38:42 | Key | KeyType | Unknown | Ed25519SignAndVerify.java:38:34:38:42 | Ed25519SignAndVerify.java:38:34:38:42 | +| LMSSignAndVerify.java:31:46:31:70 | KeyOperationAlgorithm | Name | LMS | LMSSignAndVerify.java:31:46:31:70 | LMSSignAndVerify.java:31:46:31:70 | +| LMSSignAndVerify.java:31:46:31:70 | KeyOperationAlgorithm | RawName | LMS | LMSSignAndVerify.java:31:46:31:70 | LMSSignAndVerify.java:31:46:31:70 | +| LMSSignAndVerify.java:33:47:33:74 | Key | KeyType | Asymmetric | LMSSignAndVerify.java:33:47:33:74 | LMSSignAndVerify.java:33:47:33:74 | +| LMSSignAndVerify.java:38:30:38:52 | Constant | Description | "Hello, LMS signature!" | LMSSignAndVerify.java:38:30:38:52 | LMSSignAndVerify.java:38:30:38:52 | +| LMSSignAndVerify.java:41:32:41:46 | KeyOperationAlgorithm | Name | LMS | LMSSignAndVerify.java:41:32:41:46 | LMSSignAndVerify.java:41:32:41:46 | +| LMSSignAndVerify.java:41:32:41:46 | KeyOperationAlgorithm | RawName | LMS | LMSSignAndVerify.java:41:32:41:46 | LMSSignAndVerify.java:41:32:41:46 | +| LMSSignAndVerify.java:42:31:42:40 | Key | KeyType | Unknown | LMSSignAndVerify.java:42:31:42:40 | LMSSignAndVerify.java:42:31:42:40 | +| LMSSignAndVerify.java:46:34:46:48 | KeyOperationAlgorithm | Name | LMS | LMSSignAndVerify.java:46:34:46:48 | LMSSignAndVerify.java:46:34:46:48 | +| LMSSignAndVerify.java:46:34:46:48 | KeyOperationAlgorithm | RawName | LMS | LMSSignAndVerify.java:46:34:46:48 | LMSSignAndVerify.java:46:34:46:48 | +| LMSSignAndVerify.java:47:34:47:42 | Key | KeyType | Unknown | LMSSignAndVerify.java:47:34:47:42 | LMSSignAndVerify.java:47:34:47:42 | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_properties.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_properties.ql new file mode 100644 index 000000000000..79514611e679 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/node_properties.ql @@ -0,0 +1,6 @@ +import java +import experimental.quantum.Language + +from Crypto::NodeBase n, string key, string value, Location location +where n.properties(key, value, location) +select n, key, value, location diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/nodes.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/nodes.expected new file mode 100644 index 000000000000..4fe61c5fedca --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/nodes.expected @@ -0,0 +1,67 @@ +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/sec/SECNamedCurves.java:11:16:11:26 | EllipticCurve | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:31:11:41 | EllipticCurve | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:44:11:54 | EllipticCurve | +| ../../../../../stubs/bcprov-lts8on-2.73.7/org/bouncycastle/jce/ECNamedCurveTable.java:11:57:11:67 | EllipticCurve | +| ECDSASignAndVerify.java:31:30:31:54 | Constant | +| ECDSASignAndVerify.java:47:28:47:38 | EllipticCurve | +| ECDSASignAndVerify.java:57:16:57:49 | Key | +| ECDSASignAndVerify.java:57:16:57:49 | KeyGeneration | +| ECDSASignAndVerify.java:65:28:65:38 | EllipticCurve | +| ECDSASignAndVerify.java:80:16:80:49 | Key | +| ECDSASignAndVerify.java:80:16:80:49 | KeyGeneration | +| ECDSASignAndVerify.java:89:28:89:38 | EllipticCurve | +| ECDSASignAndVerify.java:103:16:103:49 | Key | +| ECDSASignAndVerify.java:103:16:103:49 | KeyGeneration | +| ECDSASignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | +| ECDSASignAndVerify.java:115:27:115:36 | Key | +| ECDSASignAndVerify.java:116:44:116:76 | SignOperation | +| ECDSASignAndVerify.java:116:44:116:76 | SignatureOutput | +| ECDSASignAndVerify.java:116:69:116:75 | Message | +| ECDSASignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | +| ECDSASignAndVerify.java:120:30:120:38 | Key | +| ECDSASignAndVerify.java:121:28:121:88 | VerifyOperation | +| ECDSASignAndVerify.java:121:53:121:59 | Message | +| ECDSASignAndVerify.java:121:62:121:73 | SignatureInput | +| ECDSASignAndVerify.java:121:76:121:87 | SignatureInput | +| Ed448SignAndVerify.java:19:54:19:80 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:21:47:21:80 | Key | +| Ed448SignAndVerify.java:21:47:21:80 | KeyGeneration | +| Ed448SignAndVerify.java:26:30:26:54 | Constant | +| Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:30:31:30:40 | Key | +| Ed448SignAndVerify.java:31:27:31:33 | Message | +| Ed448SignAndVerify.java:32:32:32:57 | SignOperation | +| Ed448SignAndVerify.java:32:32:32:57 | SignatureOutput | +| Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | +| Ed448SignAndVerify.java:38:34:38:42 | Key | +| Ed448SignAndVerify.java:39:29:39:35 | Message | +| Ed448SignAndVerify.java:40:32:40:66 | VerifyOperation | +| Ed448SignAndVerify.java:40:57:40:65 | SignatureInput | +| Ed25519SignAndVerify.java:19:56:19:84 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:21:47:21:80 | Key | +| Ed25519SignAndVerify.java:21:47:21:80 | KeyGeneration | +| Ed25519SignAndVerify.java:26:30:26:56 | Constant | +| Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:30:31:30:40 | Key | +| Ed25519SignAndVerify.java:31:27:31:33 | Message | +| Ed25519SignAndVerify.java:32:32:32:57 | SignOperation | +| Ed25519SignAndVerify.java:32:32:32:57 | SignatureOutput | +| Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | +| Ed25519SignAndVerify.java:38:34:38:42 | Key | +| Ed25519SignAndVerify.java:39:29:39:35 | Message | +| Ed25519SignAndVerify.java:40:32:40:66 | VerifyOperation | +| Ed25519SignAndVerify.java:40:57:40:65 | SignatureInput | +| LMSSignAndVerify.java:31:46:31:70 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:33:47:33:74 | Key | +| LMSSignAndVerify.java:33:47:33:74 | KeyGeneration | +| LMSSignAndVerify.java:38:30:38:52 | Constant | +| LMSSignAndVerify.java:41:32:41:46 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:42:31:42:40 | Key | +| LMSSignAndVerify.java:43:32:43:64 | SignOperation | +| LMSSignAndVerify.java:43:32:43:64 | SignatureOutput | +| LMSSignAndVerify.java:43:57:43:63 | Message | +| LMSSignAndVerify.java:46:34:46:48 | KeyOperationAlgorithm | +| LMSSignAndVerify.java:47:34:47:42 | Key | +| LMSSignAndVerify.java:48:32:48:75 | VerifyOperation | +| LMSSignAndVerify.java:48:57:48:63 | Message | +| LMSSignAndVerify.java:48:66:48:74 | SignatureInput | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/nodes.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/nodes.ql new file mode 100644 index 000000000000..e080ce7297ac --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/nodes.ql @@ -0,0 +1,5 @@ +import java +import experimental.quantum.Language + +from Crypto::NodeBase n +select n diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected deleted file mode 100644 index 86d161c64887..000000000000 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected +++ /dev/null @@ -1,8 +0,0 @@ -| ECDSASignAndVerify.java:116:44:116:76 | SignOperation | ECDSASignAndVerify.java:114:30:114:46 | KeyOperationAlgorithm | ECDSASignAndVerify.java:115:27:115:36 | Key | ECDSASignAndVerify.java:116:69:116:75 | Message | | SignatureOutput | -| ECDSASignAndVerify.java:121:28:121:88 | VerifyOperation | ECDSASignAndVerify.java:119:32:119:48 | KeyOperationAlgorithm | ECDSASignAndVerify.java:120:30:120:38 | Key | ECDSASignAndVerify.java:121:53:121:59 | Message | SignatureInput | | -| Ed448SignAndVerify.java:32:32:32:57 | SignOperation | Ed448SignAndVerify.java:29:34:29:70 | KeyOperationAlgorithm | Ed448SignAndVerify.java:30:31:30:40 | Key | Ed448SignAndVerify.java:31:27:31:33 | Message | | SignatureOutput | -| Ed448SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed448SignAndVerify.java:37:36:37:72 | KeyOperationAlgorithm | Ed448SignAndVerify.java:38:34:38:42 | Key | Ed448SignAndVerify.java:39:29:39:35 | Message | SignatureInput | | -| Ed25519SignAndVerify.java:32:32:32:57 | SignOperation | Ed25519SignAndVerify.java:29:36:29:54 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:30:31:30:40 | Key | Ed25519SignAndVerify.java:31:27:31:33 | Message | | SignatureOutput | -| Ed25519SignAndVerify.java:40:32:40:66 | VerifyOperation | Ed25519SignAndVerify.java:37:38:37:56 | KeyOperationAlgorithm | Ed25519SignAndVerify.java:38:34:38:42 | Key | Ed25519SignAndVerify.java:39:29:39:35 | Message | SignatureInput | | -| LMSSignAndVerify.java:43:32:43:64 | SignOperation | LMSSignAndVerify.java:41:32:41:46 | KeyOperationAlgorithm | LMSSignAndVerify.java:42:31:42:40 | Key | LMSSignAndVerify.java:43:57:43:63 | Message | | SignatureOutput | -| LMSSignAndVerify.java:48:32:48:75 | VerifyOperation | LMSSignAndVerify.java:46:34:46:48 | KeyOperationAlgorithm | LMSSignAndVerify.java:47:34:47:42 | Key | LMSSignAndVerify.java:48:57:48:63 | Message | SignatureInput | | diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.ql b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.ql deleted file mode 100644 index 9eda60a6a0a8..000000000000 --- a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.ql +++ /dev/null @@ -1,22 +0,0 @@ -import java -import experimental.quantum.Language - -string getASignatureInput(Crypto::SignatureOperationNode n) { - exists(Crypto::SignatureArtifactNode input | - input = n.getASignatureArtifact() and result = input.toString() - ) - or - not exists(n.getASignatureArtifact()) and result = "" -} - -string getASignatureOutput(Crypto::SignatureOperationNode n) { - exists(Crypto::KeyOperationOutputNode output | - output = n.getAnOutputArtifact() and result = output.toString() - ) - or - not exists(n.getAnOutputArtifact()) and result = "" -} - -from Crypto::SignatureOperationNode n -select n, n.getAKnownAlgorithm(), n.getAKey(), n.getAnInputArtifact(), getASignatureInput(n), - getASignatureOutput(n)