diff --git a/java/ql/lib/experimental/quantum/BouncyCastle.qll b/java/ql/lib/experimental/quantum/BouncyCastle.qll new file mode 100644 index 000000000000..66ae785a8275 --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle.qll @@ -0,0 +1,3 @@ +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 new file mode 100644 index 000000000000..cac8ff02afa2 --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmInstances.qll @@ -0,0 +1,358 @@ +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. + */ +class EllipticCurveStringLiteralInstance extends Crypto::EllipticCurveInstance instanceof StringLiteral +{ + EllipticCurveStringLiteralInstance() { + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(super.getValue().toUpperCase(), _, _) + } + + override string getRawEllipticCurveName() { result = super.getValue() } + + Crypto::AlgorithmValueConsumer getConsumer() { + result = EllipticCurveStringLiteralToConsumerFlow::getConsumerFromLiteral(this, _, _) + } + + override Crypto::TEllipticCurveType getEllipticCurveType() { + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName().toUpperCase(), + _, result) + } + + override int getKeySize() { + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName().toUpperCase(), + result, _) + } +} + +/** + * A signature algorithm instance where the algorithm is implicitly defined by + * the constructed type. + */ +class ImplicitSignatureClassInstanceExpr extends Crypto::KeyOperationAlgorithmInstance, + ImplicitAlgorithmValueConsumer instanceof ClassInstanceExpr +{ + ImplicitSignatureClassInstanceExpr() { super.getConstructedType() instanceof Signers::Signer } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + signatureNameToAlgorithmMapping(this.getRawAlgorithmName(), result) + } + + override int getKeySizeFixed() { + signatureNameToKeySizeMapping(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() } + + // Used for data flow from elliptic curve string literals to the algorithm + // instance. + DataFlow::Node getEllipticCurveInput() { none() } +} + +/** + * A key generation algorithm instance where algorithm is a key operation (e.g. + * a signature algorithm) implicitly defined by the constructed type. + */ +class ImplicitKeyGenerationClassInstanceExpr extends Crypto::KeyOperationAlgorithmInstance, + ImplicitAlgorithmValueConsumer instanceof ClassInstanceExpr +{ + ImplicitKeyGenerationClassInstanceExpr() { + super.getConstructedType() instanceof Generators::KeyGenerator and + super.getConstructedType().getName().matches(["Ed25519%", "Ed448%", "LMS%", "HSS%"]) + } + + override string getRawAlgorithmName() { + typeNameToRawAlgorithmNameMapping(super.getConstructedType().getName(), result) + } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + generatorNameToAlgorithmMapping(this.getRawAlgorithmName(), result) + } + + override int getKeySizeFixed() { + generatorNameToKeySizeMapping(this.getRawAlgorithmName(), result) + } + + // 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 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, + ImplicitAlgorithmValueConsumer 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 typeNameToRawAlgorithmNameMapping(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" + or + // ECDSA + typeName.matches("ECDSA%") and + algorithmName = "ECDSA" + or + // DSA + typeName.matches("DSA%") and + algorithmName = "DSA" + or + // LMS + typeName.matches("LMS%") and + algorithmName = "LMS" + or + // 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 signatureNameToAlgorithmMapping( + string signatureName, Crypto::KeyOpAlg::Algorithm algorithmType +) { + signatureName = "Ed25519" and + algorithmType = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519()) + or + 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 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 +) { + 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 + generatorName = "Ed448" and + keySize = 448 +} + +private predicate blockCipherNameToAlgorithmMapping( + string cipherName, Crypto::KeyOpAlg::Algorithm algorithmType +) { + cipherName = "AES" and + algorithmType = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES()) + or + cipherName = "Aria" and + algorithmType = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::ARIA()) + or + cipherName = "Blowfish" and + algorithmType = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::BLOWFISH()) + or + cipherName = "DES" and + algorithmType = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES()) + or + 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 new file mode 100644 index 000000000000..b8bc22b6dfac --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle/AlgorithmValueConsumers.qll @@ -0,0 +1,47 @@ +private import java +private import experimental.quantum.Language +private import AlgorithmValueConsumers +private import AlgorithmInstances + +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() + } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(EllipticCurveStringLiteralInstance).getConsumer() = this + } +} + +/** + * An AVC representing the block cipher argument passed to an block cipher mode + * constructor. + */ +class BlockCipherAlgorithmArg extends Crypto::AlgorithmValueConsumer instanceof Expr { + BlockCipherAlgorithmArg() { + this = any(BlockCipherModeAlgorithmInstance mode).getBlockCipherArg() + } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(BlockCipherAlgorithmInstance).getConsumer() = this + } +} + +/** + * An AVC for an algorithm that is implicitly defined by the instance. + */ +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 new file mode 100644 index 000000000000..40865ac601c5 --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle/FlowAnalysis.qll @@ -0,0 +1,590 @@ +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 + * constructor calls used in BouncyCastle. + */ +signature class NewCallSig instanceof Call { + /** + * Gets a parameter argument that is used to initialize the object. + */ + 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. + */ + DataFlow::Node getParametersInput(); +} + +signature class UseCallSig instanceof MethodCall { + /** + * Holds if the use is not a final use, such as an `update()` call before `doFinal()`. + */ + predicate isIntermediate(); +} + +/** + * A generic analysis module for analyzing data flow from class instantiation, + * to `init()`, to `doOperation()` in BouncyCastle, and similar patterns in + * other libraries. + * + * Example: + * ``` + * gen = new KeyGenerator(...); + * gen.init(...); + * gen.generateKeyPair(...); + * ``` + */ +module NewToInitToUseFlow { + private newtype TFlowState = + TUninitialized() or + TInitialized(Init call) or + TIntermediateUse(Use call) + + abstract private class InitFlowState extends TFlowState { + string toString() { + this = TUninitialized() and result = "Uninitialized" + or + this = TInitialized(_) and result = "Initialized" + or + this = TIntermediateUse(_) and result = "Intermediate" + } + } + + // The flow state is uninitialized if the `init` call is not yet made. + private class UninitializedFlowState extends InitFlowState, TUninitialized { } + + private class InitializedFlowState extends InitFlowState, TInitialized { + Init call; + DataFlow::Node node1; + DataFlow::Node node2; // The receiver of the `init` call + + 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 } + } + + private class IntermediateUseState extends InitFlowState, TIntermediateUse { + Use call; + DataFlow::Node node1; // The receiver of the method call + 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 } + } + + 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 (to 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() | + // Ensures that the receiver of a call to `init` is tracked as initialized. + not state instanceof InitializedFlowState + or + // Ensures that call tracked by the state is the last call to `init`. + state.(InitializedFlowState).getInitCall() != call + ) + } + } + + private module NewToInitToUseFlow = DataFlow::GlobalWithState; + + New getNewFromUse(Use use, NewToInitToUseFlow::PathNode src, NewToInitToUseFlow::PathNode sink) { + src.getNode().asExpr() = result and + sink.getNode().asExpr() = use.(MethodCall).getQualifier() and + NewToInitToUseFlow::flowPath(src, sink) + } + + New getNewFromInit(Init init, NewToInitToUseFlow::PathNode src, NewToInitToUseFlow::PathNode sink) { + src.getNode().asExpr() = result and + sink.getNode().asExpr() = init.(MethodCall).getQualifier() and + NewToInitToUseFlow::flowPath(src, sink) + } + + Init getInitFromNew(New new, NewToInitToUseFlow::PathNode src, NewToInitToUseFlow::PathNode sink) { + src.getNode().asExpr() = new and + sink.getNode().asExpr() = result.(MethodCall).getQualifier() and + NewToInitToUseFlow::flowPath(src, 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 + NewToInitToUseFlow::flowPath(src, sink) + } + + predicate hasInit(Use use) { exists(getInitFromUse(use, _, _)) } + + Use getAnIntermediateUseFromFinalUse( + 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 + NewToInitToUseFlow::flowPath(src, sink) + } +} + +/** + * An `ECCurve` instance constructed by a call to either of the methods + * `X9ECParameters.getCurve()` or `ECNamedCurveParameterSpec.getCurve()`. + */ +private class CurveInstantiation extends MethodCall { + CurveInstantiation() { + this.getCallee().getDeclaringType() instanceof Params::Parameters and + this.getCallee().getName() = "getCurve" + } + + DataFlow::Node getInputNode() { result.asExpr() = this.getQualifier() } + + DataFlow::Node getOutputNode() { result.asExpr() = this } +} + +/** + * A flow analysis module for analyzing data flow from the instantiation of a + * parameter object to an `init()` call in BouncyCastle, and similar patterns in + * other libraries. + * + * Example: + * ``` + * params = new ECKeyGenerationParameters(...); + * gen = new ECKeyPairGenerator(); + * gen.init(params); + * ``` + * + * TODO: Rewrite using stateful data flow to track whether or not the node + * represents a parameter object or a curve. + */ +module ParametersToInitFlow { + 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()) } + + /** + * 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 + * determine the curve associated with the generator. + * + * Example: + * ``` + * X9ECParameters ecParams = SECNamedCurves.getByName("secp256r1"); + * ECDomainParameters domainParams = new ECDomainParameters(ecParams); + * 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) { + // 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() + ) + } + } + + private module ParametersToInitFlow = DataFlow::Global; + + /** + * Gets a parameter instantiation from a call to `init()`. + */ + New getParametersFromInit( + Init init, ParametersToInitFlow::PathNode src, ParametersToInitFlow::PathNode sink + ) { + src.getNode().asExpr() = result and + sink.getNode() = init.getParametersInput() and + ParametersToInitFlow::flowPath(src, sink) + } +} + +/** + * A model for 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") + ) + } + + DataFlow::Node getInputNode() { result.asExpr() = this.getQualifier() } + + DataFlow::Node getOutputNode() { result.asExpr() = this } +} + +/** + * 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() { + this.getArray().getType().getName() = "BigInteger[]" and + // It is reasonable to assume that the indices are integer literals + this.getIndexExpr().(IntegerLiteral).getValue().toInt() = [0, 1] + } + + DataFlow::Node getInputNode() { + // The ECDSA signature is represented as a `BigInteger` array. + result.asExpr() = this.getArray() + } + + 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) { + exists(KeyAdditionalFlowSteps fs | + node1 = fs.getInputNode() and + node2 = fs.getOutputNode() + ) + or + 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; + + ArtifactAdditionalFlowStep() { additionalFlowSteps(this, output) } + + override DataFlow::Node getOutput() { result = output } +} + +module EllipticCurveStringLiteralToConsumerFlow { + /** + * A flow from a known elliptic curve name to an elliptic curve algorithm consumer. + */ + 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 } + + predicate isSink(DataFlow::Node sink) { + exists(Crypto::AlgorithmValueConsumer 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() + } + } + + private module EllipticCurveStringLiteralToAlgorithmValueConsumerFlow = + DataFlow::Global; + + Crypto::AlgorithmValueConsumer getConsumerFromLiteral( + StringLiteral literal, EllipticCurveStringLiteralToAlgorithmValueConsumerFlow::PathNode src, + EllipticCurveStringLiteralToAlgorithmValueConsumerFlow::PathNode sink + ) { + src.getNode().asExpr() = literal and + sink.getNode() = result.getInputNode() and + 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 new file mode 100644 index 000000000000..16aa89c79293 --- /dev/null +++ b/java/ql/lib/experimental/quantum/BouncyCastle/OperationInstances.qll @@ -0,0 +1,742 @@ +private import java +private import experimental.quantum.Language +private import FlowAnalysis +private import AlgorithmInstances + +module Params { + /** + * A model of the `Parameters` class in Bouncy Castle. + */ + class Parameters extends Class { + Parameters() { + // Matches `org.bouncycastle.crypto.params`, `org.bouncycastle.asn1.x9`, etc. + this.getPackage().getName().matches("org.bouncycastle.%") and + this.getName().matches(["%Parameter", "%Parameters", "%ParameterSpec", "ParametersWith%"]) + } + } + + class Curve extends Class { + Curve() { + this.getPackage().hasName("org.bouncycastle.math.ec") and + this.hasName("ECCurve") + } + } + + class KeyParameters extends Parameters { + KeyParameters() { + this.getPackage() + .hasName(["org.bouncycastle.crypto.params", "org.bouncycastle.pqc.crypto.lms"]) and + this.getName().matches(["%KeyParameter", "%KeyParameters"]) + } + } + + /** + * A 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 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() } + + // 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 getKeyConsumer() { result.asExpr() = this.getKeyArg() } + + Crypto::ConsumerInputDataFlowNode getNonceConsumer() { result.asExpr() = this.getNonceArg() } + + 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() } + } + + /** + * A 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) + } + } + + /** + * A 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) + } + } + + /** + * 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) } + } + + /** + * 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" + } + + 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) } + } +} + +/** + * Models for the signature algorithms defined by the `org.bouncycastle.crypto.signers` package. + */ +module Signers { + /** + * 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 Class { + 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 + } + } + + /** + * 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%"]) } + + 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. + // For LMS, the signature is passed as a single argument in position 1. + 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 = ImplicitSignatureClassInstanceExpr; + + /** + * 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() } + } + + /** + * 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 = NewToInitToUseFlow; + + /** + * 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 { + /** + * A model of the `KeyGenerator` and `KeyPairGenerator` classes in Bouncy Castle. + */ + class KeyGenerator extends Class { + 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 } + } + + /** + * 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. + */ + 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 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. + * + * 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 { + KeyGeneratorInitCall() { this = any(KeyGenerator gen).getAnInitCall() } + + Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + // The `KeyGenerationParameters` argument used to configure the key generator. + DataFlow::Node getParametersInput() { result.asExpr() = this.getArgument(0) } + } + + /** + * A call to either `generateKey()` and `generateKeyPair()`. + */ + private class KeyGeneratorUseCall extends MethodCall { + KeyGenerator gen; + + KeyGeneratorUseCall() { this = gen.getAUseCall() } + + // 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() } + + Expr getOutput() { result = this } + } + + private module KeyGeneratorFlow = + NewToInitToUseFlow; + + private module ParametersFlow = + ParametersToInitFlow; + + /** + * 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() { + // 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, _, _) + } + + override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { + result.asExpr() = super.getOutput() + } + + override Crypto::KeyArtifactType getOutputKeyType() { result = super.getKeyType() } + + override int 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() { + result = KeyGeneratorFlow::getInitFromUse(this, _, _).getKeySizeConsumer() + } + + Params::ParametersInstantiation getParameters() { + exists(KeyGeneratorInitCall init | + init = KeyGeneratorFlow::getInitFromUse(this, _, _) and + result = ParametersFlow::getParametersFromInit(init, _, _) + ) + } + } + + /** + * 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 { + import FlowAnalysis + + 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") + } + + 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 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 + } + } + + /** + * A block cipher engine, like `AESEngine`. + */ + class BlockCipher extends Class { + BlockCipher() { + this.getPackage().getName() = "org.bouncycastle.crypto.engines" and + this.getName().matches("%Engine") + } + } + + /** + * A block cipher mode instantiation. + * + * 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.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() } + + 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 { + BlockCipherModeInitCall() { this = any(BlockCipherMode mode).getAnInitCall() } + + // This is true if the mode is being initialized for encryption. + Expr getEncryptingArg() { result = this.getArgument(0) } + + 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() } + + // 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 { + BlockCipherModeUseCall() { this = any(BlockCipherMode mode).getAUseCall() } + + predicate isIntermediate() { not this.getCallee().getName() = "doFinal" } + + Expr getInput() { result = this.getArgument(0) and this.isIntermediate() } + + 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. + or + this.getCallee().getName() = "doFinal" and + result = this.getArgument(0) // The `out` byte array argument. + } + } + + module BlockCipherModeFlow = + 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() + } + } +} 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 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/modes/options b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/options new file mode 100644 index 000000000000..1f8370de49a0 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/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/modes/random_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/modes/random_artifacts.expected new file mode 100644 index 000000000000..873907aec4a9 --- /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: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 new file mode 100644 index 000000000000..3f27ed82e7a5 --- /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 n.getSourceNode() instanceof Crypto::RandomNumberGenerationNode +select n, n.getSourceNode() diff --git a/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/ECDSASignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/ECDSASignAndVerify.java new file mode 100644 index 000000000000..79c0c99a20de --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/ECDSASignAndVerify.java @@ -0,0 +1,123 @@ +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; +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.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; + +/** + * Test Bouncy Castle's low-level ECDSA API + */ +public class ECDSASignAndVerify { + + public static void main(String[] args) { + // Add Bouncy Castle provider + Security.addProvider(new BouncyCastleProvider()); + + try { + byte[] message = "Hello, ECDSA signature!".getBytes("UTF-8"); + + // 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/signers/Ed25519SignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/Ed25519SignAndVerify.java new file mode 100644 index 000000000000..ffa6c88a86f3 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/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/signers/Ed448SignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/Ed448SignAndVerify.java new file mode 100644 index 000000000000..68188f14b213 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/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/signers/LMSSignAndVerify.java b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/LMSSignAndVerify.java new file mode 100644 index 000000000000..a99c4516aab5 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/LMSSignAndVerify.java @@ -0,0 +1,55 @@ +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 LMSSignAndVerify { + 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"); + + // Sign the message + LMSSigner signer = new LMSSigner(); + signer.init(true, privateKey); // true for signing + byte[] signature = signer.generateSignature(message); + + // Verify the signature + LMSSigner verifier = new LMSSigner(); + verifier.init(false, publicKey); + boolean verified = verifier.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/signers/key_artifacts.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected new file mode 100644 index 000000000000..213d8d1baaed --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_artifacts.expected @@ -0,0 +1,5 @@ +| 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 new file mode 100644 index 000000000000..394c82d70fce --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/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/signers/key_generation_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected new file mode 100644 index 000000000000..a07e0f8d8abb --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/key_generation_operations.expected @@ -0,0 +1,5 @@ +| 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 new file mode 100644 index 000000000000..30447906aed0 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/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/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/signers/signature_operations.expected b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected new file mode 100644 index 000000000000..86d161c64887 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.expected @@ -0,0 +1,8 @@ +| 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 new file mode 100644 index 000000000000..9eda60a6a0a8 --- /dev/null +++ b/java/ql/test/experimental/library-tests/quantum/BouncyCastle/signers/signature_operations.ql @@ -0,0 +1,22 @@ +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) 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. 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..88c2c8d77c8b --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/asn1/x9/X9ECParameters.java @@ -0,0 +1,29 @@ +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 ECCurve getCurve() { + return new ECCurve(); + } + + public ECPoint getG() { + return new ECPoint(); + } + + 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/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/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/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/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/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/ECDomainParameters.java b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECDomainParameters.java new file mode 100644 index 000000000000..127792dc9528 --- /dev/null +++ b/java/ql/test/stubs/bcprov-lts8on-2.73.7/org/bouncycastle/crypto/params/ECDomainParameters.java @@ -0,0 +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/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/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/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(); + } +} 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; + } +} 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/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..f6fbad27e74e --- /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", "secp256k1", "secp384r1" }; + } +} 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 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; + } +} 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() { } +} diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index e7bbe65d3115..8e81b6a03e21 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. * @@ -599,6 +606,9 @@ module CryptographyBase Input> { ECDSA() or Ed25519() or Ed448() or + LMS() or + HSS() or + MLDSA() or OtherSignatureAlgorithmType() newtype TKEMAlgorithmType = @@ -707,6 +717,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 @@ -800,6 +814,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. */ @@ -881,6 +897,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 = @@ -1408,6 +1429,25 @@ 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 + * 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 + other.asElement() = node.asElement() + ) + } + /** * A cryptographic operation, such as hashing or encryption. */ @@ -1433,7 +1473,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) { @@ -1622,6 +1663,8 @@ module CryptographyBase Input> { */ private class KeyCreationCandidateAlgorithmNode extends TKeyCreationCandidateAlgorithm instanceof AlgorithmNode { + LocatableElement asElement() { result = super.asElement() } + string toString() { result = super.getAlgorithmName() } } @@ -1664,7 +1707,8 @@ module CryptographyBase Input> { KeyCreationCandidateAlgorithmNode getAKnownAlgorithm() { result = - instance.(KeyCreationOperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() + instance.(KeyCreationOperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() and + not isConsumedEllipticCurveNode(result) } override NodeBase getChild(string edgeName) { @@ -2363,6 +2407,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 @@ -2383,6 +2447,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) {