From 0f874e4d7c0386e649639f4d93554297ff542f6f Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Thu, 22 May 2025 14:35:14 -0400 Subject: [PATCH 01/21] Crypto: Adding initial openssl tests, fixing a bug in hash modeling found through tests, and updating CODEOWNERS for quantum tests --- CODEOWNERS | 2 +- .../experimental/quantum/OpenSSL/CtxFlow.qll | 14 +- .../OpenSSL/Operations/EVPCipherOperation.qll | 4 +- .../OpenSSL/Operations/EVPHashOperation.qll | 15 +- .../openssl/cipher_key_sources.expected | 2 + .../quantum/openssl/cipher_key_sources.ql | 6 + .../openssl/cipher_nonce_sources.expected | 2 + .../quantum/openssl/cipher_nonce_sources.ql | 6 + .../openssl/cipher_operations.expected | 8 + .../quantum/openssl/cipher_operations.ql | 6 + .../openssl/cipher_plaintext_sources.expected | 1 + .../openssl/cipher_plaintext_sources.ql | 6 + .../openssl/hash_input_sources.expected | 2 + .../quantum/openssl/hash_input_sources.ql | 6 + .../quantum/openssl/hash_operations.expected | 2 + .../quantum/openssl/hash_operations.ql | 5 + .../openssl/includes/alg_macro_stubs.h | 3741 +++++++++++++ .../quantum/openssl/includes/evp_stubs.h | 4986 +++++++++++++++++ .../quantum/openssl/includes/rand_stubs.h | 3 + .../quantum/openssl/openssl_basic.c | 221 + 20 files changed, 9028 insertions(+), 10 deletions(-) create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_key_sources.expected create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_key_sources.ql create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_nonce_sources.expected create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_nonce_sources.ql create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.ql create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_plaintext_sources.expected create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_plaintext_sources.ql create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/hash_input_sources.expected create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/hash_input_sources.ql create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/hash_operations.expected create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/hash_operations.ql create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/includes/alg_macro_stubs.h create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/includes/evp_stubs.h create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/includes/rand_stubs.h create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/openssl_basic.c diff --git a/CODEOWNERS b/CODEOWNERS index 96aa46df9495..7233623d4528 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -16,7 +16,7 @@ /java/ql/test-kotlin2/ @github/codeql-kotlin # Experimental CodeQL cryptography -**/experimental/quantum/ @github/ps-codeql +**/experimental/**/quantum/ @github/ps-codeql /shared/quantum/ @github/ps-codeql # CodeQL tools and associated docs diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll index cbce19fb5dfe..38b49b8d9010 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll @@ -29,7 +29,19 @@ import semmle.code.cpp.dataflow.new.DataFlow * - EVP_PKEY_CTX */ private class CtxType extends Type { - CtxType() { this.getUnspecifiedType().stripType().getName().matches("evp_%ctx_%st") } + CtxType() { + // It is possible for users to use the underlying type of the CTX variables + // these have a name matching 'evp_%ctx_%st + this.getUnspecifiedType().stripType().getName().matches("evp_%ctx_%st") + or + // In principal the above check should be sufficient, but in case of build mode none issues + // i.e., if a typedef cannot be resolved, + // or issues with properly stubbing test cases, we also explicitly check for the wrapping type defs + // i.e., patterns matching 'EVP_%_CTX' + exists(Type base | base = this or base = this.(DerivedType).getBaseType() | + base.getName().matches("EVP_%_CTX") + ) + } } /** diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll index bb884f6db530..233bfd504338 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll @@ -10,7 +10,7 @@ private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { } predicate isSink(DataFlow::Node sink) { - exists(EVP_Cipher_Operation c | c.getInitCall().getAlgorithmArg() = sink.asExpr()) + exists(EVP_Cipher_Operation c | c.getAlgorithmArg() = sink.asExpr()) } } @@ -32,6 +32,8 @@ private module AlgGetterToAlgConsumerFlow = DataFlow::Global 0) { + // Success + plaintext_len += len; + return plaintext_len; + } else { + // Verification failed + return -1; + } +} + +// Function to calculate SHA-256 hash +int calculate_sha256(const unsigned char *message, size_t message_len, + unsigned char *digest) { + EVP_MD_CTX *mdctx; + unsigned int digest_len; + + // Create and initialize the context + if(!(mdctx = EVP_MD_CTX_new())) + return 0; + + // Initialize the hash operation + if(1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL)) + return 0; + + // Provide the message to be hashed + if(1 != EVP_DigestUpdate(mdctx, message, message_len)) + return 0; + + // Finalize the hash + if(1 != EVP_DigestFinal_ex(mdctx, digest, &digest_len)) + return 0; + + // Clean up + EVP_MD_CTX_free(mdctx); + + return 1; +} + +// Function to generate random bytes +int generate_random_bytes(unsigned char *buffer, size_t length) { + return RAND_bytes(buffer, length); +} + +// Function using direct EVP_Digest function (one-shot hash) +int calculate_md5_oneshot(const unsigned char *message, size_t message_len, + unsigned char *digest) { + unsigned int digest_len; + + // Calculate MD5 in a single call + if(1 != EVP_Digest(message, message_len, digest, &digest_len, EVP_md5(), NULL)) + return 0; + + return 1; +} + +// Function using HMAC +int calculate_hmac_sha256(const unsigned char *key, size_t key_len, + const unsigned char *message, size_t message_len, + unsigned char *mac) { + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + EVP_PKEY *pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, key_len); + + if (!ctx || !pkey) + return 0; + + if (EVP_DigestSignInit(ctx, NULL, EVP_sha256(), NULL, pkey) != 1) + return 0; + + if (EVP_DigestSignUpdate(ctx, message, message_len) != 1) + return 0; + + size_t mac_len = 32; // SHA-256 output size + if (EVP_DigestSignFinal(ctx, mac, &mac_len) != 1) + return 0; + + EVP_MD_CTX_free(ctx); + EVP_PKEY_free(pkey); + + return 1; +} + +// Test function +int test_main() { + // Test encryption and decryption + unsigned char *key = (unsigned char *)"01234567890123456789012345678901"; // 32 bytes + unsigned char *iv = (unsigned char *)"0123456789012345"; // 16 bytes + unsigned char *plaintext = (unsigned char *)"This is a test message for encryption"; + unsigned char ciphertext[1024]; + unsigned char tag[16]; + unsigned char decrypted[1024]; + int plaintext_len = strlen((char *)plaintext); + int ciphertext_len; + int decrypted_len; + + // Test SHA-256 hash + unsigned char hash[32]; + + // Test random generation + unsigned char random_bytes[32]; + + // // Initialize OpenSSL + // ERR_load_crypto_strings(); + + // Encrypt data + ciphertext_len = encrypt_aes_gcm(plaintext, plaintext_len, key, iv, 16, ciphertext, tag); + + // Decrypt data + decrypted_len = decrypt_aes_gcm(ciphertext, ciphertext_len, tag, key, iv, 16, decrypted); + + //printf("decrypted: %s\n", decrypted); + + // Calculate hash + calculate_sha256(plaintext, plaintext_len, hash); + + // Generate random bytes + generate_random_bytes(random_bytes, 32); + + // Calculate one-shot MD5 + unsigned char md5_hash[16]; + calculate_md5_oneshot(plaintext, plaintext_len, md5_hash); + + // Calculate HMAC + unsigned char hmac[32]; + calculate_hmac_sha256(key, 32, plaintext, plaintext_len, hmac); + + return 0; +} \ No newline at end of file From 10d8504700c68ada17240996c25710670f7c07f7 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 23 May 2025 18:09:39 +0200 Subject: [PATCH 02/21] refactor EVP common classes add initial work for openssl signatures add basic C test files for ciphers and signatures more signature classes, comments for evp base classes more signature tests fix super calls for input consumers fix getOutputArtifact for tests formatting delete redundant test files move algorithm methods to OpenSSLOperation refactor ECKeyGenOperation for new EVP classes formatting fix getOutputArtifact fix cipher and digest operation test results mv openssl signature to another PR --- .../OpenSSL/Operations/ECKeyGenOperation.qll | 31 +--- .../Operations/EVPCipherInitializer.qll | 14 +- .../OpenSSL/Operations/EVPCipherOperation.qll | 89 ++-------- .../OpenSSL/Operations/EVPHashInitializer.qll | 7 +- .../OpenSSL/Operations/EVPHashOperation.qll | 100 ++++-------- .../Operations/OpenSSLOperationBase.qll | 152 +++++++++++++++++- .../OpenSSL/Operations/OpenSSLOperations.qll | 1 + .../openssl/cipher_operations.expected | 16 +- .../quantum/openssl/hash_operations.expected | 4 +- 9 files changed, 209 insertions(+), 205 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/ECKeyGenOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/ECKeyGenOperation.qll index 4f07ecc0f9e3..40103569cac0 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/ECKeyGenOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/ECKeyGenOperation.qll @@ -4,42 +4,15 @@ private import OpenSSLOperationBase private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers private import semmle.code.cpp.dataflow.new.DataFlow -private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - exists(OpenSSLAlgorithmValueConsumer c | c.getResultNode() = source) - } - - predicate isSink(DataFlow::Node sink) { - exists(ECKeyGenOperation c | c.getAlgorithmArg() = sink.asExpr()) - } -} - -private module AlgGetterToAlgConsumerFlow = DataFlow::Global; - class ECKeyGenOperation extends OpenSSLOperation, Crypto::KeyGenerationOperationInstance { ECKeyGenOperation() { this.(Call).getTarget().getName() = "EC_KEY_generate_key" } - override Expr getOutputArg() { - result = this.(Call) // return value of call - } - - Expr getAlgorithmArg() { result = this.(Call).getArgument(0) } - - override Expr getInputArg() { - // there is no 'input', in the sense that no data is being manipulated by the operation. - // There is an input of an algorithm, but that is not the intention of the operation input arg. - none() - } + override Expr getAlgorithmArg() { result = this.(Call).getArgument(0) } override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() } override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { - result = this.getOutputNode() - } - - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(), - DataFlow::exprNode(this.getAlgorithmArg())) + result.asExpr() = this.(Call).getArgument(0) } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherInitializer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherInitializer.qll index 353a89645ec0..e6e9954a3332 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherInitializer.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherInitializer.qll @@ -5,6 +5,7 @@ private import experimental.quantum.Language private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow +private import OpenSSLOperationBase module EncValToInitEncArgConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] } @@ -34,19 +35,12 @@ Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) { } // TODO: need to add key consumer -abstract class EVP_Cipher_Initializer extends Call { - Expr getContextArg() { result = this.(Call).getArgument(0) } +abstract class EVP_Cipher_Initializer extends EVPInitialize { + override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } - Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } - - abstract Expr getKeyArg(); - - abstract Expr getIVArg(); - - // abstract Crypto::CipherOperationSubtype getCipherOperationSubtype(); abstract Expr getOperationSubtypeArg(); - Crypto::KeyOperationSubtype getCipherOperationSubtype() { + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { if this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%") then result instanceof Crypto::TEncryptMode else diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll index 233bfd504338..fb06543ee5bb 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll @@ -4,36 +4,21 @@ private import EVPCipherInitializer private import OpenSSLOperationBase private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers -private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - exists(OpenSSLAlgorithmValueConsumer c | c.getResultNode() = source) +class EVP_Cipher_Update_Call extends EVPUpdate { + EVP_Cipher_Update_Call() { + this.(Call).getTarget().getName() in [ + "EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate" + ] } - predicate isSink(DataFlow::Node sink) { - exists(EVP_Cipher_Operation c | c.getAlgorithmArg() = sink.asExpr()) - } + override Expr getInputArg() { result = this.(Call).getArgument(3) } } -private module AlgGetterToAlgConsumerFlow = DataFlow::Global; - -// import experimental.quantum.OpenSSL.AlgorithmValueConsumers.AlgorithmValueConsumers -// import OpenSSLOperation -// class EVPCipherOutput extends CipherOutputArtifact { -// EVPCipherOutput() { exists(EVP_Cipher_Operation op | op.getOutputArg() = this) } -// override DataFlow::Node getOutputNode() { result.asDefiningArgument() = this } -// } -// /** * see: https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis * Base configuration for all EVP cipher operations. - * NOTE: cannot extend instance of OpenSSLOperation, as we need to override - * elements of OpenSSLOperation (i.e., we are creating an instance) */ -abstract class EVP_Cipher_Operation extends OpenSSLOperation, Crypto::KeyOperationInstance { - Expr getContextArg() { result = this.(Call).getArgument(0) } - - Expr getAlgorithmArg() { this.getInitCall().getAlgorithmArg() = result } - +abstract class EVP_Cipher_Operation extends EVPOperation, Crypto::KeyOperationInstance { override Expr getOutputArg() { result = this.(Call).getArgument(1) } override Crypto::KeyOperationSubtype getKeyOperationSubtype() { @@ -43,81 +28,39 @@ abstract class EVP_Cipher_Operation extends OpenSSLOperation, Crypto::KeyOperati result instanceof Crypto::TDecryptMode and this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%") or - result = this.getInitCall().getCipherOperationSubtype() and + result = this.getInitCall().getKeyOperationSubtype() and this.(Call).getTarget().getName().toLowerCase().matches("%cipher%") } - EVP_Cipher_Initializer getInitCall() { - CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg()) - } - override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { this.getInitCall().getIVArg() = result.asExpr() } - override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() } - override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { this.getInitCall().getKeyArg() = result.asExpr() + // todo: or track to the EVP_PKEY_CTX_new } - override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() } + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result = EVPOperation.super.getOutputArtifact() + } - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(), - DataFlow::exprNode(this.getInitCall().getAlgorithmArg())) + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result = EVPOperation.super.getInputConsumer() } } -class EVP_Cipher_Call extends EVP_Cipher_Operation { +class EVP_Cipher_Call extends EVPOneShot, EVP_Cipher_Operation { EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } override Expr getInputArg() { result = this.(Call).getArgument(2) } } -// NOTE: not modeled as cipher operations, these are intermediate calls -class EVP_Cipher_Update_Call extends Call { - EVP_Cipher_Update_Call() { - this.(Call).getTarget().getName() in [ - "EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate" - ] - } - - Expr getInputArg() { result = this.(Call).getArgument(3) } - - DataFlow::Node getInputNode() { result.asExpr() = this.getInputArg() } - - Expr getContextArg() { result = this.(Call).getArgument(0) } -} - -class EVP_Cipher_Final_Call extends EVP_Cipher_Operation { +class EVP_Cipher_Final_Call extends EVPFinal, EVP_Cipher_Operation { EVP_Cipher_Final_Call() { this.(Call).getTarget().getName() in [ "EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal", "EVP_DecryptFinal", "EVP_CipherFinal" ] } - - EVP_Cipher_Update_Call getUpdateCalls() { - CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg()) - } - - override Expr getInputArg() { result = this.getUpdateCalls().getInputArg() } - - override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() } -} - -class EVP_PKEY_Operation extends EVP_Cipher_Operation { - EVP_PKEY_Operation() { - this.(Call).getTarget().getName() in ["EVP_PKEY_decrypt", "EVP_PKEY_encrypt"] - } - - override Expr getInputArg() { result = this.(Call).getArgument(3) } - // TODO: how PKEY is initialized is different that symmetric cipher - // Consider making an entirely new class for this and specializing - // the get init call -} - -class EVPCipherInputArgument extends Expr { - EVPCipherInputArgument() { exists(EVP_Cipher_Operation op | op.getInputArg() = this) } } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll index 46d414ece6ce..7309242f198b 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashInitializer.qll @@ -1,10 +1,7 @@ import cpp +private import OpenSSLOperationBase -abstract class EVP_Hash_Initializer extends Call { - Expr getContextArg() { result = this.(Call).getArgument(0) } - - abstract Expr getAlgorithmArg(); -} +abstract class EVP_Hash_Initializer extends EVPInitialize { } class EVP_DigestInit_Variant_Calls extends EVP_Hash_Initializer { EVP_DigestInit_Variant_Calls() { diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll index c68ccd960845..2b798a96b7e2 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll @@ -8,118 +8,78 @@ private import OpenSSLOperationBase private import EVPHashInitializer private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers -// import EVPHashConsumers -abstract class EVP_Hash_Operation extends OpenSSLOperation, Crypto::HashOperationInstance { - Expr getContextArg() { result = this.(Call).getArgument(0) } - - Expr getAlgorithmArg() { result = this.getInitCall().getAlgorithmArg() } - - EVP_Hash_Initializer getInitCall() { - CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg()) - } - - /** - * By default, the algorithm value comes from the init call. - * There are variants where this isn't true, in which case the - * subclass should override this method. - */ - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(), - DataFlow::exprNode(this.getAlgorithmArg())) - } -} - -private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - exists(OpenSSLAlgorithmValueConsumer c | c.getResultNode() = source) - } +class EVP_Digest_Update_Call extends EVPUpdate { + EVP_Digest_Update_Call() { this.(Call).getTarget().getName() in ["EVP_DigestUpdate"] } - predicate isSink(DataFlow::Node sink) { - exists(EVP_Hash_Operation c | c.getAlgorithmArg() = sink.asExpr()) - } + override Expr getInputArg() { result = this.(Call).getArgument(1) } } -private module AlgGetterToAlgConsumerFlow = DataFlow::Global; - //https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis -class EVP_Q_Digest_Operation extends EVP_Hash_Operation { +class EVP_Q_Digest_Operation extends EVPOneShot, Crypto::HashOperationInstance { EVP_Q_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Q_digest" } - //override Crypto::AlgorithmConsumer getAlgorithmConsumer() { } + override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } + override EVP_Hash_Initializer getInitCall() { // This variant of digest does not use an init // and even if it were used, the init would be ignored/undefined none() } - override Expr getOutputArg() { result = this.(Call).getArgument(5) } - override Expr getInputArg() { result = this.(Call).getArgument(3) } - override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() } - - override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() } + override Expr getOutputArg() { result = this.(Call).getArgument(5) } - override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { - // The operation is a direct algorithm consumer - // NOTE: the operation itself is already modeld as a value consumer, so we can - // simply return 'this', see modeled hash algorithm consuers for EVP_Q_Digest - this = result + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result = EVPOneShot.super.getOutputArtifact() } - override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result = EVPOneShot.super.getInputConsumer() + } } -class EVP_Digest_Operation extends EVP_Hash_Operation { +class EVP_Digest_Operation extends EVPOneShot, Crypto::HashOperationInstance { EVP_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Digest" } // There is no context argument for this function override Expr getContextArg() { none() } + override Expr getAlgorithmArg() { result = this.(Call).getArgument(4) } + override EVP_Hash_Initializer getInitCall() { // This variant of digest does not use an init // and even if it were used, the init would be ignored/undefined none() } - override Expr getAlgorithmArg() { result = this.(Call).getArgument(4) } - - override Expr getOutputArg() { result = this.(Call).getArgument(2) } - override Expr getInputArg() { result = this.(Call).getArgument(0) } - override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() } - - override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() } -} - -// NOTE: not modeled as hash operations, these are intermediate calls -class EVP_Digest_Update_Call extends Call { - EVP_Digest_Update_Call() { this.(Call).getTarget().getName() in ["EVP_DigestUpdate"] } - - Expr getInputArg() { result = this.(Call).getArgument(1) } + override Expr getOutputArg() { result = this.(Call).getArgument(2) } - DataFlow::Node getInputNode() { result.asExpr() = this.getInputArg() } + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result = EVPOneShot.super.getOutputArtifact() + } - Expr getContextArg() { result = this.(Call).getArgument(0) } + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result = EVPOneShot.super.getInputConsumer() + } } -class EVP_Digest_Final_Call extends EVP_Hash_Operation { +class EVP_Digest_Final_Call extends EVPFinal, Crypto::HashOperationInstance { EVP_Digest_Final_Call() { this.(Call).getTarget().getName() in [ "EVP_DigestFinal", "EVP_DigestFinal_ex", "EVP_DigestFinalXOF" ] } - EVP_Digest_Update_Call getUpdateCalls() { - CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg()) - } - - override Expr getInputArg() { result = this.getUpdateCalls().getInputArg() } - - override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() } - override Expr getOutputArg() { result = this.(Call).getArgument(1) } - override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() } + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result = EVPFinal.super.getOutputArtifact() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result = EVPFinal.super.getInputConsumer() + } } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll index f9753e92c5d2..9cccd6395a1c 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll @@ -1,21 +1,157 @@ private import experimental.quantum.Language +private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow +private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +/** + * All OpenSSL operations. + */ abstract class OpenSSLOperation extends Crypto::OperationInstance instanceof Call { + /** + * Expression that specifies the algorithm for the operation. + * Will be an argument of the operation in the simplest case. + */ + abstract Expr getAlgorithmArg(); + + /** + * Algorithm is specified in initialization call or is implicitly established by the key. + */ + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(), + DataFlow::exprNode(this.getAlgorithmArg())) + } +} + +/** + * Calls to initialization functions of EVP API. + * These are not operations in the sense of Crypto::OperationInstance, + * but they are used to initialize the context for the operation. + */ +abstract class EVPInitialize extends Call { + /** + * The context argument that ties together initialization, updates and/or final calls. + */ + Expr getContextArg() { result = this.(Call).getArgument(0) } + + /** + * The type of key operation, none if not applicable. + */ + Crypto::KeyOperationSubtype getKeyOperationSubtype() { none() } + + /** + * Explicitly specified algorithm or none if implicit (e.g., established by the key). + * None if not applicable. + */ + Expr getAlgorithmArg() { none() } + + /** + * The key for the operation, none if not applicable. + */ + Expr getKeyArg() { none() } + + /** + * The IV/nonce, none if not applicable. + */ + Expr getIVArg() { none() } +} + +/** + * Calls to update functions of EVP API. + * These are not operations in the sense of Crypto::OperationInstance, + * but they are used to update the context for the operation. + */ +abstract class EVPUpdate extends Call { + /** + * The context argument that ties together initialization, updates and/or final calls. + */ + Expr getContextArg() { result = this.(Call).getArgument(0) } + + /** + * Update calls always have some input data like plaintext or message digest. + */ abstract Expr getInputArg(); +} + +/** + * Flows from algorithm values to operations, specific to OpenSSL + */ +private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(OpenSSLAlgorithmValueConsumer c | c.getResultNode() = source) + } + + predicate isSink(DataFlow::Node sink) { + exists(EVPOperation c | c.getAlgorithmArg() = sink.asExpr()) + } +} + +private module AlgGetterToAlgConsumerFlow = DataFlow::Global; + +/** + * Base class for all operations of the EVP API. + * Currently final calls and one-shot calls are implemented. + * Provides some default methods for Crypto::KeyOperationInstance class + */ +abstract class EVPOperation extends OpenSSLOperation { + /** + * The context argument that ties together initialization, updates and/or final calls. + */ + Expr getContextArg() { result = this.(Call).getArgument(0) } /** - * Can be an argument of a call or a return value of a function. + * Some input data like plaintext or message digest. + * Either argument provided direcly in the call or all arguments that were provided in update calls. + */ + abstract Expr getInputArg(); + + /** + * Some output data like ciphertext or signature. + * Always produced directly by this operation. + * Assumption: output is provided as an argument to the call, never as return value. */ abstract Expr getOutputArg(); - DataFlow::Node getInputNode() { - // Assumed to be default to asExpr - result.asExpr() = this.getInputArg() + /** + * Overwrite with an explicitly specified algorithm or leave base implementation to find it in the initialization call. + */ + override Expr getAlgorithmArg() { + if exists(this.getInitCall()) then result = this.getInitCall().getAlgorithmArg() else none() + } + + /** + * Finds the initialization call, may be none. + */ + EVPInitialize getInitCall() { + CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg()) } - DataFlow::Node getOutputNode() { - if exists(Call c | c.getAnArgument() = this) - then result.asDefiningArgument() = this - else result.asExpr() = this + Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result.asExpr() = this.getOutputArg() } + + /** + * Input consumer is the input argument of the call. + */ + Crypto::ConsumerInputDataFlowNode getInputConsumer() { result.asExpr() = this.getInputArg() } } + +/** + * Final calls of EVP API. + */ +abstract class EVPFinal extends EVPOperation { + /** + * All update calls that were executed before this final call. + */ + EVPUpdate getUpdateCalls() { + CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg()) + } + + /** + * The input data was provided to all update calls. + */ + override Expr getInputArg() { result = this.getUpdateCalls().getInputArg() } +} + +/** + * One-shot calls of EVP API. + */ +abstract class EVPOneShot extends EVPOperation { } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll index f6ff0dd1f077..54ac977ead0d 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll @@ -2,3 +2,4 @@ import OpenSSLOperationBase import EVPCipherOperation import EVPHashOperation import ECKeyGenOperation +import EVPSignatureOperation diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected index 074f86fd4494..d566397eacad 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected @@ -1,8 +1,8 @@ -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:13:40:31 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:13:40:31 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:13:40:31 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:13:40:31 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:11:90:29 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:11:90:29 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:11:90:29 | KeyOperationOutput | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:11:90:29 | KeyOperationOutput | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:36:90:50 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:36:90:50 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:36:90:50 | KeyOperationOutput | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:36:90:50 | KeyOperationOutput | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_operations.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_operations.expected index 9e52ea448856..247c4389bc1a 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_operations.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_operations.expected @@ -1,2 +1,2 @@ -| openssl_basic.c:124:13:124:30 | HashOperation | openssl_basic.c:124:13:124:30 | Digest | openssl_basic.c:116:38:116:47 | HashAlgorithm | openssl_basic.c:120:37:120:43 | Message | -| openssl_basic.c:144:13:144:22 | HashOperation | openssl_basic.c:144:13:144:22 | Digest | openssl_basic.c:144:67:144:73 | HashAlgorithm | openssl_basic.c:144:24:144:30 | Message | +| openssl_basic.c:124:13:124:30 | HashOperation | openssl_basic.c:124:39:124:44 | Digest | openssl_basic.c:116:38:116:47 | HashAlgorithm | openssl_basic.c:120:37:120:43 | Message | +| openssl_basic.c:144:13:144:22 | HashOperation | openssl_basic.c:144:46:144:51 | Digest | openssl_basic.c:144:67:144:73 | HashAlgorithm | openssl_basic.c:144:24:144:30 | Message | From 8b96ec9e4cbc5c52b1694567ace0f486c3498d16 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Wed, 28 May 2025 18:39:15 +0200 Subject: [PATCH 03/21] fix openssl outputs --- .../OpenSSL/Operations/EVPCipherOperation.qll | 11 ++++++++++ .../Operations/OpenSSLOperationBase.qll | 20 +++++++++++++++---- .../OpenSSL/Operations/OpenSSLOperations.qll | 1 - .../openssl/cipher_operations.expected | 8 ++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll index fb06543ee5bb..993b5ad60a92 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll @@ -12,6 +12,8 @@ class EVP_Cipher_Update_Call extends EVPUpdate { } override Expr getInputArg() { result = this.(Call).getArgument(3) } + + override Expr getOutputArg() { result = this.(Call).getArgument(1) } } /** @@ -63,4 +65,13 @@ class EVP_Cipher_Final_Call extends EVPFinal, EVP_Cipher_Operation { "EVP_DecryptFinal", "EVP_CipherFinal" ] } + + /** + * Output is both from update calls and from the final call. + */ + override Expr getOutputArg() { + result = EVPFinal.super.getOutputArg() + or + result = EVP_Cipher_Operation.super.getOutputArg() + } } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll index 9cccd6395a1c..d7dc2483c84a 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll @@ -69,6 +69,11 @@ abstract class EVPUpdate extends Call { * Update calls always have some input data like plaintext or message digest. */ abstract Expr getInputArg(); + + /** + * Update calls sometimes have some output data like a plaintext. + */ + Expr getOutputArg() { none() } } /** @@ -105,8 +110,6 @@ abstract class EVPOperation extends OpenSSLOperation { /** * Some output data like ciphertext or signature. - * Always produced directly by this operation. - * Assumption: output is provided as an argument to the call, never as return value. */ abstract Expr getOutputArg(); @@ -125,13 +128,15 @@ abstract class EVPOperation extends OpenSSLOperation { } Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result.asExpr() = this.getOutputArg() + result = DataFlow::exprNode(this.getOutputArg()) } /** * Input consumer is the input argument of the call. */ - Crypto::ConsumerInputDataFlowNode getInputConsumer() { result.asExpr() = this.getInputArg() } + Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result = DataFlow::exprNode(this.getInputArg()) + } } /** @@ -147,8 +152,15 @@ abstract class EVPFinal extends EVPOperation { /** * The input data was provided to all update calls. + * If more input data was provided in the final call, override the method. */ override Expr getInputArg() { result = this.getUpdateCalls().getInputArg() } + + /** + * The output data was provided to all update calls. + * If more output data was provided in the final call, override the method. + */ + override Expr getOutputArg() { result = this.getUpdateCalls().getOutputArg() } } /** diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll index 54ac977ead0d..f6ff0dd1f077 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll @@ -2,4 +2,3 @@ import OpenSSLOperationBase import EVPCipherOperation import EVPHashOperation import ECKeyGenOperation -import EVPSignatureOperation diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected index d566397eacad..73b0af3ad5f4 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected @@ -1,7 +1,15 @@ +| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:35:36:35:45 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:35:36:35:45 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:35:36:35:45 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:35:36:35:45 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | | openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | | openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | | openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | | openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:81:32:81:40 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:81:32:81:40 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:81:32:81:40 | KeyOperationOutput | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:81:32:81:40 | KeyOperationOutput | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | | openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:36:90:50 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | | openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:36:90:50 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | | openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:36:90:50 | KeyOperationOutput | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | From adb9c3b8e3fbceebd6caa1046f7d735d76df2da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20P=C5=82atek?= Date: Thu, 29 May 2025 11:12:31 +0200 Subject: [PATCH 04/21] Update cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll Co-authored-by: Ben Rodes --- .../quantum/OpenSSL/Operations/OpenSSLOperationBase.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll index d7dc2483c84a..e97181c744f4 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll @@ -22,7 +22,7 @@ abstract class OpenSSLOperation extends Crypto::OperationInstance instanceof Cal } /** - * Calls to initialization functions of EVP API. + * A Call to initialization functions from the EVP API. * These are not operations in the sense of Crypto::OperationInstance, * but they are used to initialize the context for the operation. */ From 408224aeb0e64c1796bf6d9d13f108487a2734f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20P=C5=82atek?= Date: Thu, 29 May 2025 11:14:16 +0200 Subject: [PATCH 05/21] Apply docs suggestions Co-authored-by: Ben Rodes --- .../quantum/OpenSSL/Operations/OpenSSLOperationBase.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll index e97181c744f4..dcd2d83cdc5b 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll @@ -55,7 +55,7 @@ abstract class EVPInitialize extends Call { } /** - * Calls to update functions of EVP API. + * A Call to update functions from the EVP API. * These are not operations in the sense of Crypto::OperationInstance, * but they are used to update the context for the operation. */ @@ -92,7 +92,7 @@ private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { private module AlgGetterToAlgConsumerFlow = DataFlow::Global; /** - * Base class for all operations of the EVP API. + * The base class for all operations of the EVP API. * Currently final calls and one-shot calls are implemented. * Provides some default methods for Crypto::KeyOperationInstance class */ From 0c17cbf4df734d232585714d3451f6f00202d76c Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 29 May 2025 11:33:52 +0200 Subject: [PATCH 06/21] rm one-shot class --- .../OpenSSL/Operations/EVPCipherOperation.qll | 2 +- .../quantum/OpenSSL/Operations/EVPHashOperation.qll | 12 ++++++------ .../OpenSSL/Operations/OpenSSLOperationBase.qll | 7 +------ 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll index 993b5ad60a92..5f24d840ff88 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll @@ -52,7 +52,7 @@ abstract class EVP_Cipher_Operation extends EVPOperation, Crypto::KeyOperationIn } } -class EVP_Cipher_Call extends EVPOneShot, EVP_Cipher_Operation { +class EVP_Cipher_Call extends EVPOperation, EVP_Cipher_Operation { EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" } override Expr getInputArg() { result = this.(Call).getArgument(2) } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll index 2b798a96b7e2..f2907db56781 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPHashOperation.qll @@ -15,7 +15,7 @@ class EVP_Digest_Update_Call extends EVPUpdate { } //https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis -class EVP_Q_Digest_Operation extends EVPOneShot, Crypto::HashOperationInstance { +class EVP_Q_Digest_Operation extends EVPOperation, Crypto::HashOperationInstance { EVP_Q_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Q_digest" } override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) } @@ -31,15 +31,15 @@ class EVP_Q_Digest_Operation extends EVPOneShot, Crypto::HashOperationInstance { override Expr getOutputArg() { result = this.(Call).getArgument(5) } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result = EVPOneShot.super.getOutputArtifact() + result = EVPOperation.super.getOutputArtifact() } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result = EVPOneShot.super.getInputConsumer() + result = EVPOperation.super.getInputConsumer() } } -class EVP_Digest_Operation extends EVPOneShot, Crypto::HashOperationInstance { +class EVP_Digest_Operation extends EVPOperation, Crypto::HashOperationInstance { EVP_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Digest" } // There is no context argument for this function @@ -58,11 +58,11 @@ class EVP_Digest_Operation extends EVPOneShot, Crypto::HashOperationInstance { override Expr getOutputArg() { result = this.(Call).getArgument(2) } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result = EVPOneShot.super.getOutputArtifact() + result = EVPOperation.super.getOutputArtifact() } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result = EVPOneShot.super.getInputConsumer() + result = EVPOperation.super.getInputConsumer() } } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll index dcd2d83cdc5b..7e9e781c8971 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll @@ -93,7 +93,7 @@ private module AlgGetterToAlgConsumerFlow = DataFlow::Global Date: Wed, 28 May 2025 14:53:10 +0200 Subject: [PATCH 07/21] init work for openssl signatures --- .../KnownAlgorithmConstants.qll | 2 + .../SignatureAlgorithmInstance.qll | 93 +++++++++++++++ .../SignatureAlgorithmValueConsumer.qll | 32 ++++++ .../Operations/EVPSignatureOperation.qll | 108 ++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/SignatureAlgorithmInstance.qll create mode 100644 cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/SignatureAlgorithmValueConsumer.qll create mode 100644 cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll index 402fbac02ecb..e28b89b03f77 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll @@ -2783,6 +2783,8 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "rsa-sha256" and nid = 668 and normalized = "SHA-256" and algType = "HASH" or + name = "rsa-sha256" and nid = 668 and normalized = "SHA-256" and algType = "SIGNATURE" + or name = "rsa-sha3-224" and nid = 1116 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or name = "rsa-sha3-224" and nid = 1116 and normalized = "SHA3-224" and algType = "HASH" diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/SignatureAlgorithmInstance.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/SignatureAlgorithmInstance.qll new file mode 100644 index 000000000000..cb46a6ed72f0 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/SignatureAlgorithmInstance.qll @@ -0,0 +1,93 @@ +import cpp +private import experimental.quantum.Language +private import KnownAlgorithmConstants +private import Crypto::KeyOpAlg as KeyOpAlg +private import OpenSSLAlgorithmInstanceBase +private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase +private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer +private import AlgToAVCFlow + +/** + * Gets the signature algorithm type based on the normalized algorithm name. + */ +private predicate knownOpenSSLConstantToSignatureFamilyType( + KnownOpenSSLSignatureAlgorithmConstant e, Crypto::KeyOpAlg::TAlgorithm type +) { + exists(string name | + name = e.getNormalizedName() and + ( + name.matches("RSA%") and type = KeyOpAlg::TAsymmetricCipher(KeyOpAlg::RSA()) + or + name.matches("DSA%") and type = KeyOpAlg::TSignature(KeyOpAlg::DSA()) + or + name.matches("ECDSA%") and type = KeyOpAlg::TSignature(KeyOpAlg::ECDSA()) + or + name.matches("ED25519%") and type = KeyOpAlg::TSignature(KeyOpAlg::Ed25519()) + or + name.matches("ED448%") and type = KeyOpAlg::TSignature(KeyOpAlg::Ed448()) + // or + // name.matches("sm2%") and type = KeyOpAlg::TSignature(KeyOpAlg::SM2()) + // or + // name.matches("ml-dsa%") and type = KeyOpAlg::TSignature(KeyOpAlg::MLDSA()) + // or + // name.matches("slh-dsa%") and type = KeyOpAlg::TSignature(KeyOpAlg::SLHDSA()) + ) + ) +} + +/** + * A signature algorithm instance derived from an OpenSSL constant. + */ +class KnownOpenSSLSignatureConstantAlgorithmInstance extends OpenSSLAlgorithmInstance, + Crypto::KeyOperationAlgorithmInstance instanceof KnownOpenSSLSignatureAlgorithmConstant +{ + OpenSSLAlgorithmValueConsumer getterCall; + + KnownOpenSSLSignatureConstantAlgorithmInstance() { + // Two possibilities: + // 1) The source is a literal and flows to a getter, then we know we have an instance + // 2) The source is a KnownOpenSSLAlgorithm call, and we know we have an instance immediately from that + // Possibility 1: + this instanceof Literal and + exists(DataFlow::Node src, DataFlow::Node sink | + // Sink is an argument to a signature getter call + sink = getterCall.getInputNode() and + // Source is `this` + src.asExpr() = this and + // This traces to a getter + KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink) + ) + or + // Possibility 2: + this instanceof DirectAlgorithmValueConsumer and getterCall = this + } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } + + override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } + + override int getKeySizeFixed() { + // this.(KnownOpenSSLSignatureAlgorithmConstant).getExplicitKeySize() = result + none() + } + + override KeyOpAlg::Algorithm getAlgorithmType() { + knownOpenSSLConstantToSignatureFamilyType(this, result) + or + not knownOpenSSLConstantToSignatureFamilyType(this, _) and + result = KeyOpAlg::TSignature(KeyOpAlg::OtherSignatureAlgorithmType()) + } + + override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { + // TODO: trace to any key size initializer, symmetric and asymmetric + none() + } + + override predicate shouldHaveModeOfOperation() { none() } + + override predicate shouldHavePaddingScheme() { none() } +} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/SignatureAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/SignatureAlgorithmValueConsumer.qll new file mode 100644 index 000000000000..8701a3658934 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/SignatureAlgorithmValueConsumer.qll @@ -0,0 +1,32 @@ +import cpp +private import experimental.quantum.Language +private import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants +private import experimental.quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstanceBase +private import OpenSSLAlgorithmValueConsumerBase +private import experimental.quantum.OpenSSL.LibraryDetector + +abstract class SignatureAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { } + +class EVPSignatureAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { + DataFlow::Node valueArgNode; + DataFlow::Node resultNode; + + EVPSignatureAlgorithmValueConsumer() { + resultNode.asExpr() = this and + isPossibleOpenSSLFunction(this.(Call).getTarget()) and + ( + // EVP_SIGNATURE + this.(Call).getTarget().getName() = "EVP_SIGNATURE_fetch" and + valueArgNode.asExpr() = this.(Call).getArgument(1) + // EVP_PKEY_get1_DSA, DSA_SIG_new, EVP_RSA_gen + ) + } + + override DataFlow::Node getResultNode() { result = resultNode } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result = valueArgNode } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + exists(OpenSSLAlgorithmInstance i | i.getAVC() = this and result = i) + } +} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll new file mode 100644 index 000000000000..f3039a394b74 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll @@ -0,0 +1,108 @@ +/** + * Provides classes for modeling OpenSSL's EVP signature operations + */ + +private import experimental.quantum.Language +private import OpenSSLOperationBase +private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow + +// TODO: verification +class EVP_Cipher_Initializer extends EVPInitialize { + EVP_Cipher_Initializer() { + this.(Call).getTarget().getName() in [ + "EVP_DigestSignInit", "EVP_DigestSignInit_ex", "EVP_SignInit", "EVP_SignInit_ex", + "EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex", "EVP_PKEY_sign_init_ex2", + "EVP_PKEY_sign_message_init" + ] + } + + override Expr getAlgorithmArg() { + this.(Call).getTarget().getName() = "EVP_DigestSignInit" and + result = this.(Call).getArgument(1) + or + this.(Call).getTarget().getName() = "EVP_DigestSignInit_ex" and + result = this.(Call).getArgument(1) + or + this.(Call).getTarget().getName() = "EVP_PKEY_sign_init_ex2" and + result = this.(Call).getArgument(1) + or + this.(Call).getTarget().getName() = "EVP_PKEY_sign_message_init" and + result = this.(Call).getArgument(1) + } + + override Expr getKeyArg() { + this.(Call).getTarget().getName() = "EVP_DigestSignInit" and + result = this.(Call).getArgument(4) + or + this.(Call).getTarget().getName() = "EVP_DigestSignInit_ex" and + result = this.(Call).getArgument(5) + } + + override Expr getIVArg() { none() } + + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + if this.(Call).getTarget().getName().toLowerCase().matches("%sign%") + then result instanceof Crypto::TSignMode + else + if this.(Call).getTarget().getName().toLowerCase().matches("%verify%") + then result instanceof Crypto::TVerifyMode + else result instanceof Crypto::TUnknownKeyOperationMode + } +} + +class EVP_Signature_Update_Call extends EVPUpdate { + EVP_Signature_Update_Call() { + this.(Call).getTarget().getName() in [ + "EVP_DigestSignUpdate", "EVP_SignUpdate", "EVP_PKEY_sign_message_update" + ] + } + + override Expr getInputArg() { result = this.(Call).getArgument(1) } +} + +abstract class EVP_Signature_Operation extends EVPOperation, Crypto::KeyOperationInstance { + EVP_Signature_Operation() { this.(Call).getTarget().getName().matches("EVP_%") } + + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + if this.(Call).getTarget().getName().toLowerCase().matches("%sign%") + then result instanceof Crypto::TSignMode + else + if this.(Call).getTarget().getName().toLowerCase().matches("%verify%") + then result instanceof Crypto::TVerifyMode + else result instanceof Crypto::TUnknownKeyOperationMode + } + + override Expr getOutputArg() { result = this.(Call).getArgument(1) } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { + // this.getInitCall().getIVArg() = result.asExpr() + none() + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + this.getInitCall().getKeyArg() = result.asExpr() + // todo: or track to the EVP_PKEY_CTX_new + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result = this.(EVPOperation).getOutputArtifact() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result = this.(EVPOperation).getInputConsumer() + } +} + +class EVP_Signature_Call extends EVPOneShot, EVP_Signature_Operation { + EVP_Signature_Call() { this.(Call).getTarget().getName() in ["EVP_DigestSign", "EVP_PKEY_sign"] } + + override Expr getInputArg() { result = this.(Call).getArgument(3) } +} + +class EVP_Signature_Final_Call extends EVPFinal, EVP_Signature_Operation { + EVP_Signature_Final_Call() { + this.(Call).getTarget().getName() in [ + "EVP_DigestSignFinal", "EVP_SignFinal_ex", "EVP_SignFinal", "EVP_PKEY_sign_message_final" + ] + } +} From c14fba5edd7f88816c415020c7910957827daf75 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 29 May 2025 12:21:51 +0200 Subject: [PATCH 08/21] openssl signatures - inputs, outputs and algorithms base --- .../Operations/EVPSignatureOperation.qll | 100 +++++++++++++----- 1 file changed, 74 insertions(+), 26 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll index f3039a394b74..fb1be039838b 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll @@ -6,30 +6,51 @@ private import experimental.quantum.Language private import OpenSSLOperationBase private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow -// TODO: verification -class EVP_Cipher_Initializer extends EVPInitialize { - EVP_Cipher_Initializer() { +// TODO: verification functions + + +class EVP_Signature_Initializer extends EVPInitialize { + boolean isAlgorithmSpecifiedByKey; + boolean isAlgorithmSpecifiedByCtx; + + EVP_Signature_Initializer() { this.(Call).getTarget().getName() in [ - "EVP_DigestSignInit", "EVP_DigestSignInit_ex", "EVP_SignInit", "EVP_SignInit_ex", - "EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex", "EVP_PKEY_sign_init_ex2", - "EVP_PKEY_sign_message_init" - ] + "EVP_DigestSignInit", "EVP_DigestSignInit_ex", "EVP_SignInit", "EVP_SignInit_ex", + "EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex", "EVP_PKEY_sign_init_ex2", + "EVP_PKEY_sign_message_init" + ] and + ( + if this.(Call).getTarget().getName().matches("EVP_PKEY_%") then + isAlgorithmSpecifiedByKey = false + else + isAlgorithmSpecifiedByKey = true + ) + and + ( + if this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex"] then + isAlgorithmSpecifiedByCtx = true + else + isAlgorithmSpecifiedByCtx = false + ) } + /** + * Returns the argument that specifies the algorithm or none if the algorithm is implicit (from context or from key). + * Note that the key may be not provided in the initialization call. + */ override Expr getAlgorithmArg() { - this.(Call).getTarget().getName() = "EVP_DigestSignInit" and - result = this.(Call).getArgument(1) - or - this.(Call).getTarget().getName() = "EVP_DigestSignInit_ex" and - result = this.(Call).getArgument(1) - or - this.(Call).getTarget().getName() = "EVP_PKEY_sign_init_ex2" and - result = this.(Call).getArgument(1) - or - this.(Call).getTarget().getName() = "EVP_PKEY_sign_message_init" and - result = this.(Call).getArgument(1) + if isAlgorithmSpecifiedByKey = true or isAlgorithmSpecifiedByCtx = true then + none() + else ( + this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] and + result = this.(Call).getArgument(1) + ) } + /** + * Returns the key argument if there is one. + * They key could be provided in the context or in a later call (final or one-shot). + */ override Expr getKeyArg() { this.(Call).getTarget().getName() = "EVP_DigestSignInit" and result = this.(Call).getArgument(4) @@ -38,8 +59,9 @@ class EVP_Cipher_Initializer extends EVPInitialize { result = this.(Call).getArgument(5) } - override Expr getIVArg() { none() } - + /** + * Signing, verification or unknown. + */ override Crypto::KeyOperationSubtype getKeyOperationSubtype() { if this.(Call).getTarget().getName().toLowerCase().matches("%sign%") then result instanceof Crypto::TSignMode @@ -57,13 +79,23 @@ class EVP_Signature_Update_Call extends EVPUpdate { ] } + /** + * Input is the message to sign. + */ override Expr getInputArg() { result = this.(Call).getArgument(1) } } +/** + * Base configuration for all EVP signature operations. + */ abstract class EVP_Signature_Operation extends EVPOperation, Crypto::KeyOperationInstance { EVP_Signature_Operation() { this.(Call).getTarget().getName().matches("EVP_%") } + /** + * Signing, verification or unknown. + */ override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + // TODO: if this KeyOperationSubtype does not match initialization call's KeyOperationSubtype then we found a bug if this.(Call).getTarget().getName().toLowerCase().matches("%sign%") then result instanceof Crypto::TSignMode else @@ -72,16 +104,14 @@ abstract class EVP_Signature_Operation extends EVPOperation, Crypto::KeyOperatio else result instanceof Crypto::TUnknownKeyOperationMode } - override Expr getOutputArg() { result = this.(Call).getArgument(1) } - override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { - // this.getInitCall().getIVArg() = result.asExpr() + // TODO: some signing operations may have explicit nonce generators none() } override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { - this.getInitCall().getKeyArg() = result.asExpr() - // todo: or track to the EVP_PKEY_CTX_new + result = DataFlow::exprNode(this.getInitCall().getKeyArg()) + // TODO: or track to the EVP_PKEY_CTX_new } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { @@ -93,9 +123,17 @@ abstract class EVP_Signature_Operation extends EVPOperation, Crypto::KeyOperatio } } -class EVP_Signature_Call extends EVPOneShot, EVP_Signature_Operation { +class EVP_Signature_Call extends EVPOperation, EVP_Signature_Operation { EVP_Signature_Call() { this.(Call).getTarget().getName() in ["EVP_DigestSign", "EVP_PKEY_sign"] } + /** + * Output is the signature. + */ + override Expr getOutputArg() { result = this.(Call).getArgument(1) } + + /** + * Input is the message to sign. + */ override Expr getInputArg() { result = this.(Call).getArgument(3) } } @@ -105,4 +143,14 @@ class EVP_Signature_Final_Call extends EVPFinal, EVP_Signature_Operation { "EVP_DigestSignFinal", "EVP_SignFinal_ex", "EVP_SignFinal", "EVP_PKEY_sign_message_final" ] } + + /** + * Output is the signature. + */ + override Expr getOutputArg() { + if this.(Call).getTarget().getName() = "EVP_SignFinal_ex" then + result = this.(Call).getArgument(2) + else + result = this.(Call).getArgument(1) + } } From b068833732bd3ee8b3d690be38ad530eab8ada48 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 29 May 2025 13:41:38 +0200 Subject: [PATCH 09/21] start on openssl signature tests --- .../Operations/EVPSignatureOperation.qll | 60 +- .../OpenSSL/Operations/OpenSSLOperations.qll | 1 + .../openssl/includes/alg_macro_stubs.h | 6 + .../quantum/openssl/includes/evp_stubs.h | 147 ++++ .../openssl/signature/openssl_signature.c | 821 ++++++++++++++++++ .../openssl/signature/signature_operations.ql | 7 + 6 files changed, 1016 insertions(+), 26 deletions(-) create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll index fb1be039838b..c9b7927c7126 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll @@ -7,30 +7,25 @@ private import OpenSSLOperationBase private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow // TODO: verification functions - - class EVP_Signature_Initializer extends EVPInitialize { boolean isAlgorithmSpecifiedByKey; boolean isAlgorithmSpecifiedByCtx; EVP_Signature_Initializer() { this.(Call).getTarget().getName() in [ - "EVP_DigestSignInit", "EVP_DigestSignInit_ex", "EVP_SignInit", "EVP_SignInit_ex", - "EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex", "EVP_PKEY_sign_init_ex2", - "EVP_PKEY_sign_message_init" - ] and + "EVP_DigestSignInit", "EVP_DigestSignInit_ex", "EVP_SignInit", "EVP_SignInit_ex", + "EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex", "EVP_PKEY_sign_init_ex2", + "EVP_PKEY_sign_message_init" + ] and ( - if this.(Call).getTarget().getName().matches("EVP_PKEY_%") then - isAlgorithmSpecifiedByKey = false - else - isAlgorithmSpecifiedByKey = true - ) - and + if this.(Call).getTarget().getName().matches("EVP_PKEY_%") + then isAlgorithmSpecifiedByKey = false + else isAlgorithmSpecifiedByKey = true + ) and ( - if this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex"] then - isAlgorithmSpecifiedByCtx = true - else - isAlgorithmSpecifiedByCtx = false + if this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex"] + then isAlgorithmSpecifiedByCtx = true + else isAlgorithmSpecifiedByCtx = false ) } @@ -39,8 +34,8 @@ class EVP_Signature_Initializer extends EVPInitialize { * Note that the key may be not provided in the initialization call. */ override Expr getAlgorithmArg() { - if isAlgorithmSpecifiedByKey = true or isAlgorithmSpecifiedByCtx = true then - none() + if isAlgorithmSpecifiedByKey = true or isAlgorithmSpecifiedByCtx = true + then none() else ( this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] and result = this.(Call).getArgument(1) @@ -85,11 +80,29 @@ class EVP_Signature_Update_Call extends EVPUpdate { override Expr getInputArg() { result = this.(Call).getArgument(1) } } +/** + * We model output explicit output arguments as predicate to use it in constructors. + * The predicate must cover all EVP_Signature_Operation subclasses. + */ +private Expr signatureOperationOutputArg(Call call) { + if call.getTarget().getName() = "EVP_SignFinal_ex" + then result = call.getArgument(2) + else result = call.getArgument(1) +} + /** * Base configuration for all EVP signature operations. */ abstract class EVP_Signature_Operation extends EVPOperation, Crypto::KeyOperationInstance { - EVP_Signature_Operation() { this.(Call).getTarget().getName().matches("EVP_%") } + EVP_Signature_Operation() { + this.(Call).getTarget().getName().matches("EVP_%") and + // NULL output argument means the call is to get the size of the signature + ( + not exists(signatureOperationOutputArg(this).getValue()) + or + signatureOperationOutputArg(this).getValue() != "0" + ) + } /** * Signing, verification or unknown. @@ -129,7 +142,7 @@ class EVP_Signature_Call extends EVPOperation, EVP_Signature_Operation { /** * Output is the signature. */ - override Expr getOutputArg() { result = this.(Call).getArgument(1) } + override Expr getOutputArg() { result = signatureOperationOutputArg(this) } /** * Input is the message to sign. @@ -147,10 +160,5 @@ class EVP_Signature_Final_Call extends EVPFinal, EVP_Signature_Operation { /** * Output is the signature. */ - override Expr getOutputArg() { - if this.(Call).getTarget().getName() = "EVP_SignFinal_ex" then - result = this.(Call).getArgument(2) - else - result = this.(Call).getArgument(1) - } + override Expr getOutputArg() { result = signatureOperationOutputArg(this) } } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll index f6ff0dd1f077..54ac977ead0d 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperations.qll @@ -2,3 +2,4 @@ import OpenSSLOperationBase import EVPCipherOperation import EVPHashOperation import ECKeyGenOperation +import EVPSignatureOperation diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/alg_macro_stubs.h b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/alg_macro_stubs.h index 3058681d71d7..f08de0f2e959 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/alg_macro_stubs.h +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/alg_macro_stubs.h @@ -1,3 +1,6 @@ +# define RSA_PKCS1_PSS_PADDING 6 +# define NID_sha256 672 + # define EVP_PKEY_NONE NID_undef # define EVP_PKEY_RSA NID_rsaEncryption # define EVP_PKEY_RSA2 NID_rsa @@ -3739,3 +3742,6 @@ #define SN_itu_t "ITU-T" #define LN_undef "undefined" #define SN_undef "UNDEF" + +#define RSA_PKCS1_PSS_PADDING 6 +# define EVP_MAX_MD_SIZE 64 \ No newline at end of file diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/evp_stubs.h b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/evp_stubs.h index 4bc1af0b15d6..bf8cf73a4f81 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/evp_stubs.h +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/evp_stubs.h @@ -28,6 +28,7 @@ # define EVP_CTRL_CCM_SET_IV_FIXED EVP_CTRL_AEAD_SET_IV_FIXED # define EVP_CTRL_CCM_SET_L 0x14 # define EVP_CTRL_CCM_SET_MSGLEN 0x15 +# define EVP_MAX_MD_SIZE 64 typedef unsigned long size_t; @@ -35,6 +36,18 @@ typedef unsigned char uint8_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; +// Forward declarations for opaque structs +struct rsa_st; +struct dsa_st; +struct dh_st; +struct ec_key_st; +struct DSA_SIG_st; +typedef struct rsa_st RSA; +typedef struct dsa_st DSA; +typedef struct dh_st DH; +typedef struct ec_key_st EC_KEY; +typedef struct DSA_SIG_st DSA_SIG;; + // Type aliases. typedef int OSSL_PROVIDER; @@ -4983,4 +4996,138 @@ EVP_SKEY * EVP_SKEY_to_provider(EVP_SKEY * skey, OSSL_LIB_CTX * libctx, OSSL_PRO return NULL; } +int ERR_get_error() { + return 0; +} + +void ERR_error_string_n(int error, char* buf, int len) { + return; +} + +int EVP_SignInit(EVP_MD_CTX *ctx, const EVP_MD *type) { + return 0; +} + +int EVP_SignUpdate(EVP_MD_CTX *ctx, const unsigned char *data, size_t len) { + return 0; +} + +void* OPENSSL_malloc(size_t size) { + return NULL; +} + +void OPENSSL_free(void *ptr) { + return; +} + +int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX *ctx, int bits) { + return 0; +} + +int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int saltlen) { + return 0; +} + +int RSA_do_verify(const unsigned char *dgst, int dgst_len, DSA_SIG *sig, DSA *dsa) { + return 1; +} + +int DSA_do_verify(const unsigned char *dgst, int dgst_len, DSA_SIG *sig, DSA *dsa) { + return 1; +} + +int RSA_free(RSA *rsa) { + return 1; +} + +int DSA_free(DSA *dsa) { + return 1; +} + +int EVP_PKEY_size(const EVP_PKEY * pkey) { + return 0; +} + +int EVP_VerifyInit(EVP_MD_CTX * ctx, const EVP_MD * type) { + return 0; +} + +int EVP_VerifyUpdate(EVP_MD_CTX * ctx, const void * data, size_t dsize) { + return 0; +} + +int printf(const char*, ...) { + return NULL; +} + +int strlen(const char *s) { + return NULL; +} + +void* memset(void *s, int c, size_t n) { + return NULL; +} + +int RSA_size(const RSA * rsa) { + return 0; +} + +int RSA_sign(int type, const unsigned char * m, unsigned int m_length, unsigned char * sigret, unsigned int * siglen, RSA * rsa) { + return 0; +} + +int RSA_verify(int type, const unsigned char * m, unsigned int m_length, const unsigned char * sigbuf, unsigned int siglen, RSA * rsa) { + return 0; +} + +int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX * ctx, int padding) { + return 0; +} + +int EVP_PKEY_CTX_set_dsa_paramgen_bits(EVP_PKEY_CTX * ctx, int bits) { + return 0; +} + +int DSA_size(const DSA * dsa) { + return 0; +} + +DSA_SIG * DSA_SIG_new(void) { + return NULL; +} + +void DSA_SIG_free(DSA_SIG * sig) ; + +DSA_SIG * DSA_do_sign(const unsigned char * dgst, int dgst_len, DSA * dsa) { + return NULL; +} + +void DSA_SIG_get0(const DSA_SIG * sig, const BIGNUM ** pr, const BIGNUM ** ps) ; + +int DSA_SIG_set0(DSA_SIG * sig, BIGNUM * r, BIGNUM * s) { + return 0; +} + +int BN_num_bytes(const BIGNUM * a) { + return 0; +} + +int BN_bn2bin(const BIGNUM * a, unsigned char * to) { + return 0; +} + +BIGNUM * BN_new(void) { + return NULL; +} + +void BN_free(BIGNUM * a) ; + +BIGNUM * BN_bin2bn(const unsigned char * s, int len, BIGNUM * ret) { + return NULL; +} + +void OpenSSL_add_all_algorithms(void) ; + +void ERR_load_crypto_strings(void) ; + #endif /* OSSL_EVP_H */ diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c new file mode 100644 index 000000000000..e8054467f518 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c @@ -0,0 +1,821 @@ +#ifndef USE_REAL_HEADERS +#include "../includes/evp_stubs.h" +#include "../includes/alg_macro_stubs.h" +#include "../includes/rand_stubs.h" +#else +#include +#include +#include +#include +#include +#endif + +/* ============================================================================= + * UTILITY FUNCTIONS - Common operations shared across signature APIs + * ============================================================================= + */ + +/** + * Create message digest from raw message data + */ +static int create_digest(const unsigned char *message, size_t message_len, + const EVP_MD *md, unsigned char *digest, unsigned int *digest_len) { + EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); + int ret = 0; + + if (!md_ctx || + EVP_DigestInit_ex(md_ctx, md, NULL) != 1 || + EVP_DigestUpdate(md_ctx, message, message_len) != 1 || + EVP_DigestFinal_ex(md_ctx, digest, digest_len) != 1) { + goto cleanup; + } + ret = 1; + +cleanup: + EVP_MD_CTX_free(md_ctx); + return ret; +} + +/** + * Allocate signature buffer with appropriate size + */ +static unsigned char* allocate_signature_buffer(size_t *sig_len, const EVP_PKEY *pkey) { + *sig_len = EVP_PKEY_size(pkey); + return OPENSSL_malloc(*sig_len); +} + +/** + * Helper to extract key from EVP_PKEY + */ +static RSA* get_rsa_from_pkey(EVP_PKEY *pkey) { + return EVP_PKEY_get1_RSA(pkey); +} + +static DSA* get_dsa_from_pkey(EVP_PKEY *pkey) { + return EVP_PKEY_get1_DSA(pkey); +} + +/* ============================================================================= + * EVP_SIGN/VERIFY API - Legacy high-level API (older, simpler) + * ============================================================================= + */ + +/** + * Sign message using EVP_Sign API (legacy) + * Simple API with built-in hashing and signing + */ +int sign_using_evp_sign(const unsigned char *message, size_t message_len, + unsigned char **signature, size_t *signature_len, + EVP_PKEY *pkey, const EVP_MD *md) { + EVP_MD_CTX *md_ctx = NULL; + unsigned int sig_len = 0; + int ret = 0; + + if (!(md_ctx = EVP_MD_CTX_new()) || + EVP_SignInit(md_ctx, md) != 1 || + EVP_SignUpdate(md_ctx, message, message_len) != 1) { + goto cleanup; + } + + *signature = allocate_signature_buffer(signature_len, pkey); + if (!*signature) goto cleanup; + + if (EVP_SignFinal(md_ctx, *signature, &sig_len, pkey) == 1) { + *signature_len = sig_len; + ret = 1; + } else { + OPENSSL_free(*signature); + *signature = NULL; + } + +cleanup: + EVP_MD_CTX_free(md_ctx); + return ret; +} + +/** + * Verify signature using EVP_Verify API (legacy) + * Simple API with built-in hashing and verification + */ +int verify_using_evp_verify(const unsigned char *message, size_t message_len, + const unsigned char *signature, size_t signature_len, + EVP_PKEY *pkey, const EVP_MD *md) { + EVP_MD_CTX *md_ctx = NULL; + int ret = 0; + + if (!(md_ctx = EVP_MD_CTX_new()) || + EVP_VerifyInit(md_ctx, md) != 1 || + EVP_VerifyUpdate(md_ctx, message, message_len) != 1 || + EVP_VerifyFinal(md_ctx, signature, (unsigned int)signature_len, pkey) != 1) { + goto cleanup; + } + ret = 1; + +cleanup: + EVP_MD_CTX_free(md_ctx); + return ret; +} + +/* ============================================================================= + * EVP_DIGESTSIGN/DIGESTVERIFY API - Modern recommended API + * ============================================================================= + */ + +/** + * Sign message using EVP_DigestSign API (recommended) + * Modern flexible API with better algorithm support + */ +int sign_using_evp_digestsign(const unsigned char *message, size_t message_len, + unsigned char **signature, size_t *signature_len, + EVP_PKEY *pkey, const EVP_MD *md) { + EVP_MD_CTX *md_ctx = NULL; + int ret = 0; + + if (!(md_ctx = EVP_MD_CTX_new()) || + EVP_DigestSignInit(md_ctx, NULL, md, NULL, pkey) != 1 || + EVP_DigestSignUpdate(md_ctx, message, message_len) != 1 || + EVP_DigestSignFinal(md_ctx, NULL, signature_len) != 1) { + goto cleanup; + } + + *signature = OPENSSL_malloc(*signature_len); + if (!*signature) goto cleanup; + + if (EVP_DigestSignFinal(md_ctx, *signature, signature_len) == 1) { + ret = 1; + } else { + OPENSSL_free(*signature); + *signature = NULL; + } + +cleanup: + EVP_MD_CTX_free(md_ctx); + return ret; +} + +/** + * Verify signature using EVP_DigestVerify API (recommended) + * Modern flexible API with better algorithm support + */ +int verify_using_evp_digestverify(const unsigned char *message, size_t message_len, + const unsigned char *signature, size_t signature_len, + EVP_PKEY *pkey, const EVP_MD *md) { + EVP_MD_CTX *md_ctx = NULL; + int ret = 0; + + if (!(md_ctx = EVP_MD_CTX_new()) || + EVP_DigestVerifyInit(md_ctx, NULL, md, NULL, pkey) != 1 || + EVP_DigestVerifyUpdate(md_ctx, message, message_len) != 1 || + EVP_DigestVerifyFinal(md_ctx, signature, signature_len) != 1) { + goto cleanup; + } + ret = 1; + +cleanup: + EVP_MD_CTX_free(md_ctx); + return ret; +} + +/** + * Sign with explicit PKEY_CTX for fine-grained parameter control + * Allows custom parameter settings (e.g., padding, salt length) + */ +int sign_using_digestsign_with_ctx(const unsigned char *message, size_t message_len, + unsigned char **signature, size_t *signature_len, + EVP_PKEY *pkey, const EVP_MD *md, + int (*param_setter)(EVP_PKEY_CTX *ctx)) { + EVP_MD_CTX *md_ctx = NULL; + EVP_PKEY_CTX *pkey_ctx = NULL; + int ret = 0; + + if (!(md_ctx = EVP_MD_CTX_new()) || + EVP_DigestSignInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) { + goto cleanup; + } + + if (param_setter && param_setter(pkey_ctx) != 1) goto cleanup; + + if (EVP_DigestSignUpdate(md_ctx, message, message_len) != 1 || + EVP_DigestSignFinal(md_ctx, NULL, signature_len) != 1) { + goto cleanup; + } + + *signature = OPENSSL_malloc(*signature_len); + if (!*signature) goto cleanup; + + if (EVP_DigestSignFinal(md_ctx, *signature, signature_len) == 1) { + ret = 1; + } else { + OPENSSL_free(*signature); + *signature = NULL; + } + +cleanup: + EVP_MD_CTX_free(md_ctx); + return ret; +} + +/** + * Verify with explicit PKEY_CTX for fine-grained parameter control + */ +int verify_using_digestverify_with_ctx(const unsigned char *message, size_t message_len, + const unsigned char *signature, size_t signature_len, + EVP_PKEY *pkey, const EVP_MD *md, + int (*param_setter)(EVP_PKEY_CTX *ctx)) { + EVP_MD_CTX *md_ctx = NULL; + EVP_PKEY_CTX *pkey_ctx = NULL; + int ret = 0; + + if (!(md_ctx = EVP_MD_CTX_new()) || + EVP_DigestVerifyInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) { + goto cleanup; + } + + if (param_setter && param_setter(pkey_ctx) != 1) goto cleanup; + + if (EVP_DigestVerifyUpdate(md_ctx, message, message_len) != 1 || + EVP_DigestVerifyFinal(md_ctx, signature, signature_len) != 1) { + goto cleanup; + } + ret = 1; + +cleanup: + EVP_MD_CTX_free(md_ctx); + return ret; +} + +/* ============================================================================= + * EVP_PKEY_SIGN/VERIFY API - Lower level API with pre-hashed input + * ============================================================================= + */ + +/** + * Sign pre-hashed digest using EVP_PKEY_sign API + * Lower-level API requiring pre-computed digest + */ +int sign_using_evp_pkey_sign(const unsigned char *digest, size_t digest_len, + unsigned char **signature, size_t *signature_len, + EVP_PKEY *pkey, const EVP_MD *md) { + EVP_PKEY_CTX *pkey_ctx = NULL; + int ret = 0; + + if (!(pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL)) || + EVP_PKEY_sign_init(pkey_ctx) != 1 || + EVP_PKEY_CTX_set_signature_md(pkey_ctx, md) != 1 || + EVP_PKEY_sign(pkey_ctx, NULL, signature_len, digest, digest_len) != 1) { + goto cleanup; + } + + *signature = OPENSSL_malloc(*signature_len); + if (!*signature) goto cleanup; + + if (EVP_PKEY_sign(pkey_ctx, *signature, signature_len, digest, digest_len) == 1) { + ret = 1; + } else { + OPENSSL_free(*signature); + *signature = NULL; + } + +cleanup: + EVP_PKEY_CTX_free(pkey_ctx); + return ret; +} + +/** + * Verify pre-hashed digest using EVP_PKEY_verify API + * Lower-level API requiring pre-computed digest + */ +int verify_using_evp_pkey_verify(const unsigned char *digest, size_t digest_len, + const unsigned char *signature, size_t signature_len, + EVP_PKEY *pkey, const EVP_MD *md) { + EVP_PKEY_CTX *pkey_ctx = NULL; + int ret = 0; + + if (!(pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL)) || + EVP_PKEY_verify_init(pkey_ctx) != 1 || + EVP_PKEY_CTX_set_signature_md(pkey_ctx, md) != 1 || + EVP_PKEY_verify(pkey_ctx, signature, signature_len, digest, digest_len) != 1) { + goto cleanup; + } + ret = 1; + +cleanup: + EVP_PKEY_CTX_free(pkey_ctx); + return ret; +} + +/* ============================================================================= + * EVP_PKEY_SIGN_MESSAGE API - Streamlined message signing + * ============================================================================= + */ + +/** + * Sign message using EVP_PKEY_sign_message API + * Streamlined interface for direct message signing + */ +int sign_using_evp_pkey_sign_message(const unsigned char *message, size_t message_len, + unsigned char **signature, size_t *signature_len, + EVP_PKEY *pkey, const EVP_MD *md) { + EVP_PKEY_CTX *pkey_ctx = NULL; + EVP_SIGNATURE *alg = NULL; + int ret = 0; + + if (!(pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL))) goto cleanup; + + alg = EVP_SIGNATURE_fetch(NULL, "RSA-SHA256", NULL); + + if (EVP_PKEY_sign_message_init(pkey_ctx, alg, NULL) != 1 || + EVP_PKEY_sign_message_update(pkey_ctx, message, message_len) != 1 || + EVP_PKEY_sign_message_final(pkey_ctx, NULL, signature_len) != 1) { + goto cleanup; + } + + *signature = OPENSSL_malloc(*signature_len); + if (!*signature) goto cleanup; + + if (EVP_PKEY_sign_message_final(pkey_ctx, *signature, signature_len) == 1) { + ret = 1; + } else { + OPENSSL_free(*signature); + *signature = NULL; + } + +cleanup: + EVP_PKEY_CTX_free(pkey_ctx); + return ret; +} + +/** + * Verify message using EVP_PKEY_verify_message API + * Streamlined interface for direct message verification + */ +int verify_using_evp_pkey_verify_message(const unsigned char *message, size_t message_len, + const unsigned char *signature, size_t signature_len, + EVP_PKEY *pkey, const EVP_MD *md) { + EVP_PKEY_CTX *pkey_ctx = NULL; + EVP_SIGNATURE *alg = NULL; + int ret = 0; + + if (!(pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL))) goto cleanup; + + alg = EVP_SIGNATURE_fetch(NULL, "RSA-SHA256", NULL); + + if (EVP_PKEY_verify_message_init(pkey_ctx, alg, NULL) != 1) goto cleanup; + + EVP_PKEY_CTX_set_signature(pkey_ctx, signature, signature_len); + + if (EVP_PKEY_verify_message_update(pkey_ctx, message, message_len) != 1 || + EVP_PKEY_verify_message_final(pkey_ctx) != 1) { + goto cleanup; + } + ret = 1; + +cleanup: + EVP_PKEY_CTX_free(pkey_ctx); + return ret; +} + +/* ============================================================================= + * LOW-LEVEL RSA API - Algorithm-specific functions (deprecated) + * ============================================================================= + */ + +/** + * Sign using low-level RSA_sign API (deprecated, RSA-only) + * Direct RSA signing with manual digest computation + */ +int sign_using_rsa_sign(const unsigned char *message, size_t message_len, + unsigned char **signature, size_t *signature_len, + RSA *rsa_key, int hash_nid, const EVP_MD *md) { + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int digest_len; + int ret = 0; + + if (!create_digest(message, message_len, md, digest, &digest_len)) return 0; + + *signature_len = RSA_size(rsa_key); + *signature = OPENSSL_malloc(*signature_len); + if (!*signature) return 0; + + if (RSA_sign(hash_nid, digest, digest_len, *signature, + (unsigned int*)signature_len, rsa_key) == 1) { + ret = 1; + } else { + OPENSSL_free(*signature); + *signature = NULL; + } + + return ret; +} + +/** + * Verify using low-level RSA_verify API (deprecated, RSA-only) + * Direct RSA verification with manual digest computation + */ +int verify_using_rsa_verify(const unsigned char *message, size_t message_len, + const unsigned char *signature, size_t signature_len, + RSA *rsa_key, int hash_nid, const EVP_MD *md) { + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int digest_len; + + if (!create_digest(message, message_len, md, digest, &digest_len)) return 0; + + return RSA_verify(hash_nid, digest, digest_len, signature, + (unsigned int)signature_len, rsa_key); +} + +/* ============================================================================= + * LOW-LEVEL DSA API - Algorithm-specific functions (deprecated) + * ============================================================================= + */ + +/** + * Sign using low-level DSA_do_sign API (deprecated, DSA-only) + * Direct DSA signing with manual digest and signature encoding + */ +int sign_using_dsa_sign(const unsigned char *message, size_t message_len, + unsigned char **signature, size_t *signature_len, + DSA *dsa_key, const EVP_MD *md) { + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int digest_len; + DSA_SIG *sig = NULL; + const BIGNUM *r = NULL, *s = NULL; + unsigned int bn_len; + int ret = 0; + + if (!create_digest(message, message_len, md, digest, &digest_len)) return 0; + + sig = DSA_do_sign(digest, digest_len, dsa_key); + if (!sig) return 0; + + DSA_SIG_get0(sig, &r, &s); + if (!r || !s) goto cleanup; + + bn_len = DSA_size(dsa_key) / 2; + *signature_len = DSA_size(dsa_key); + *signature = OPENSSL_malloc(*signature_len); + if (!*signature) goto cleanup; + + memset(*signature, 0, *signature_len); + + if (BN_bn2bin(r, *signature + (bn_len - BN_num_bytes(r))) > 0 && + BN_bn2bin(s, *signature + bn_len + (bn_len - BN_num_bytes(s))) > 0) { + ret = 1; + } else { + OPENSSL_free(*signature); + *signature = NULL; + } + +cleanup: + DSA_SIG_free(sig); + return ret; +} + +/** + * Verify using low-level DSA_do_verify API (deprecated, DSA-only) + * Direct DSA verification with manual digest and signature decoding + */ +int verify_using_dsa_verify(const unsigned char *message, size_t message_len, + const unsigned char *signature, size_t signature_len, + DSA *dsa_key, const EVP_MD *md) { + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int digest_len; + DSA_SIG *sig = NULL; + BIGNUM *r = NULL, *s = NULL; + unsigned int bn_len; + int ret = 0; + + if (!create_digest(message, message_len, md, digest, &digest_len)) return 0; + + sig = DSA_SIG_new(); + if (!sig) return 0; + + r = BN_new(); + s = BN_new(); + if (!r || !s) goto cleanup; + + bn_len = DSA_size(dsa_key) / 2; + + if (BN_bin2bn(signature, bn_len, r) && + BN_bin2bn(signature + bn_len, bn_len, s) && + DSA_SIG_set0(sig, r, s) == 1) { + /* r and s are now owned by sig */ + r = s = NULL; + ret = DSA_do_verify(digest, digest_len, sig, dsa_key); + } + +cleanup: + DSA_SIG_free(sig); + BN_free(r); + BN_free(s); + return (ret == 1); +} + +/* ============================================================================= + * PARAMETER SETTERS - Helper functions for algorithm configuration + * ============================================================================= + */ + +/** + * Set RSA PSS padding mode + */ +int set_rsa_pss_padding(EVP_PKEY_CTX *ctx) { + return EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING); +} + +/** + * No-op parameter setter for default behavior + */ +int no_parameter_setter(EVP_PKEY_CTX *ctx) { + return 1; +} + +/* ============================================================================= + * KEY GENERATION HELPERS + * ============================================================================= + */ + +/** + * Generate RSA key pair for testing + */ +static EVP_PKEY* generate_rsa_key(void) { + EVP_PKEY_CTX *key_ctx = NULL; + EVP_PKEY *key = NULL; + + key_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (!key_ctx) return NULL; + + if (EVP_PKEY_keygen_init(key_ctx) <= 0 || + EVP_PKEY_CTX_set_rsa_keygen_bits(key_ctx, 2048) <= 0 || + EVP_PKEY_keygen(key_ctx, &key) <= 0) { + EVP_PKEY_free(key); + key = NULL; + } + + EVP_PKEY_CTX_free(key_ctx); + return key; +} + +/** + * Generate DSA key pair for testing + */ +static EVP_PKEY* generate_dsa_key(void) { + EVP_PKEY_CTX *param_ctx = NULL, *key_ctx = NULL; + EVP_PKEY *params = NULL, *key = NULL; + + /* Generate parameters first */ + param_ctx = EVP_PKEY_CTX_new_from_name(NULL, "DSA", NULL); + if (!param_ctx) return NULL; + + if (EVP_PKEY_paramgen_init(param_ctx) <= 0 || + EVP_PKEY_CTX_set_dsa_paramgen_bits(param_ctx, 2048) <= 0 || + EVP_PKEY_paramgen(param_ctx, ¶ms) <= 0) { + goto cleanup; + } + + /* Generate key using parameters */ + key_ctx = EVP_PKEY_CTX_new(params, NULL); + if (!key_ctx || + EVP_PKEY_keygen_init(key_ctx) <= 0 || + EVP_PKEY_keygen(key_ctx, &key) <= 0) { + EVP_PKEY_free(key); + key = NULL; + } + +cleanup: + EVP_PKEY_CTX_free(param_ctx); + EVP_PKEY_CTX_free(key_ctx); + EVP_PKEY_free(params); + return key; +} + +/* ============================================================================= + * TEST FUNCTIONS - Comprehensive API testing + * ============================================================================= + */ + +/** + * Test all signature APIs with a given key and algorithm + * Demonstrates the 6 different signature API approaches + */ +int test_signature_apis(EVP_PKEY *key, const EVP_MD *md, + int (*param_setter)(EVP_PKEY_CTX *ctx), + const char *algo_name) { + const unsigned char message[] = "Test message for OpenSSL signature APIs"; + const size_t message_len = strlen((char *)message); + + unsigned char *sig1 = NULL, *sig2 = NULL, *sig3 = NULL, + *sig4 = NULL, *sig6 = NULL; + size_t sig_len1 = 0, sig_len2 = 0, sig_len3 = 0, sig_len4 = 0, sig_len6 = 0; + + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int digest_len; + int success = 1; + + printf("\nTesting signature APIs with %s:\n", algo_name); + + /* Test 1: EVP_Sign API */ + printf("1. EVP_Sign API: "); + if (sign_using_evp_sign(message, message_len, &sig1, &sig_len1, key, md) && + verify_using_evp_verify(message, message_len, sig1, sig_len1, key, md)) { + printf("PASS\n"); + } else { + printf("FAIL\n"); + success = 0; + } + + /* Test 2: EVP_DigestSign API */ + printf("2. EVP_DigestSign API: "); + if (sign_using_evp_digestsign(message, message_len, &sig2, &sig_len2, key, md) && + verify_using_evp_digestverify(message, message_len, sig2, sig_len2, key, md)) { + printf("PASS\n"); + } else { + printf("FAIL\n"); + success = 0; + } + + /* Test 3: EVP_PKEY_sign API (requires pre-hashed input) */ + printf("3. EVP_PKEY_sign API: "); + if (create_digest(message, message_len, md, digest, &digest_len) && + sign_using_evp_pkey_sign(digest, digest_len, &sig3, &sig_len3, key, md) && + verify_using_evp_pkey_verify(digest, digest_len, sig3, sig_len3, key, md)) { + printf("PASS\n"); + } else { + printf("FAIL\n"); + success = 0; + } + + /* Test 4: EVP_DigestSign with explicit PKEY_CTX */ + printf("4. EVP_DigestSign with explicit PKEY_CTX: "); + if (sign_using_digestsign_with_ctx(message, message_len, &sig4, &sig_len4, + key, md, param_setter) && + verify_using_digestverify_with_ctx(message, message_len, sig4, sig_len4, + key, md, param_setter)) { + printf("PASS\n"); + } else { + printf("FAIL\n"); + success = 0; + } + + /* Test 6: EVP_PKEY_sign_message API */ + printf("6. EVP_PKEY_sign_message API: "); + if (sign_using_evp_pkey_sign_message(message, message_len, &sig6, &sig_len6, key, md) && + verify_using_evp_pkey_verify_message(message, message_len, sig6, sig_len6, key, md)) { + printf("PASS\n"); + } else { + printf("FAIL\n"); + success = 0; + } + + /* Cleanup */ + OPENSSL_free(sig1); + OPENSSL_free(sig2); + OPENSSL_free(sig3); + OPENSSL_free(sig4); + OPENSSL_free(sig6); + + return success; +} + +/** + * Test RSA-specific signature APIs including low-level RSA functions + */ +int test_signature_apis_rsa(void) { + EVP_PKEY *key = NULL; + RSA *rsa_key = NULL; + const EVP_MD *md = EVP_sha256(); + const unsigned char message[] = "Test message for OpenSSL signature APIs"; + const size_t message_len = strlen((char *)message); + unsigned char *sig5 = NULL; + size_t sig_len5 = 0; + int success = 1; + + printf("\nGenerating RSA key pair...\n"); + key = generate_rsa_key(); + if (!key) return 0; + + rsa_key = get_rsa_from_pkey(key); + if (!rsa_key) { + EVP_PKEY_free(key); + return 0; + } + + /* Test generic APIs */ + if (!test_signature_apis(key, md, set_rsa_pss_padding, "RSA")) { + success = 0; + } + + /* Test 5: Low-level RSA API */ + printf("5. Low-level RSA API: "); + if (sign_using_rsa_sign(message, message_len, &sig5, &sig_len5, + rsa_key, NID_sha256, md) && + verify_using_rsa_verify(message, message_len, sig5, sig_len5, + rsa_key, NID_sha256, md)) { + printf("PASS\n"); + } else { + printf("FAIL\n"); + success = 0; + } + + printf("\nRSA API Summary:\n"); + printf("1. EVP_Sign API: Legacy, simple\n"); + printf("2. EVP_DigestSign API: Modern, recommended\n"); + printf("3. EVP_PKEY_sign API: Lower-level, pre-hashed input\n"); + printf("4. EVP_DigestSign with PKEY_CTX: Fine-grained control\n"); + printf("5. Low-level RSA API: Deprecated, algorithm-specific\n"); + printf("6. EVP_PKEY_sign_message API: Streamlined message signing\n"); + + /* Cleanup */ + OPENSSL_free(sig5); + RSA_free(rsa_key); + EVP_PKEY_free(key); + + return success; +} + +/** + * Test DSA-specific signature APIs including low-level DSA functions + */ +int test_signature_apis_dsa(void) { + EVP_PKEY *key = NULL; + DSA *dsa_key = NULL; + const EVP_MD *md = EVP_sha256(); + const unsigned char message[] = "Test message for OpenSSL signature APIs"; + const size_t message_len = strlen((char *)message); + unsigned char *sig5 = NULL; + size_t sig_len5 = 0; + int success = 1; + + printf("\nGenerating DSA key pair...\n"); + key = generate_dsa_key(); + if (!key) return 0; + + dsa_key = get_dsa_from_pkey(key); + if (!dsa_key) { + EVP_PKEY_free(key); + return 0; + } + + /* Test generic APIs */ + if (!test_signature_apis(key, md, no_parameter_setter, "DSA")) { + success = 0; + } + + /* Test 5: Low-level DSA API */ + printf("5. Low-level DSA API: "); + if (sign_using_dsa_sign(message, message_len, &sig5, &sig_len5, dsa_key, md) && + verify_using_dsa_verify(message, message_len, sig5, sig_len5, dsa_key, md)) { + printf("PASS\n"); + } else { + printf("FAIL\n"); + success = 0; + } + + printf("\nDSA API Summary:\n"); + printf("1. EVP_Sign API: Legacy, simple\n"); + printf("2. EVP_DigestSign API: Modern, recommended\n"); + printf("3. EVP_PKEY_sign API: Lower-level, pre-hashed input\n"); + printf("4. EVP_DigestSign with PKEY_CTX: Fine-grained control\n"); + printf("5. Low-level DSA API: Deprecated, algorithm-specific\n"); + printf("6. EVP_PKEY_sign_message API: Streamlined message signing\n"); + + /* Cleanup */ + OPENSSL_free(sig5); + EVP_PKEY_free(key); + + return success; +} + +/* ============================================================================= + * MAIN FUNCTION - Entry point for testing all signature APIs + * ============================================================================= + */ + +/** + * Main function demonstrating all OpenSSL signature APIs + * Tests both RSA and DSA algorithms with all 6 API approaches + */ +int main(void) { + /* Initialize OpenSSL */ + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + + printf("=================================================================\n"); + printf("OpenSSL Signature API Demonstration\n"); + printf("=================================================================\n"); + + printf("\n-------- TESTING RSA SIGNATURES --------\n"); + int rsa_result = test_signature_apis_rsa(); + + printf("\n-------- TESTING DSA SIGNATURES --------\n"); + int dsa_result = test_signature_apis_dsa(); + + printf("\n=================================================================\n"); + if (rsa_result && dsa_result) { + printf("All tests completed successfully.\n"); + return 0; + } else { + printf("Some tests failed.\n"); + return 1; + } +} diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql new file mode 100644 index 000000000000..9696a6947fd3 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql @@ -0,0 +1,7 @@ +import cpp +import experimental.quantum.Language +import experimental.quantum.OpenSSL.Operations.EVPSignatureOperation + + +from EVP_Signature_Operation n +select n, n.(Call).getTarget().getName(), n.getOutputArg() \ No newline at end of file From 310469a61f9d82053fa56bb0fe11c8bef4ae6c47 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 29 May 2025 14:18:06 +0200 Subject: [PATCH 10/21] fix super/parent bug, use new SignatureOperationNode --- .../OpenSSL/Operations/EVPSignatureOperation.qll | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll index c9b7927c7126..590999317ec6 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll @@ -93,7 +93,7 @@ private Expr signatureOperationOutputArg(Call call) { /** * Base configuration for all EVP signature operations. */ -abstract class EVP_Signature_Operation extends EVPOperation, Crypto::KeyOperationInstance { +abstract class EVP_Signature_Operation extends EVPOperation, Crypto::SignatureOperationInstance { EVP_Signature_Operation() { this.(Call).getTarget().getName().matches("EVP_%") and // NULL output argument means the call is to get the size of the signature @@ -128,12 +128,18 @@ abstract class EVP_Signature_Operation extends EVPOperation, Crypto::KeyOperatio } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { - result = this.(EVPOperation).getOutputArtifact() + result = EVPOperation.super.getOutputArtifact() } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { - result = this.(EVPOperation).getInputConsumer() + result = EVPOperation.super.getInputConsumer() } + + /** + * TODO: only signing operations for now, change when verificaiton is added + */ + override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { none() } + } class EVP_Signature_Call extends EVPOperation, EVP_Signature_Operation { From 804bd892c9b7c1235a1d7a87f358812d4f1b5b89 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 29 May 2025 15:28:41 +0200 Subject: [PATCH 11/21] flows for key from contexts --- .../PKeyAlgorithmValueConsumer.qll | 10 ++++++-- .../experimental/quantum/OpenSSL/CtxFlow.qll | 17 +++++++++++-- .../Operations/EVPSignatureOperation.qll | 24 +++++++++++++++---- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll index 0d40ceeb68af..63a510e1940a 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll @@ -17,13 +17,14 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer { // in these cases, the operation will be created separately for the same function. this.(Call).getTarget().getName() in [ "EVP_PKEY_CTX_new_id", "EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", - "EVP_PKEY_new_mac_key" + "EVP_PKEY_new_mac_key", "EVP_PKEY_CTX_new" ] and valueArgNode.asExpr() = this.(Call).getArgument(0) or this.(Call).getTarget().getName() in [ "EVP_PKEY_CTX_new_from_name", "EVP_PKEY_new_raw_private_key_ex", - "EVP_PKEY_new_raw_public_key_ex", "EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name" + "EVP_PKEY_new_raw_public_key_ex", "EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name", + "EVP_PKEY_CTX_new_from_pkey" ] and valueArgNode.asExpr() = this.(Call).getArgument(1) or @@ -42,6 +43,7 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer { // All other cases valueArgNode.asExpr() = this.(Call).getArgument(2) ) + // TODO: data flow for EVP_PKEY_CTX_dup ) } @@ -52,4 +54,8 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer { override DataFlow::Node getResultNode() { result = resultNode } override Crypto::ConsumerInputDataFlowNode getInputNode() { result = valueArgNode } + + // expose the valueArg as an Expr to avoid circular dependency that may arise + // when trying to get the valueArg with getInputNode. + Expr getValueArgExpr() { result = valueArgNode.asExpr() } } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll index 38b49b8d9010..c83870e887a9 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll @@ -100,7 +100,7 @@ private class CtxCopyReturnCall extends Call, CtxPointerExpr { * Flow from any CtxPointerArgument to any other CtxPointerArgument */ module OpenSSLCtxArgumentFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { source.asExpr() instanceof CtxPointerArgument } + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof CtxPointerExpr } predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof CtxPointerArgument } @@ -128,7 +128,8 @@ module OpenSSLCtxArgumentFlowConfig implements DataFlow::ConfigSig { module OpenSSLCtxArgumentFlow = DataFlow::Global; /** - * Holds if there is a context flow from the source to the sink. + * Holds if there is a context flow from the source to the sink, + * where the source and sink are arguments to function calls. */ predicate ctxArgFlowsToCtxArg(CtxPointerArgument source, CtxPointerArgument sink) { exists(DataFlow::Node a, DataFlow::Node b | @@ -137,3 +138,15 @@ predicate ctxArgFlowsToCtxArg(CtxPointerArgument source, CtxPointerArgument sink b.asExpr() = sink ) } + +/** + * Holds if there is a context flow from the source to the sink, + * where the source is a variable and the sink is an argument to a function call. + */ +predicate ctxFlowsToCtxArg(CtxPointerExpr source, CtxPointerArgument sink) { + exists(DataFlow::Node a, DataFlow::Node b | + OpenSSLCtxArgumentFlow::flow(a, b) and + a.asExpr() = source and + b.asExpr() = sink + ) +} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll index 590999317ec6..4298131a2546 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll @@ -4,7 +4,8 @@ private import experimental.quantum.Language private import OpenSSLOperationBase -private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow +private import experimental.quantum.OpenSSL.CtxFlow +private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.PKeyAlgorithmValueConsumer // TODO: verification functions class EVP_Signature_Initializer extends EVPInitialize { @@ -44,7 +45,7 @@ class EVP_Signature_Initializer extends EVPInitialize { /** * Returns the key argument if there is one. - * They key could be provided in the context or in a later call (final or one-shot). + * If the key was provided via the context, we track it to the context. */ override Expr getKeyArg() { this.(Call).getTarget().getName() = "EVP_DigestSignInit" and @@ -52,6 +53,12 @@ class EVP_Signature_Initializer extends EVPInitialize { or this.(Call).getTarget().getName() = "EVP_DigestSignInit_ex" and result = this.(Call).getArgument(5) + or + this.(Call).getTarget().getName().matches("EVP_PKEY_%") and + exists(EVPPKeyAlgorithmConsumer source | + result = source.getValueArgExpr() and + ctxFlowsToCtxArg(source.getResultNode().asExpr(), this.getContextArg()) + ) } /** @@ -123,8 +130,10 @@ abstract class EVP_Signature_Operation extends EVPOperation, Crypto::SignatureOp } override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { - result = DataFlow::exprNode(this.getInitCall().getKeyArg()) - // TODO: or track to the EVP_PKEY_CTX_new + // TODO: move to EVPOperation similarly to getAlgorithmArg + if exists(this.getInitCall().getKeyArg()) + then result = DataFlow::exprNode(this.getInitCall().getKeyArg()) + else none() } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { @@ -139,7 +148,6 @@ abstract class EVP_Signature_Operation extends EVPOperation, Crypto::SignatureOp * TODO: only signing operations for now, change when verificaiton is added */ override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { none() } - } class EVP_Signature_Call extends EVPOperation, EVP_Signature_Operation { @@ -163,6 +171,12 @@ class EVP_Signature_Final_Call extends EVPFinal, EVP_Signature_Operation { ] } + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + if this.(Call).getTarget().getName() in ["EVP_SignFinal", "EVP_SignFinal_ex"] + then result = DataFlow::exprNode(this.(Call).getArgument(3)) + else none() + } + /** * Output is the signature. */ From 02fb52c3796c9927a5e44f37bc4c7552aad41b02 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 29 May 2025 17:58:36 +0200 Subject: [PATCH 12/21] make signature tests work for algorithms --- .../KnownAlgorithmConstants.qll | 2 ++ .../OpenSSLAlgorithmValueConsumers.qll | 1 + .../SignatureAlgorithmValueConsumer.qll | 6 +++--- .../Operations/EVPSignatureOperation.qll | 11 +++++----- .../openssl/includes/alg_macro_stubs.h | 6 ++---- .../quantum/openssl/includes/evp_stubs.h | 19 ++--------------- .../quantum/openssl/includes/std_stubs.h | 12 +++++++++++ .../quantum/openssl/openssl_basic.c | 21 +++++++++++++------ .../openssl/signature/openssl_signature.c | 21 ++++++++++++------- .../signature/signature_algorithms.expected | 4 ++++ .../openssl/signature/signature_algorithms.ql | 6 ++++++ 11 files changed, 67 insertions(+), 42 deletions(-) create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/includes/std_stubs.h create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.ql diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll index e28b89b03f77..29438d2ef996 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll @@ -2829,6 +2829,8 @@ predicate knownOpenSSLAlgorithmLiteral(string name, int nid, string normalized, or name = "rsaencryption" and nid = 6 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or + name = "rsaencryption" and nid = 6 and normalized = "RSA" and algType = "SIGNATURE" + or name = "rsaes-oaep" and nid = 919 and normalized = "RSA" and algType = "ASYMMETRIC_ENCRYPTION" or name = "rsaes-oaep" and nid = 919 and normalized = "OAEP" and algType = "ASYMMETRIC_PADDING" diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll index c76d6d6f041c..8b862e2a7ccb 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/OpenSSLAlgorithmValueConsumers.qll @@ -5,3 +5,4 @@ import PaddingAlgorithmValueConsumer import HashAlgorithmValueConsumer import EllipticCurveAlgorithmValueConsumer import PKeyAlgorithmValueConsumer +import SignatureAlgorithmValueConsumer diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/SignatureAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/SignatureAlgorithmValueConsumer.qll index 8701a3658934..077457e90cc9 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/SignatureAlgorithmValueConsumer.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/SignatureAlgorithmValueConsumer.qll @@ -7,18 +7,18 @@ private import experimental.quantum.OpenSSL.LibraryDetector abstract class SignatureAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { } -class EVPSignatureAlgorithmValueConsumer extends OpenSSLAlgorithmValueConsumer { +class EVPSignatureAlgorithmValueConsumer extends SignatureAlgorithmValueConsumer { DataFlow::Node valueArgNode; DataFlow::Node resultNode; EVPSignatureAlgorithmValueConsumer() { resultNode.asExpr() = this and - isPossibleOpenSSLFunction(this.(Call).getTarget()) and ( // EVP_SIGNATURE this.(Call).getTarget().getName() = "EVP_SIGNATURE_fetch" and valueArgNode.asExpr() = this.(Call).getArgument(1) - // EVP_PKEY_get1_DSA, DSA_SIG_new, EVP_RSA_gen + // EVP_PKEY_get1_DSA, EVP_PKEY_get1_RSA + // DSA_SIG_new, DSA_SIG_get0, RSA_sign ? ) } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll index 4298131a2546..696f11dd0909 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll @@ -129,11 +129,12 @@ abstract class EVP_Signature_Operation extends EVPOperation, Crypto::SignatureOp none() } + /** + * Keys provided in the initialization call or in a context are found by this method. + * Keys in explicit arguments are found by overriden methods in extending classes. + */ override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { - // TODO: move to EVPOperation similarly to getAlgorithmArg - if exists(this.getInitCall().getKeyArg()) - then result = DataFlow::exprNode(this.getInitCall().getKeyArg()) - else none() + result = DataFlow::exprNode(this.getInitCall().getKeyArg()) } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { @@ -174,7 +175,7 @@ class EVP_Signature_Final_Call extends EVPFinal, EVP_Signature_Operation { override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { if this.(Call).getTarget().getName() in ["EVP_SignFinal", "EVP_SignFinal_ex"] then result = DataFlow::exprNode(this.(Call).getArgument(3)) - else none() + else result = EVP_Signature_Operation.super.getKeyConsumer() } /** diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/alg_macro_stubs.h b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/alg_macro_stubs.h index f08de0f2e959..a3e74b5ca008 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/alg_macro_stubs.h +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/alg_macro_stubs.h @@ -1,6 +1,3 @@ -# define RSA_PKCS1_PSS_PADDING 6 -# define NID_sha256 672 - # define EVP_PKEY_NONE NID_undef # define EVP_PKEY_RSA NID_rsaEncryption # define EVP_PKEY_RSA2 NID_rsa @@ -3743,5 +3740,6 @@ #define LN_undef "undefined" #define SN_undef "UNDEF" +#define EVP_MAX_MD_SIZE 64 #define RSA_PKCS1_PSS_PADDING 6 -# define EVP_MAX_MD_SIZE 64 \ No newline at end of file +#define NID_sha256 672 \ No newline at end of file diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/evp_stubs.h b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/evp_stubs.h index bf8cf73a4f81..5fcc90bfe288 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/evp_stubs.h +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/evp_stubs.h @@ -28,7 +28,6 @@ # define EVP_CTRL_CCM_SET_IV_FIXED EVP_CTRL_AEAD_SET_IV_FIXED # define EVP_CTRL_CCM_SET_L 0x14 # define EVP_CTRL_CCM_SET_MSGLEN 0x15 -# define EVP_MAX_MD_SIZE 64 typedef unsigned long size_t; @@ -36,7 +35,6 @@ typedef unsigned char uint8_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; -// Forward declarations for opaque structs struct rsa_st; struct dsa_st; struct dh_st; @@ -5056,18 +5054,6 @@ int EVP_VerifyUpdate(EVP_MD_CTX * ctx, const void * data, size_t dsize) { return 0; } -int printf(const char*, ...) { - return NULL; -} - -int strlen(const char *s) { - return NULL; -} - -void* memset(void *s, int c, size_t n) { - return NULL; -} - int RSA_size(const RSA * rsa) { return 0; } @@ -5126,8 +5112,7 @@ BIGNUM * BN_bin2bn(const unsigned char * s, int len, BIGNUM * ret) { return NULL; } -void OpenSSL_add_all_algorithms(void) ; - -void ERR_load_crypto_strings(void) ; +void OpenSSL_add_all_algorithms(void); +void ERR_load_crypto_strings(void); #endif /* OSSL_EVP_H */ diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/std_stubs.h b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/std_stubs.h new file mode 100644 index 000000000000..fe8e31f7cbcb --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/std_stubs.h @@ -0,0 +1,12 @@ +#ifndef STD_STUBS_H +#define STD_STUBS_H + +unsigned long strlen(const char *s) { + return 0; +} + +void* memset(void *s, int c, unsigned long n) { + return 0; +} + +#endif /* STD_STUBS_H */ \ No newline at end of file diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/openssl_basic.c b/cpp/ql/test/experimental/library-tests/quantum/openssl/openssl_basic.c index 5e3537064b5b..8cae5f1cbd94 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/openssl_basic.c +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/openssl_basic.c @@ -1,8 +1,17 @@ -#include "includes/evp_stubs.h" -#include "includes/alg_macro_stubs.h" -#include "includes/rand_stubs.h" - -size_t strlen(const char* str); +#ifdef USE_REAL_HEADERS +#include +#include +#include +#include +#include +#include +#include +#else +#include "./includes/evp_stubs.h" +#include "./includes/alg_macro_stubs.h" +#include "./includes/rand_stubs.h" +#include "./includes/std_stubs.h" +#endif // Sample OpenSSL code that demonstrates various cryptographic operations // that can be detected by the quantum model @@ -218,4 +227,4 @@ int test_main() { calculate_hmac_sha256(key, 32, plaintext, plaintext_len, hmac); return 0; -} \ No newline at end of file +} diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c index e8054467f518..6455f7e2809b 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c @@ -1,13 +1,16 @@ -#ifndef USE_REAL_HEADERS -#include "../includes/evp_stubs.h" -#include "../includes/alg_macro_stubs.h" -#include "../includes/rand_stubs.h" -#else +#ifdef USE_REAL_HEADERS #include -#include #include #include -#include +#include +#include +#include +#include +#else +#include "../includes/evp_stubs.h" +#include "../includes/alg_macro_stubs.h" +#include "../includes/rand_stubs.h" +#include "../includes/std_stubs.h" #endif /* ============================================================================= @@ -76,6 +79,9 @@ int sign_using_evp_sign(const unsigned char *message, size_t message_len, EVP_SignUpdate(md_ctx, message, message_len) != 1) { goto cleanup; } + + // more updates + EVP_SignUpdate(md_ctx, message+1, message_len-1); *signature = allocate_signature_buffer(signature_len, pkey); if (!*signature) goto cleanup; @@ -106,6 +112,7 @@ int verify_using_evp_verify(const unsigned char *message, size_t message_len, if (!(md_ctx = EVP_MD_CTX_new()) || EVP_VerifyInit(md_ctx, md) != 1 || EVP_VerifyUpdate(md_ctx, message, message_len) != 1 || + EVP_VerifyUpdate(md_ctx, message+1, message_len-1) != 1 || EVP_VerifyFinal(md_ctx, signature, (unsigned int)signature_len, pkey) != 1) { goto cleanup; } diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected new file mode 100644 index 000000000000..a1859f541ec4 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected @@ -0,0 +1,4 @@ +| openssl_signature.c:332:37:332:48 | RSA-SHA256 | RSA | openssl_signature.c:332:11:332:29 | call to EVP_SIGNATURE_fetch | +| openssl_signature.c:368:37:368:48 | RSA-SHA256 | RSA | openssl_signature.c:368:11:368:29 | call to EVP_SIGNATURE_fetch | +| openssl_signature.c:552:35:552:46 | 6 | RSA | openssl_signature.c:552:15:552:33 | call to EVP_PKEY_CTX_new_id | +| openssl_signature.c:574:50:574:54 | DSA | DSA | openssl_signature.c:574:17:574:42 | call to EVP_PKEY_CTX_new_from_name | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.ql b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.ql new file mode 100644 index 000000000000..113fba415c95 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.ql @@ -0,0 +1,6 @@ +import cpp +import experimental.quantum.Language +import experimental.quantum.OpenSSL.AlgorithmInstances.SignatureAlgorithmInstance + +from KnownOpenSSLSignatureConstantAlgorithmInstance algoInstance +select algoInstance, algoInstance.getAlgorithmType(), algoInstance.getAVC() From 16c136eeb532712e2c2d56eae84b27014f4356c3 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Thu, 29 May 2025 20:07:04 +0200 Subject: [PATCH 13/21] openssl keygen & tracking of keys-contexts-algorithms --- .../PKeyAlgorithmValueConsumer.qll | 64 ++++++++++++++++++- .../experimental/quantum/OpenSSL/CtxFlow.qll | 2 +- .../OpenSSL/Operations/EVPKeyGenOperation.qll | 49 ++++++++++++++ .../Operations/EVPSignatureOperation.qll | 26 ++++---- .../Operations/OpenSSLOperationBase.qll | 9 ++- .../openssl/signature/signature_operations.ql | 11 +++- 6 files changed, 139 insertions(+), 22 deletions(-) create mode 100644 cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll index 63a510e1940a..19c2d76bdb65 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll @@ -3,6 +3,9 @@ private import experimental.quantum.Language private import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase private import experimental.quantum.OpenSSL.AlgorithmInstances.OpenSSLAlgorithmInstances +private import experimental.quantum.OpenSSL.Operations.EVPKeyGenOperation +private import experimental.quantum.OpenSSL.Operations.OpenSSLOperationBase +private import experimental.quantum.OpenSSL.CtxFlow abstract class PKeyValueConsumer extends OpenSSLAlgorithmValueConsumer { } @@ -17,14 +20,13 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer { // in these cases, the operation will be created separately for the same function. this.(Call).getTarget().getName() in [ "EVP_PKEY_CTX_new_id", "EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", - "EVP_PKEY_new_mac_key", "EVP_PKEY_CTX_new" + "EVP_PKEY_new_mac_key" ] and valueArgNode.asExpr() = this.(Call).getArgument(0) or this.(Call).getTarget().getName() in [ "EVP_PKEY_CTX_new_from_name", "EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex", "EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name", - "EVP_PKEY_CTX_new_from_pkey" ] and valueArgNode.asExpr() = this.(Call).getArgument(1) or @@ -59,3 +61,61 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer { // when trying to get the valueArg with getInputNode. Expr getValueArgExpr() { result = valueArgNode.asExpr() } } + +// TODO: not sure where to put these + +/** + * Given context expression (EVP_PKEY_CTX), finds the algorithm. + */ +Expr getAlgorithmFromCtx(CtxPointerExpr ctx) { + exists(EVPPKeyAlgorithmConsumer source | + result = source.getValueArgExpr() and + ctxFlowsToCtxArg(source.getResultNode().asExpr(), ctx) + ) + or + result = getAlgorithmFromKey(getKeyFromCtx(ctx)) +} + +/** + * Given context expression (EVP_PKEY_CTX), finds the key used to initialize the context. + */ +Expr getKeyFromCtx(CtxPointerExpr ctx) { + exists(Call contextCreationCall | + ctxFlowsToCtxArg(contextCreationCall, ctx) and ( + contextCreationCall.getTarget().getName() = "EVP_PKEY_CTX_new" and + result = contextCreationCall.getArgument(0) + or + contextCreationCall.getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and + result = contextCreationCall.getArgument(1) + ) + ) +} + +/** + * Flow from key creation to key used in a call + */ +module OpenSSLKeyFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(EVPKeyGenOperation keygen | + keygen.getOutputKeyArtifact() = source + ) + } + + predicate isSink(DataFlow::Node sink) { + exists(OpenSSLOperation call | + call.(Call).getAnArgument() = sink.asExpr() + ) + } +} + +module OpenSSLKeyFlow = TaintTracking::Global; + +/** + * Given key expression (EVP_PKEY), finds the algorithm. + */ +Expr getAlgorithmFromKey(Expr key) { + exists(EVPKeyGenOperation keygen | + OpenSSLKeyFlow::flow(keygen.getOutputKeyArtifact(), DataFlow::exprNode(key)) and + result = keygen.getAlgorithmArg() + ) +} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll index c83870e887a9..f4684eae3cb9 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll @@ -47,7 +47,7 @@ private class CtxType extends Type { /** * A pointer to a CtxType */ -private class CtxPointerExpr extends Expr { +class CtxPointerExpr extends Expr { CtxPointerExpr() { this.getType() instanceof CtxType and this.getType() instanceof PointerType diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll new file mode 100644 index 000000000000..594d61f7c3a4 --- /dev/null +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll @@ -0,0 +1,49 @@ +private import experimental.quantum.Language +private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow +private import OpenSSLOperationBase +private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +private import semmle.code.cpp.dataflow.new.DataFlow + + +class EVPKeyGenInitialize extends EVPInitialize { + EVPKeyGenInitialize() { this.(Call).getTarget().getName() in [ + "EVP_PKEY_keygen_init", "EVP_PKEY_paramgen_init" + ] + } + + override Expr getAlgorithmArg() { + result = getAlgorithmFromCtx(this.getContextArg()) + } +} + +class EVPKeyGenOperation extends EVPOperation, Crypto::KeyGenerationOperationInstance { + EVPKeyGenOperation() { this.(Call).getTarget().getName() in [ + "EVP_PKEY_generate", "EVP_PKEY_paramgen", "EVP_PKEY_keygen", "EVP_PKEY_Q_keygen" + ] + } + + override Expr getAlgorithmArg() { + if this.(Call).getTarget().getName() = "EVP_PKEY_Q_keygen" then + result = this.(Call).getArgument(0) + else + result = EVPOperation.super.getAlgorithmArg() +} + + override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() } + + override Expr getInputArg() { none() } + + override Expr getOutputArg() { result = this.(Call).getArgument(1) } + + override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { + result = EVPOperation.super.getOutputKeyArtifact() + } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { + none() // TODO + } + + override int getKeySizeFixed() { + none() // TODO + } +} diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll index 696f11dd0909..feca732a5267 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll @@ -7,6 +7,7 @@ private import OpenSSLOperationBase private import experimental.quantum.OpenSSL.CtxFlow private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.PKeyAlgorithmValueConsumer + // TODO: verification functions class EVP_Signature_Initializer extends EVPInitialize { boolean isAlgorithmSpecifiedByKey; @@ -30,17 +31,17 @@ class EVP_Signature_Initializer extends EVPInitialize { ) } - /** - * Returns the argument that specifies the algorithm or none if the algorithm is implicit (from context or from key). - * Note that the key may be not provided in the initialization call. - */ override Expr getAlgorithmArg() { - if isAlgorithmSpecifiedByKey = true or isAlgorithmSpecifiedByCtx = true - then none() - else ( - this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] and + if this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] then result = this.(Call).getArgument(1) - ) + else + if isAlgorithmSpecifiedByKey = true then + result = getAlgorithmFromKey(this.getKeyArg()) + else + if isAlgorithmSpecifiedByCtx = true then + result = getAlgorithmFromCtx(this.getContextArg()) + else + none() } /** @@ -55,10 +56,7 @@ class EVP_Signature_Initializer extends EVPInitialize { result = this.(Call).getArgument(5) or this.(Call).getTarget().getName().matches("EVP_PKEY_%") and - exists(EVPPKeyAlgorithmConsumer source | - result = source.getValueArgExpr() and - ctxFlowsToCtxArg(source.getResultNode().asExpr(), this.getContextArg()) - ) + result = getKeyFromCtx(this.getContextArg()) } /** @@ -103,7 +101,7 @@ private Expr signatureOperationOutputArg(Call call) { abstract class EVP_Signature_Operation extends EVPOperation, Crypto::SignatureOperationInstance { EVP_Signature_Operation() { this.(Call).getTarget().getName().matches("EVP_%") and - // NULL output argument means the call is to get the size of the signature + // NULL output argument means the call is to get the size of the signature and such call is not an operation ( not exists(signatureOperationOutputArg(this).getValue()) or diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll index 7e9e781c8971..9dd7f9aa35e0 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll @@ -13,7 +13,7 @@ abstract class OpenSSLOperation extends Crypto::OperationInstance instanceof Cal abstract Expr getAlgorithmArg(); /** - * Algorithm is specified in initialization call or is implicitly established by the key. + * Algorithm is specified in initialization call or is implicitly established by the key or context. */ override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(), @@ -38,7 +38,8 @@ abstract class EVPInitialize extends Call { Crypto::KeyOperationSubtype getKeyOperationSubtype() { none() } /** - * Explicitly specified algorithm or none if implicit (e.g., established by the key). + * Explicitly specified algorithm or algorithm established by the key or context + * (should track flows to the key and/or context to return the algorithm expression) * None if not applicable. */ Expr getAlgorithmArg() { none() } @@ -131,6 +132,10 @@ abstract class EVPOperation extends OpenSSLOperation { result = DataFlow::exprNode(this.getOutputArg()) } + Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { + result = DataFlow::exprNode(this.getOutputArg()) + } + /** * Input consumer is the input argument of the call. */ diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql index 9696a6947fd3..6319cf6a1908 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql @@ -1,7 +1,12 @@ import cpp import experimental.quantum.Language +import experimental.quantum.OpenSSL.AlgorithmValueConsumers.PKeyAlgorithmValueConsumer +import experimental.quantum.OpenSSL.CtxFlow import experimental.quantum.OpenSSL.Operations.EVPSignatureOperation +import experimental.quantum.OpenSSL.Operations.OpenSSLOperationBase - -from EVP_Signature_Operation n -select n, n.(Call).getTarget().getName(), n.getOutputArg() \ No newline at end of file +from Crypto::SignatureOperationNode n +select n, n.asElement(), n.getAnInputArtifact(), n.getAnOutputArtifact(), n.getAKey(), + n.asElement().(OpenSSLOperation).getAlgorithmArg() + // n.asElement().(Crypto::OperationInstance).getAnAlgorithmValueConsumer(), + // n.getAnAlgorithmOrGenericSource() From 367235804b0e9b42924af2cd2ebc56a22db43b0c Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 30 May 2025 12:09:09 +0200 Subject: [PATCH 14/21] signature algorithms are tracked from keys and contexts --- .../PKeyAlgorithmValueConsumer.qll | 14 ++--- .../Operations/EVPSignatureOperation.qll | 55 ++++++++++--------- .../Operations/OpenSSLOperationBase.qll | 21 +++++-- .../quantum/openssl/includes/std_stubs.h | 2 + .../openssl/signature/openssl_signature.c | 14 ++--- .../signature/signature_algorithms.expected | 6 +- .../signature/signature_operations.expected | 12 ++++ .../openssl/signature/signature_operations.ql | 8 +-- 8 files changed, 79 insertions(+), 53 deletions(-) create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.expected diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll index 19c2d76bdb65..458cae68139c 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll @@ -62,7 +62,8 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer { Expr getValueArgExpr() { result = valueArgNode.asExpr() } } -// TODO: not sure where to put these +// TODO: not sure where to put these predicates +Expr getAlgorithmFromArgument(Expr arg) { none() } /** * Given context expression (EVP_PKEY_CTX), finds the algorithm. @@ -81,7 +82,8 @@ Expr getAlgorithmFromCtx(CtxPointerExpr ctx) { */ Expr getKeyFromCtx(CtxPointerExpr ctx) { exists(Call contextCreationCall | - ctxFlowsToCtxArg(contextCreationCall, ctx) and ( + ctxFlowsToCtxArg(contextCreationCall, ctx) and + ( contextCreationCall.getTarget().getName() = "EVP_PKEY_CTX_new" and result = contextCreationCall.getArgument(0) or @@ -96,15 +98,11 @@ Expr getKeyFromCtx(CtxPointerExpr ctx) { */ module OpenSSLKeyFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { - exists(EVPKeyGenOperation keygen | - keygen.getOutputKeyArtifact() = source - ) + exists(EVPKeyGenOperation keygen | keygen.getOutputKeyArtifact() = source) } predicate isSink(DataFlow::Node sink) { - exists(OpenSSLOperation call | - call.(Call).getAnArgument() = sink.asExpr() - ) + exists(OpenSSLCall call | call.(Call).getAnArgument() = sink.asExpr()) } } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll index feca732a5267..a5eaf30eabaa 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPSignatureOperation.qll @@ -7,41 +7,32 @@ private import OpenSSLOperationBase private import experimental.quantum.OpenSSL.CtxFlow private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.PKeyAlgorithmValueConsumer - // TODO: verification functions class EVP_Signature_Initializer extends EVPInitialize { - boolean isAlgorithmSpecifiedByKey; - boolean isAlgorithmSpecifiedByCtx; - EVP_Signature_Initializer() { this.(Call).getTarget().getName() in [ "EVP_DigestSignInit", "EVP_DigestSignInit_ex", "EVP_SignInit", "EVP_SignInit_ex", "EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex", "EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init" - ] and - ( - if this.(Call).getTarget().getName().matches("EVP_PKEY_%") - then isAlgorithmSpecifiedByKey = false - else isAlgorithmSpecifiedByKey = true - ) and - ( - if this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex"] - then isAlgorithmSpecifiedByCtx = true - else isAlgorithmSpecifiedByCtx = false - ) + ] } override Expr getAlgorithmArg() { - if this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] then - result = this.(Call).getArgument(1) - else - if isAlgorithmSpecifiedByKey = true then - result = getAlgorithmFromKey(this.getKeyArg()) - else - if isAlgorithmSpecifiedByCtx = true then - result = getAlgorithmFromCtx(this.getContextArg()) - else - none() + // explicit algorithm as argument + this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] and + result = this.(Call).getArgument(1) + or + // algorithm (and key) specified in the context + this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex"] and + result = getAlgorithmFromCtx(this.getContextArg()) + or + // algorithm specified by the key + this.(Call).getTarget().getName() in ["EVP_DigestSignInit", "EVP_DigestSignInit_ex"] and + result = getAlgorithmFromKey(this.getKeyArg()) + or + // cannot determine algorithm from the initialization call + this.(Call).getTarget().getName() in ["EVP_SignInit", "EVP_SignInit_ex"] and + none() } /** @@ -170,10 +161,22 @@ class EVP_Signature_Final_Call extends EVPFinal, EVP_Signature_Operation { ] } + override Expr getAlgorithmArg() { + // algorithm specified by the key and the key is provided in this operation + if this.(Call).getTarget().getName() in ["EVP_SignFinal", "EVP_SignFinal_ex"] + then result = getAlgorithmFromKey(this.getKeyConsumer().asExpr()) + else + // or find algorithm in the initialization call + result = EVP_Signature_Operation.super.getAlgorithmArg() + } + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + // key provided as an argument if this.(Call).getTarget().getName() in ["EVP_SignFinal", "EVP_SignFinal_ex"] then result = DataFlow::exprNode(this.(Call).getArgument(3)) - else result = EVP_Signature_Operation.super.getKeyConsumer() + else + // or find key in the initialization call + result = EVP_Signature_Operation.super.getKeyConsumer() } /** diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll index 9dd7f9aa35e0..85cde41671e8 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll @@ -2,22 +2,31 @@ private import experimental.quantum.Language private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers +/** + * All functions from the OpenSSL API. + */ +class OpenSSLCall extends Call { } + /** * All OpenSSL operations. */ -abstract class OpenSSLOperation extends Crypto::OperationInstance instanceof Call { +abstract class OpenSSLOperation extends Crypto::OperationInstance instanceof OpenSSLCall { /** * Expression that specifies the algorithm for the operation. - * Will be an argument of the operation in the simplest case. + * Will be an argument of the operation in the simplest case + * and EVPPKeyAlgorithmConsumer's valueArgExpr in more complex cases. */ abstract Expr getAlgorithmArg(); /** - * Algorithm is specified in initialization call or is implicitly established by the key or context. + * Algorithm is either an argument and we track it to AlgorithmValueConsumer + * or we have the AlgorithmValueConsumer already and just return it. */ override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(), DataFlow::exprNode(this.getAlgorithmArg())) + or + result.(EVPPKeyAlgorithmConsumer).getValueArgExpr() = this.getAlgorithmArg() } } @@ -26,7 +35,7 @@ abstract class OpenSSLOperation extends Crypto::OperationInstance instanceof Cal * These are not operations in the sense of Crypto::OperationInstance, * but they are used to initialize the context for the operation. */ -abstract class EVPInitialize extends Call { +abstract class EVPInitialize extends OpenSSLCall { /** * The context argument that ties together initialization, updates and/or final calls. */ @@ -60,7 +69,7 @@ abstract class EVPInitialize extends Call { * These are not operations in the sense of Crypto::OperationInstance, * but they are used to update the context for the operation. */ -abstract class EVPUpdate extends Call { +abstract class EVPUpdate extends OpenSSLCall { /** * The context argument that ties together initialization, updates and/or final calls. */ @@ -86,7 +95,7 @@ private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig { } predicate isSink(DataFlow::Node sink) { - exists(EVPOperation c | c.getAlgorithmArg() = sink.asExpr()) + exists(OpenSSLOperation c | c.getAlgorithmArg() = sink.asExpr()) } } diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/std_stubs.h b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/std_stubs.h index fe8e31f7cbcb..20fab47d6db6 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/std_stubs.h +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/includes/std_stubs.h @@ -1,6 +1,8 @@ #ifndef STD_STUBS_H #define STD_STUBS_H +int printf(const char *format, ...); + unsigned long strlen(const char *s) { return 0; } diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c index 6455f7e2809b..890453b2da96 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c @@ -322,14 +322,14 @@ int verify_using_evp_pkey_verify(const unsigned char *digest, size_t digest_len, */ int sign_using_evp_pkey_sign_message(const unsigned char *message, size_t message_len, unsigned char **signature, size_t *signature_len, - EVP_PKEY *pkey, const EVP_MD *md) { + EVP_PKEY *pkey, const EVP_MD *md, const char *alg_name) { EVP_PKEY_CTX *pkey_ctx = NULL; EVP_SIGNATURE *alg = NULL; int ret = 0; if (!(pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL))) goto cleanup; - alg = EVP_SIGNATURE_fetch(NULL, "RSA-SHA256", NULL); + alg = EVP_SIGNATURE_fetch(NULL, alg_name, NULL); if (EVP_PKEY_sign_message_init(pkey_ctx, alg, NULL) != 1 || EVP_PKEY_sign_message_update(pkey_ctx, message, message_len) != 1 || @@ -358,14 +358,14 @@ int sign_using_evp_pkey_sign_message(const unsigned char *message, size_t messag */ int verify_using_evp_pkey_verify_message(const unsigned char *message, size_t message_len, const unsigned char *signature, size_t signature_len, - EVP_PKEY *pkey, const EVP_MD *md) { + EVP_PKEY *pkey, const EVP_MD *md, const char *alg_name) { EVP_PKEY_CTX *pkey_ctx = NULL; EVP_SIGNATURE *alg = NULL; int ret = 0; if (!(pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL))) goto cleanup; - alg = EVP_SIGNATURE_fetch(NULL, "RSA-SHA256", NULL); + alg = EVP_SIGNATURE_fetch(NULL, alg_name, NULL); if (EVP_PKEY_verify_message_init(pkey_ctx, alg, NULL) != 1) goto cleanup; @@ -666,8 +666,8 @@ int test_signature_apis(EVP_PKEY *key, const EVP_MD *md, /* Test 6: EVP_PKEY_sign_message API */ printf("6. EVP_PKEY_sign_message API: "); - if (sign_using_evp_pkey_sign_message(message, message_len, &sig6, &sig_len6, key, md) && - verify_using_evp_pkey_verify_message(message, message_len, sig6, sig_len6, key, md)) { + if (sign_using_evp_pkey_sign_message(message, message_len, &sig6, &sig_len6, key, md, algo_name) && + verify_using_evp_pkey_verify_message(message, message_len, sig6, sig_len6, key, md, algo_name)) { printf("PASS\n"); } else { printf("FAIL\n"); @@ -708,7 +708,7 @@ int test_signature_apis_rsa(void) { } /* Test generic APIs */ - if (!test_signature_apis(key, md, set_rsa_pss_padding, "RSA")) { + if (!test_signature_apis(key, md, set_rsa_pss_padding, "RSA-SHA256")) { success = 0; } diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected index a1859f541ec4..31928dc37041 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected @@ -1,4 +1,6 @@ -| openssl_signature.c:332:37:332:48 | RSA-SHA256 | RSA | openssl_signature.c:332:11:332:29 | call to EVP_SIGNATURE_fetch | -| openssl_signature.c:368:37:368:48 | RSA-SHA256 | RSA | openssl_signature.c:368:11:368:29 | call to EVP_SIGNATURE_fetch | | openssl_signature.c:552:35:552:46 | 6 | RSA | openssl_signature.c:552:15:552:33 | call to EVP_PKEY_CTX_new_id | | openssl_signature.c:574:50:574:54 | DSA | DSA | openssl_signature.c:574:17:574:42 | call to EVP_PKEY_CTX_new_from_name | +| openssl_signature.c:711:60:711:71 | RSA-SHA256 | RSA | openssl_signature.c:332:11:332:29 | call to EVP_SIGNATURE_fetch | +| openssl_signature.c:711:60:711:71 | RSA-SHA256 | RSA | openssl_signature.c:368:11:368:29 | call to EVP_SIGNATURE_fetch | +| openssl_signature.c:767:60:767:64 | DSA | DSA | openssl_signature.c:332:11:332:29 | call to EVP_SIGNATURE_fetch | +| openssl_signature.c:767:60:767:64 | DSA | DSA | openssl_signature.c:368:11:368:29 | call to EVP_SIGNATURE_fetch | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.expected new file mode 100644 index 000000000000..88d44cda217c --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.expected @@ -0,0 +1,12 @@ +| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:79:32:79:38 | Message | openssl_signature.c:89:31:89:40 | SignatureOutput | openssl_signature.c:89:53:89:56 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | Sign | +| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:79:32:79:38 | Message | openssl_signature.c:89:31:89:40 | SignatureOutput | openssl_signature.c:89:53:89:56 | Key | openssl_signature.c:574:50:574:54 | Constant | Sign | +| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:84:28:84:36 | Message | openssl_signature.c:89:31:89:40 | SignatureOutput | openssl_signature.c:89:53:89:56 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | Sign | +| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:84:28:84:36 | Message | openssl_signature.c:89:31:89:40 | SignatureOutput | openssl_signature.c:89:53:89:56 | Key | openssl_signature.c:574:50:574:54 | Constant | Sign | +| openssl_signature.c:151:9:151:27 | SignOperation | openssl_signature.c:143:38:143:44 | Message | openssl_signature.c:151:37:151:46 | SignatureOutput | openssl_signature.c:142:52:142:55 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | Sign | +| openssl_signature.c:151:9:151:27 | SignOperation | openssl_signature.c:143:38:143:44 | Message | openssl_signature.c:151:37:151:46 | SignatureOutput | openssl_signature.c:142:52:142:55 | Key | openssl_signature.c:574:50:574:54 | Constant | Sign | +| openssl_signature.c:213:9:213:27 | SignOperation | openssl_signature.c:205:38:205:44 | Message | openssl_signature.c:213:37:213:46 | SignatureOutput | openssl_signature.c:199:57:199:60 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | Sign | +| openssl_signature.c:213:9:213:27 | SignOperation | openssl_signature.c:205:38:205:44 | Message | openssl_signature.c:213:37:213:46 | SignatureOutput | openssl_signature.c:199:57:199:60 | Key | openssl_signature.c:574:50:574:54 | Constant | Sign | +| openssl_signature.c:279:9:279:21 | SignOperation | openssl_signature.c:279:60:279:65 | Message | openssl_signature.c:279:33:279:42 | SignatureOutput | openssl_signature.c:269:39:269:42 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | Sign | +| openssl_signature.c:279:9:279:21 | SignOperation | openssl_signature.c:279:60:279:65 | Message | openssl_signature.c:279:33:279:42 | SignatureOutput | openssl_signature.c:269:39:269:42 | Key | openssl_signature.c:574:50:574:54 | Constant | Sign | +| openssl_signature.c:343:9:343:35 | SignOperation | openssl_signature.c:335:48:335:54 | Message | openssl_signature.c:343:47:343:56 | SignatureOutput | openssl_signature.c:330:39:330:42 | Key | openssl_signature.c:711:60:711:71 | KeyOperationAlgorithm | Sign | +| openssl_signature.c:343:9:343:35 | SignOperation | openssl_signature.c:335:48:335:54 | Message | openssl_signature.c:343:47:343:56 | SignatureOutput | openssl_signature.c:330:39:330:42 | Key | openssl_signature.c:767:60:767:64 | Constant | Sign | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql index 6319cf6a1908..6b723e1c1172 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql @@ -6,7 +6,7 @@ import experimental.quantum.OpenSSL.Operations.EVPSignatureOperation import experimental.quantum.OpenSSL.Operations.OpenSSLOperationBase from Crypto::SignatureOperationNode n -select n, n.asElement(), n.getAnInputArtifact(), n.getAnOutputArtifact(), n.getAKey(), - n.asElement().(OpenSSLOperation).getAlgorithmArg() - // n.asElement().(Crypto::OperationInstance).getAnAlgorithmValueConsumer(), - // n.getAnAlgorithmOrGenericSource() +select n, n.getAnInputArtifact(), n.getAnOutputArtifact(), n.getAKey(), + n.getAnAlgorithmOrGenericSource(), n.getKeyOperationSubtype() + // , n.getASignatureArtifact() + From 54a3e5cf0c58f6f5f114918d02d321797394d40c Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 30 May 2025 12:10:54 +0200 Subject: [PATCH 15/21] fix cipher tests after adding new stubs --- .../openssl/cipher_key_sources.expected | 4 +-- .../openssl/cipher_nonce_sources.expected | 4 +-- .../openssl/cipher_operations.expected | 32 +++++++++---------- .../openssl/cipher_plaintext_sources.expected | 2 +- .../openssl/hash_input_sources.expected | 4 +-- .../quantum/openssl/hash_operations.expected | 4 +-- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_key_sources.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_key_sources.expected index 749e7ee0fc61..3867d4956abf 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_key_sources.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_key_sources.expected @@ -1,2 +1,2 @@ -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:179:43:179:76 | Constant | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:179:43:179:76 | Constant | +| openssl_basic.c:49:13:49:31 | EncryptOperation | openssl_basic.c:40:49:40:51 | Key | openssl_basic.c:188:43:188:76 | Constant | +| openssl_basic.c:99:11:99:29 | DecryptOperation | openssl_basic.c:86:45:86:47 | Key | openssl_basic.c:188:43:188:76 | Constant | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_nonce_sources.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_nonce_sources.expected index 76cce2449439..b62256fc5f2f 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_nonce_sources.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_nonce_sources.expected @@ -1,2 +1,2 @@ -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:180:42:180:59 | Constant | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:180:42:180:59 | Constant | +| openssl_basic.c:49:13:49:31 | EncryptOperation | openssl_basic.c:40:54:40:55 | Nonce | openssl_basic.c:189:42:189:59 | Constant | +| openssl_basic.c:99:11:99:29 | DecryptOperation | openssl_basic.c:86:50:86:51 | Nonce | openssl_basic.c:189:42:189:59 | Constant | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected index 73b0af3ad5f4..54d888cf8d1c 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_operations.expected @@ -1,16 +1,16 @@ -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:35:36:35:45 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:35:36:35:45 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:35:36:35:45 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:35:36:35:45 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:23:62:23:65 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:23:68:23:71 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:40:38:40:53 | KeyOperationOutput | openssl_basic.c:31:49:31:51 | Key | openssl_basic.c:31:54:31:55 | Nonce | openssl_basic.c:23:37:23:51 | KeyOperationAlgorithm | Encrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:81:32:81:40 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:81:32:81:40 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:81:32:81:40 | KeyOperationOutput | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:81:32:81:40 | KeyOperationOutput | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:36:90:50 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:36:90:50 | KeyOperationOutput | openssl_basic.c:69:58:69:61 | Key | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:36:90:50 | KeyOperationOutput | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:69:64:69:67 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | -| openssl_basic.c:90:11:90:29 | DecryptOperation | openssl_basic.c:81:49:81:58 | Message | openssl_basic.c:90:36:90:50 | KeyOperationOutput | openssl_basic.c:77:45:77:47 | Key | openssl_basic.c:77:50:77:51 | Nonce | openssl_basic.c:69:33:69:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:49:13:49:31 | EncryptOperation | openssl_basic.c:44:54:44:62 | Message | openssl_basic.c:44:36:44:45 | KeyOperationOutput | openssl_basic.c:32:62:32:65 | Key | openssl_basic.c:32:68:32:71 | Nonce | openssl_basic.c:32:37:32:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:49:13:49:31 | EncryptOperation | openssl_basic.c:44:54:44:62 | Message | openssl_basic.c:44:36:44:45 | KeyOperationOutput | openssl_basic.c:32:62:32:65 | Key | openssl_basic.c:40:54:40:55 | Nonce | openssl_basic.c:32:37:32:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:49:13:49:31 | EncryptOperation | openssl_basic.c:44:54:44:62 | Message | openssl_basic.c:44:36:44:45 | KeyOperationOutput | openssl_basic.c:40:49:40:51 | Key | openssl_basic.c:32:68:32:71 | Nonce | openssl_basic.c:32:37:32:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:49:13:49:31 | EncryptOperation | openssl_basic.c:44:54:44:62 | Message | openssl_basic.c:44:36:44:45 | KeyOperationOutput | openssl_basic.c:40:49:40:51 | Key | openssl_basic.c:40:54:40:55 | Nonce | openssl_basic.c:32:37:32:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:49:13:49:31 | EncryptOperation | openssl_basic.c:44:54:44:62 | Message | openssl_basic.c:49:38:49:53 | KeyOperationOutput | openssl_basic.c:32:62:32:65 | Key | openssl_basic.c:32:68:32:71 | Nonce | openssl_basic.c:32:37:32:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:49:13:49:31 | EncryptOperation | openssl_basic.c:44:54:44:62 | Message | openssl_basic.c:49:38:49:53 | KeyOperationOutput | openssl_basic.c:32:62:32:65 | Key | openssl_basic.c:40:54:40:55 | Nonce | openssl_basic.c:32:37:32:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:49:13:49:31 | EncryptOperation | openssl_basic.c:44:54:44:62 | Message | openssl_basic.c:49:38:49:53 | KeyOperationOutput | openssl_basic.c:40:49:40:51 | Key | openssl_basic.c:32:68:32:71 | Nonce | openssl_basic.c:32:37:32:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:49:13:49:31 | EncryptOperation | openssl_basic.c:44:54:44:62 | Message | openssl_basic.c:49:38:49:53 | KeyOperationOutput | openssl_basic.c:40:49:40:51 | Key | openssl_basic.c:40:54:40:55 | Nonce | openssl_basic.c:32:37:32:51 | KeyOperationAlgorithm | Encrypt | +| openssl_basic.c:99:11:99:29 | DecryptOperation | openssl_basic.c:90:49:90:58 | Message | openssl_basic.c:90:32:90:40 | KeyOperationOutput | openssl_basic.c:78:58:78:61 | Key | openssl_basic.c:78:64:78:67 | Nonce | openssl_basic.c:78:33:78:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:99:11:99:29 | DecryptOperation | openssl_basic.c:90:49:90:58 | Message | openssl_basic.c:90:32:90:40 | KeyOperationOutput | openssl_basic.c:78:58:78:61 | Key | openssl_basic.c:86:50:86:51 | Nonce | openssl_basic.c:78:33:78:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:99:11:99:29 | DecryptOperation | openssl_basic.c:90:49:90:58 | Message | openssl_basic.c:90:32:90:40 | KeyOperationOutput | openssl_basic.c:86:45:86:47 | Key | openssl_basic.c:78:64:78:67 | Nonce | openssl_basic.c:78:33:78:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:99:11:99:29 | DecryptOperation | openssl_basic.c:90:49:90:58 | Message | openssl_basic.c:90:32:90:40 | KeyOperationOutput | openssl_basic.c:86:45:86:47 | Key | openssl_basic.c:86:50:86:51 | Nonce | openssl_basic.c:78:33:78:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:99:11:99:29 | DecryptOperation | openssl_basic.c:90:49:90:58 | Message | openssl_basic.c:99:36:99:50 | KeyOperationOutput | openssl_basic.c:78:58:78:61 | Key | openssl_basic.c:78:64:78:67 | Nonce | openssl_basic.c:78:33:78:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:99:11:99:29 | DecryptOperation | openssl_basic.c:90:49:90:58 | Message | openssl_basic.c:99:36:99:50 | KeyOperationOutput | openssl_basic.c:78:58:78:61 | Key | openssl_basic.c:86:50:86:51 | Nonce | openssl_basic.c:78:33:78:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:99:11:99:29 | DecryptOperation | openssl_basic.c:90:49:90:58 | Message | openssl_basic.c:99:36:99:50 | KeyOperationOutput | openssl_basic.c:86:45:86:47 | Key | openssl_basic.c:78:64:78:67 | Nonce | openssl_basic.c:78:33:78:47 | KeyOperationAlgorithm | Decrypt | +| openssl_basic.c:99:11:99:29 | DecryptOperation | openssl_basic.c:90:49:90:58 | Message | openssl_basic.c:99:36:99:50 | KeyOperationOutput | openssl_basic.c:86:45:86:47 | Key | openssl_basic.c:86:50:86:51 | Nonce | openssl_basic.c:78:33:78:47 | KeyOperationAlgorithm | Decrypt | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_plaintext_sources.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_plaintext_sources.expected index 1bea7895a349..ed105e5a9d09 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_plaintext_sources.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/cipher_plaintext_sources.expected @@ -1 +1 @@ -| openssl_basic.c:40:13:40:31 | EncryptOperation | openssl_basic.c:35:54:35:62 | Message | openssl_basic.c:181:49:181:87 | Constant | +| openssl_basic.c:49:13:49:31 | EncryptOperation | openssl_basic.c:44:54:44:62 | Message | openssl_basic.c:190:49:190:87 | Constant | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_input_sources.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_input_sources.expected index 79ba1387a1ec..a84b51cc06a0 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_input_sources.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_input_sources.expected @@ -1,2 +1,2 @@ -| openssl_basic.c:124:13:124:30 | HashOperation | openssl_basic.c:120:37:120:43 | Message | openssl_basic.c:181:49:181:87 | Constant | -| openssl_basic.c:144:13:144:22 | HashOperation | openssl_basic.c:144:24:144:30 | Message | openssl_basic.c:181:49:181:87 | Constant | +| openssl_basic.c:133:13:133:30 | HashOperation | openssl_basic.c:129:37:129:43 | Message | openssl_basic.c:190:49:190:87 | Constant | +| openssl_basic.c:153:13:153:22 | HashOperation | openssl_basic.c:153:24:153:30 | Message | openssl_basic.c:190:49:190:87 | Constant | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_operations.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_operations.expected index 247c4389bc1a..b88bc19cbe1e 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_operations.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/hash_operations.expected @@ -1,2 +1,2 @@ -| openssl_basic.c:124:13:124:30 | HashOperation | openssl_basic.c:124:39:124:44 | Digest | openssl_basic.c:116:38:116:47 | HashAlgorithm | openssl_basic.c:120:37:120:43 | Message | -| openssl_basic.c:144:13:144:22 | HashOperation | openssl_basic.c:144:46:144:51 | Digest | openssl_basic.c:144:67:144:73 | HashAlgorithm | openssl_basic.c:144:24:144:30 | Message | +| openssl_basic.c:133:13:133:30 | HashOperation | openssl_basic.c:133:39:133:44 | Digest | openssl_basic.c:125:38:125:47 | HashAlgorithm | openssl_basic.c:129:37:129:43 | Message | +| openssl_basic.c:153:13:153:22 | HashOperation | openssl_basic.c:153:46:153:51 | Digest | openssl_basic.c:153:67:153:73 | HashAlgorithm | openssl_basic.c:153:24:153:30 | Message | From 0178bf323bd0764f6bc65a8cb73198f1fd27baab Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 30 May 2025 14:41:53 +0200 Subject: [PATCH 16/21] more tests --- .../OpenSSLAlgorithmInstances.qll | 1 + .../OpenSSL/Operations/EVPKeyGenOperation.qll | 36 +++++++++---------- .../openssl/signature/keygen_key_sources.ql | 8 +++++ .../openssl/signature/keygen_operations.ql | 5 +++ .../openssl/signature/openssl_signature.c | 4 +-- .../signature/signature_key_sources.ql | 8 +++++ .../signature/signature_message_sources.ql | 8 +++++ .../openssl/signature/signature_operations.ql | 7 +--- 8 files changed, 51 insertions(+), 26 deletions(-) create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_key_sources.ql create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_operations.ql create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_key_sources.ql create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_message_sources.ql diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll index 55beb58588b3..f169ca28c0dc 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/OpenSSLAlgorithmInstances.qll @@ -4,3 +4,4 @@ import PaddingAlgorithmInstance import BlockAlgorithmInstance import HashAlgorithmInstance import EllipticCurveAlgorithmInstance +import SignatureAlgorithmInstance diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll index 594d61f7c3a4..33d40c39cda2 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll @@ -4,30 +4,30 @@ private import OpenSSLOperationBase private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers private import semmle.code.cpp.dataflow.new.DataFlow - class EVPKeyGenInitialize extends EVPInitialize { - EVPKeyGenInitialize() { this.(Call).getTarget().getName() in [ - "EVP_PKEY_keygen_init", "EVP_PKEY_paramgen_init" - ] - } - - override Expr getAlgorithmArg() { - result = getAlgorithmFromCtx(this.getContextArg()) - } + EVPKeyGenInitialize() { + this.(Call).getTarget().getName() in [ + "EVP_PKEY_keygen_init", + "EVP_PKEY_paramgen_init" + ] + } + + override Expr getAlgorithmArg() { result = getAlgorithmFromCtx(this.getContextArg()) } } class EVPKeyGenOperation extends EVPOperation, Crypto::KeyGenerationOperationInstance { - EVPKeyGenOperation() { this.(Call).getTarget().getName() in [ - "EVP_PKEY_generate", "EVP_PKEY_paramgen", "EVP_PKEY_keygen", "EVP_PKEY_Q_keygen" - ] - } + EVPKeyGenOperation() { + this.(Call).getTarget().getName() in [ + "EVP_PKEY_generate", "EVP_PKEY_keygen", "EVP_PKEY_Q_keygen", "EVP_PKEY_paramgen" + // TODO: "EVP_PKEY_paramgen" + ] + } override Expr getAlgorithmArg() { - if this.(Call).getTarget().getName() = "EVP_PKEY_Q_keygen" then - result = this.(Call).getArgument(0) - else - result = EVPOperation.super.getAlgorithmArg() -} + if this.(Call).getTarget().getName() = "EVP_PKEY_Q_keygen" + then result = this.(Call).getArgument(0) + else result = EVPOperation.super.getAlgorithmArg() + } override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() } diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_key_sources.ql b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_key_sources.ql new file mode 100644 index 000000000000..3c55105df54a --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_key_sources.ql @@ -0,0 +1,8 @@ +import cpp +import experimental.quantum.Language +import experimental.quantum.OpenSSL.Operations.EVPKeyGenOperation + +from EVPKeyGenOperation keyGen, Crypto::KeyArtifactNode key +where keyGen = key.asElement().(Crypto::KeyArtifactOutputInstance).getCreator() +select keyGen, key, key.getAKnownAlgorithm() +// TODO: add key.getSourceNode() when the test has any explicit key source (now we always generate new keys) diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_operations.ql b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_operations.ql new file mode 100644 index 000000000000..dc9e6cb1bd3c --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_operations.ql @@ -0,0 +1,5 @@ +import cpp +import experimental.quantum.Language + +from Crypto::KeyGenerationOperationNode n +select n, n.getOutputKeyArtifact(), n.getAnAlgorithmOrGenericSource() diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c index 890453b2da96..6f3848dd0e63 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/openssl_signature.c @@ -571,7 +571,7 @@ static EVP_PKEY* generate_dsa_key(void) { EVP_PKEY *params = NULL, *key = NULL; /* Generate parameters first */ - param_ctx = EVP_PKEY_CTX_new_from_name(NULL, "DSA", NULL); + param_ctx = EVP_PKEY_CTX_new_from_name(NULL, "dsa", NULL); if (!param_ctx) return NULL; if (EVP_PKEY_paramgen_init(param_ctx) <= 0 || @@ -764,7 +764,7 @@ int test_signature_apis_dsa(void) { } /* Test generic APIs */ - if (!test_signature_apis(key, md, no_parameter_setter, "DSA")) { + if (!test_signature_apis(key, md, no_parameter_setter, "dsa")) { success = 0; } diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_key_sources.ql b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_key_sources.ql new file mode 100644 index 000000000000..a0e9f903a148 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_key_sources.ql @@ -0,0 +1,8 @@ +import cpp +import experimental.quantum.Language + +from Crypto::SignatureOperationNode op, Crypto::KeyArtifactNode key +where op.getAKey() = key +select op, key +// TODO: should key.getAKnownAlgorithm() return a value? +// TODO: add key.getSourceNode() when the test has any explicit key source (now we always generate new keys) diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_message_sources.ql b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_message_sources.ql new file mode 100644 index 000000000000..ca22636ec923 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_message_sources.ql @@ -0,0 +1,8 @@ +import cpp +import experimental.quantum.Language + +from Crypto::SignatureOperationNode n, Crypto::MessageArtifactNode m +where n.getAnInputArtifact() = m +select n, m, m.getSourceNode() +// TODO: we miss call to EVP_PKEY_sign, because getSourceNode does not find the `digest` we sign +// TODO: we miss message generated with `EVP_SignUpdate(md_ctx, message+1, message_len-1)`, because getSourceNode does not find it diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql index 6b723e1c1172..a1ae27880e4a 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.ql @@ -1,12 +1,7 @@ import cpp import experimental.quantum.Language -import experimental.quantum.OpenSSL.AlgorithmValueConsumers.PKeyAlgorithmValueConsumer -import experimental.quantum.OpenSSL.CtxFlow -import experimental.quantum.OpenSSL.Operations.EVPSignatureOperation -import experimental.quantum.OpenSSL.Operations.OpenSSLOperationBase from Crypto::SignatureOperationNode n select n, n.getAnInputArtifact(), n.getAnOutputArtifact(), n.getAKey(), n.getAnAlgorithmOrGenericSource(), n.getKeyOperationSubtype() - // , n.getASignatureArtifact() - +// TODO: add n.getASignatureArtifact() for verification operations From e618097cb523a3a43d8876be377172128f1bfc22 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 30 May 2025 14:51:14 +0200 Subject: [PATCH 17/21] signature and keygen tests - expected --- .../openssl/signature/keygen_key_sources.expected | 6 ++++++ .../openssl/signature/keygen_operations.expected | 6 ++++++ .../openssl/signature/signature_algorithms.expected | 6 +++--- .../openssl/signature/signature_key_sources.expected | 5 +++++ .../signature/signature_message_sources.expected | 4 ++++ .../openssl/signature/signature_operations.expected | 12 ++++++------ 6 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_key_sources.expected create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_operations.expected create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_key_sources.expected create mode 100644 cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_message_sources.expected diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_key_sources.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_key_sources.expected new file mode 100644 index 000000000000..4775dced6a5e --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_key_sources.expected @@ -0,0 +1,6 @@ +| openssl_signature.c:557:9:557:23 | call to EVP_PKEY_keygen | openssl_signature.c:557:34:557:37 | Key | RSA | +| openssl_signature.c:557:9:557:23 | call to EVP_PKEY_keygen | openssl_signature.c:557:35:557:37 | Key | RSA | +| openssl_signature.c:579:9:579:25 | call to EVP_PKEY_paramgen | openssl_signature.c:579:38:579:44 | Key | DSA | +| openssl_signature.c:579:9:579:25 | call to EVP_PKEY_paramgen | openssl_signature.c:579:39:579:44 | Key | DSA | +| openssl_signature.c:587:9:587:23 | call to EVP_PKEY_keygen | openssl_signature.c:587:34:587:37 | Key | DSA | +| openssl_signature.c:587:9:587:23 | call to EVP_PKEY_keygen | openssl_signature.c:587:35:587:37 | Key | DSA | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_operations.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_operations.expected new file mode 100644 index 000000000000..97281dd4dfaa --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/keygen_operations.expected @@ -0,0 +1,6 @@ +| openssl_signature.c:557:9:557:23 | KeyGeneration | openssl_signature.c:557:34:557:37 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | +| openssl_signature.c:557:9:557:23 | KeyGeneration | openssl_signature.c:557:35:557:37 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | +| openssl_signature.c:579:9:579:25 | KeyGeneration | openssl_signature.c:579:38:579:44 | Key | openssl_signature.c:574:50:574:54 | KeyOperationAlgorithm | +| openssl_signature.c:579:9:579:25 | KeyGeneration | openssl_signature.c:579:39:579:44 | Key | openssl_signature.c:574:50:574:54 | KeyOperationAlgorithm | +| openssl_signature.c:587:9:587:23 | KeyGeneration | openssl_signature.c:587:34:587:37 | Key | openssl_signature.c:574:50:574:54 | KeyOperationAlgorithm | +| openssl_signature.c:587:9:587:23 | KeyGeneration | openssl_signature.c:587:35:587:37 | Key | openssl_signature.c:574:50:574:54 | KeyOperationAlgorithm | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected index 31928dc37041..f223547d74eb 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_algorithms.expected @@ -1,6 +1,6 @@ | openssl_signature.c:552:35:552:46 | 6 | RSA | openssl_signature.c:552:15:552:33 | call to EVP_PKEY_CTX_new_id | -| openssl_signature.c:574:50:574:54 | DSA | DSA | openssl_signature.c:574:17:574:42 | call to EVP_PKEY_CTX_new_from_name | +| openssl_signature.c:574:50:574:54 | dsa | DSA | openssl_signature.c:574:17:574:42 | call to EVP_PKEY_CTX_new_from_name | | openssl_signature.c:711:60:711:71 | RSA-SHA256 | RSA | openssl_signature.c:332:11:332:29 | call to EVP_SIGNATURE_fetch | | openssl_signature.c:711:60:711:71 | RSA-SHA256 | RSA | openssl_signature.c:368:11:368:29 | call to EVP_SIGNATURE_fetch | -| openssl_signature.c:767:60:767:64 | DSA | DSA | openssl_signature.c:332:11:332:29 | call to EVP_SIGNATURE_fetch | -| openssl_signature.c:767:60:767:64 | DSA | DSA | openssl_signature.c:368:11:368:29 | call to EVP_SIGNATURE_fetch | +| openssl_signature.c:767:60:767:64 | dsa | DSA | openssl_signature.c:332:11:332:29 | call to EVP_SIGNATURE_fetch | +| openssl_signature.c:767:60:767:64 | dsa | DSA | openssl_signature.c:368:11:368:29 | call to EVP_SIGNATURE_fetch | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_key_sources.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_key_sources.expected new file mode 100644 index 000000000000..8d3a6f5a5e05 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_key_sources.expected @@ -0,0 +1,5 @@ +| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:89:53:89:56 | Key | +| openssl_signature.c:151:9:151:27 | SignOperation | openssl_signature.c:142:52:142:55 | Key | +| openssl_signature.c:213:9:213:27 | SignOperation | openssl_signature.c:199:57:199:60 | Key | +| openssl_signature.c:279:9:279:21 | SignOperation | openssl_signature.c:269:39:269:42 | Key | +| openssl_signature.c:343:9:343:35 | SignOperation | openssl_signature.c:330:39:330:42 | Key | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_message_sources.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_message_sources.expected new file mode 100644 index 000000000000..1818d9d7a0c0 --- /dev/null +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_message_sources.expected @@ -0,0 +1,4 @@ +| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:79:32:79:38 | Message | openssl_signature.c:611:37:611:77 | Constant | +| openssl_signature.c:151:9:151:27 | SignOperation | openssl_signature.c:143:38:143:44 | Message | openssl_signature.c:611:37:611:77 | Constant | +| openssl_signature.c:213:9:213:27 | SignOperation | openssl_signature.c:205:38:205:44 | Message | openssl_signature.c:611:37:611:77 | Constant | +| openssl_signature.c:343:9:343:35 | SignOperation | openssl_signature.c:335:48:335:54 | Message | openssl_signature.c:611:37:611:77 | Constant | diff --git a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.expected b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.expected index 88d44cda217c..20a861584d6b 100644 --- a/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.expected +++ b/cpp/ql/test/experimental/library-tests/quantum/openssl/signature/signature_operations.expected @@ -1,12 +1,12 @@ | openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:79:32:79:38 | Message | openssl_signature.c:89:31:89:40 | SignatureOutput | openssl_signature.c:89:53:89:56 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | Sign | -| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:79:32:79:38 | Message | openssl_signature.c:89:31:89:40 | SignatureOutput | openssl_signature.c:89:53:89:56 | Key | openssl_signature.c:574:50:574:54 | Constant | Sign | +| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:79:32:79:38 | Message | openssl_signature.c:89:31:89:40 | SignatureOutput | openssl_signature.c:89:53:89:56 | Key | openssl_signature.c:574:50:574:54 | KeyOperationAlgorithm | Sign | | openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:84:28:84:36 | Message | openssl_signature.c:89:31:89:40 | SignatureOutput | openssl_signature.c:89:53:89:56 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | Sign | -| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:84:28:84:36 | Message | openssl_signature.c:89:31:89:40 | SignatureOutput | openssl_signature.c:89:53:89:56 | Key | openssl_signature.c:574:50:574:54 | Constant | Sign | +| openssl_signature.c:89:9:89:21 | SignOperation | openssl_signature.c:84:28:84:36 | Message | openssl_signature.c:89:31:89:40 | SignatureOutput | openssl_signature.c:89:53:89:56 | Key | openssl_signature.c:574:50:574:54 | KeyOperationAlgorithm | Sign | | openssl_signature.c:151:9:151:27 | SignOperation | openssl_signature.c:143:38:143:44 | Message | openssl_signature.c:151:37:151:46 | SignatureOutput | openssl_signature.c:142:52:142:55 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | Sign | -| openssl_signature.c:151:9:151:27 | SignOperation | openssl_signature.c:143:38:143:44 | Message | openssl_signature.c:151:37:151:46 | SignatureOutput | openssl_signature.c:142:52:142:55 | Key | openssl_signature.c:574:50:574:54 | Constant | Sign | +| openssl_signature.c:151:9:151:27 | SignOperation | openssl_signature.c:143:38:143:44 | Message | openssl_signature.c:151:37:151:46 | SignatureOutput | openssl_signature.c:142:52:142:55 | Key | openssl_signature.c:574:50:574:54 | KeyOperationAlgorithm | Sign | | openssl_signature.c:213:9:213:27 | SignOperation | openssl_signature.c:205:38:205:44 | Message | openssl_signature.c:213:37:213:46 | SignatureOutput | openssl_signature.c:199:57:199:60 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | Sign | -| openssl_signature.c:213:9:213:27 | SignOperation | openssl_signature.c:205:38:205:44 | Message | openssl_signature.c:213:37:213:46 | SignatureOutput | openssl_signature.c:199:57:199:60 | Key | openssl_signature.c:574:50:574:54 | Constant | Sign | +| openssl_signature.c:213:9:213:27 | SignOperation | openssl_signature.c:205:38:205:44 | Message | openssl_signature.c:213:37:213:46 | SignatureOutput | openssl_signature.c:199:57:199:60 | Key | openssl_signature.c:574:50:574:54 | KeyOperationAlgorithm | Sign | | openssl_signature.c:279:9:279:21 | SignOperation | openssl_signature.c:279:60:279:65 | Message | openssl_signature.c:279:33:279:42 | SignatureOutput | openssl_signature.c:269:39:269:42 | Key | openssl_signature.c:552:35:552:46 | KeyOperationAlgorithm | Sign | -| openssl_signature.c:279:9:279:21 | SignOperation | openssl_signature.c:279:60:279:65 | Message | openssl_signature.c:279:33:279:42 | SignatureOutput | openssl_signature.c:269:39:269:42 | Key | openssl_signature.c:574:50:574:54 | Constant | Sign | +| openssl_signature.c:279:9:279:21 | SignOperation | openssl_signature.c:279:60:279:65 | Message | openssl_signature.c:279:33:279:42 | SignatureOutput | openssl_signature.c:269:39:269:42 | Key | openssl_signature.c:574:50:574:54 | KeyOperationAlgorithm | Sign | | openssl_signature.c:343:9:343:35 | SignOperation | openssl_signature.c:335:48:335:54 | Message | openssl_signature.c:343:47:343:56 | SignatureOutput | openssl_signature.c:330:39:330:42 | Key | openssl_signature.c:711:60:711:71 | KeyOperationAlgorithm | Sign | -| openssl_signature.c:343:9:343:35 | SignOperation | openssl_signature.c:335:48:335:54 | Message | openssl_signature.c:343:47:343:56 | SignatureOutput | openssl_signature.c:330:39:330:42 | Key | openssl_signature.c:767:60:767:64 | Constant | Sign | +| openssl_signature.c:343:9:343:35 | SignOperation | openssl_signature.c:335:48:335:54 | Message | openssl_signature.c:343:47:343:56 | SignatureOutput | openssl_signature.c:330:39:330:42 | Key | openssl_signature.c:767:60:767:64 | KeyOperationAlgorithm | Sign | From c6b21654041a773655a4414789e4be211c2902f7 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 30 May 2025 14:51:35 +0200 Subject: [PATCH 18/21] change model.qll - KeyArtifactNode getAKnownAlgorithm fix --- shared/quantum/codeql/quantum/experimental/Model.qll | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/shared/quantum/codeql/quantum/experimental/Model.qll b/shared/quantum/codeql/quantum/experimental/Model.qll index c8b52080ca96..80f4b102f256 100644 --- a/shared/quantum/codeql/quantum/experimental/Model.qll +++ b/shared/quantum/codeql/quantum/experimental/Model.qll @@ -1655,14 +1655,19 @@ module CryptographyBase Input> { result = this.getAKnownAlgorithm() or result = instance - .(KeyCreationOperationInstance) + .(KeyArtifactOutputInstance) + .getCreator() .getAnAlgorithmValueConsumer() .getAGenericSourceNode() } KeyCreationCandidateAlgorithmNode getAKnownAlgorithm() { result = - instance.(KeyCreationOperationInstance).getAnAlgorithmValueConsumer().getAKnownSourceNode() + instance + .(KeyArtifactOutputInstance) + .getCreator() + .getAnAlgorithmValueConsumer() + .getAKnownSourceNode() } override NodeBase getChild(string edgeName) { From a70cd60b37551552c8eac7a3f69a21b8e79c08ea Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 30 May 2025 17:31:46 +0200 Subject: [PATCH 19/21] key sizes basic support --- .../KnownAlgorithmConstants.qll | 7 ++- .../SignatureAlgorithmInstance.qll | 16 ++++-- .../OpenSSL/Operations/EVPKeyGenOperation.qll | 50 ++++++++++++++++--- .../Operations/OpenSSLOperationBase.qll | 2 +- 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll index 29438d2ef996..67ce3e3c78c9 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll @@ -82,7 +82,12 @@ class KnownOpenSSLSignatureAlgorithmConstant extends KnownOpenSSLAlgorithmConsta predicate resolveAlgorithmFromCall(Call c, string normalized, string algType) { exists(string name, string parsedTargetName | parsedTargetName = - c.getTarget().getName().replaceAll("EVP_", "").toLowerCase().replaceAll("_", "-") and + c.getTarget() + .getName() + .replaceAll("EVP_", "") + .replaceAll("_gen", "") + .toLowerCase() + .replaceAll("_", "-") and name = resolveAlgorithmAlias(parsedTargetName) and knownOpenSSLAlgorithmLiteral(name, _, normalized, algType) ) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/SignatureAlgorithmInstance.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/SignatureAlgorithmInstance.qll index cb46a6ed72f0..fc6e4d90709f 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/SignatureAlgorithmInstance.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/SignatureAlgorithmInstance.qll @@ -69,7 +69,8 @@ class KnownOpenSSLSignatureConstantAlgorithmInstance extends OpenSSLAlgorithmIns override string getRawAlgorithmName() { result = this.(Literal).getValue().toString() } override int getKeySizeFixed() { - // this.(KnownOpenSSLSignatureAlgorithmConstant).getExplicitKeySize() = result + // TODO: use ellipticCurveNameToKeySizeAndFamilyMapping or KnownOpenSSLEllipticCurveConstantAlgorithmInstance + // TODO: maybe add getExplicitKeySize to KnownOpenSSLSignatureAlgorithmConstant and use it here none() } @@ -83,11 +84,20 @@ class KnownOpenSSLSignatureConstantAlgorithmInstance extends OpenSSLAlgorithmIns override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { - // TODO: trace to any key size initializer, symmetric and asymmetric + // TODO: trace to any key size initializer + // probably PKeyAlgorithmValueConsumer and SignatureAlgorithmValueConsumer none() } + /** + * No mode for signatures. + */ override predicate shouldHaveModeOfOperation() { none() } - override predicate shouldHavePaddingScheme() { none() } + /** + * Padding only for RSA. + */ + override predicate shouldHavePaddingScheme() { + this.getAlgorithmType() instanceof KeyOpAlg::TAsymmetricCipher + } } diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll index 33d40c39cda2..c8dca0731008 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPKeyGenOperation.qll @@ -15,18 +15,51 @@ class EVPKeyGenInitialize extends EVPInitialize { override Expr getAlgorithmArg() { result = getAlgorithmFromCtx(this.getContextArg()) } } -class EVPKeyGenOperation extends EVPOperation, Crypto::KeyGenerationOperationInstance { +/** + * All calls that can be tracked via ctx. + * For example calls used to set parameters like a key size. + */ +class EVPKeyGenUpdate extends EVPUpdate { + EVPKeyGenUpdate() { + this.(Call).getTarget().getName() in [ + "EVP_PKEY_CTX_set_rsa_keygen_bits", + // TODO: "EVP_PKEY_CTX_set_params" + ] + } + + /** + * No input in our meaning. + */ + override Expr getInputArg() { none() } + + /** + * No output in our meaning. + */ + override Expr getOutputArg() { none() } + + Expr getKeySizeArg() { + this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_rsa_keygen_bits" and + result = this.(Call).getArgument(1) + } +} + +class EVPKeyGenOperation extends EVPFinal, Crypto::KeyGenerationOperationInstance { EVPKeyGenOperation() { this.(Call).getTarget().getName() in [ - "EVP_PKEY_generate", "EVP_PKEY_keygen", "EVP_PKEY_Q_keygen", "EVP_PKEY_paramgen" - // TODO: "EVP_PKEY_paramgen" + "EVP_PKEY_generate", "EVP_PKEY_keygen", "EVP_PKEY_Q_keygen", "EVP_PKEY_paramgen", + "EVP_RSA_gen" + // TODO: "EVP_PKEY_paramgen" may need special handling + // TODO: RSA_generate_key, RSA_generate_key_ex, etc ] } override Expr getAlgorithmArg() { if this.(Call).getTarget().getName() = "EVP_PKEY_Q_keygen" then result = this.(Call).getArgument(0) - else result = EVPOperation.super.getAlgorithmArg() + else + if this.(Call).getTarget().getName() = "EVP_RSA_gen" + then result = this + else result = EVPFinal.super.getAlgorithmArg() } override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() } @@ -36,11 +69,16 @@ class EVPKeyGenOperation extends EVPOperation, Crypto::KeyGenerationOperationIns override Expr getOutputArg() { result = this.(Call).getArgument(1) } override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { - result = EVPOperation.super.getOutputKeyArtifact() + result = EVPFinal.super.getOutputKeyArtifact() } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { - none() // TODO + if this.(Call).getTarget().getName() = "EVP_PKEY_Q_keygen" + then result = DataFlow::exprNode(this.(Call).getArgument(3)) // TODO: may be wrong for EC keys + else + if this.(Call).getTarget().getName() = "EVP_RSA_gen" + then result = DataFlow::exprNode(this.(Call).getArgument(0)) + else result = DataFlow::exprNode(this.getUpdateCalls().(EVPKeyGenUpdate).getKeySizeArg()) } override int getKeySizeFixed() { diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll index 85cde41671e8..eac06fd06339 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/Operations/OpenSSLOperationBase.qll @@ -20,7 +20,7 @@ abstract class OpenSSLOperation extends Crypto::OperationInstance instanceof Ope /** * Algorithm is either an argument and we track it to AlgorithmValueConsumer - * or we have the AlgorithmValueConsumer already and just return it. + * or we have the AlgorithmValueConsumer already tracked down and just return it. */ override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(), From 9b87f1f406aae1a140ac109a2c46ae1ec850da0b Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Fri, 30 May 2025 17:42:11 +0200 Subject: [PATCH 20/21] rm redundant predicate --- .../AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll index 458cae68139c..4ba5d0dd85de 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll @@ -62,8 +62,7 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer { Expr getValueArgExpr() { result = valueArgNode.asExpr() } } -// TODO: not sure where to put these predicates -Expr getAlgorithmFromArgument(Expr arg) { none() } +// TODO: not sure where to put all these predicates below /** * Given context expression (EVP_PKEY_CTX), finds the algorithm. From 30bc605f9ef912862203f4194ebef6eac26c8b86 Mon Sep 17 00:00:00 2001 From: GrosQuildu Date: Wed, 4 Jun 2025 12:21:45 +0200 Subject: [PATCH 21/21] fix formatting --- .../AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll index 4ba5d0dd85de..66f928a41e14 100644 --- a/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll +++ b/cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll @@ -63,7 +63,6 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer { } // TODO: not sure where to put all these predicates below - /** * Given context expression (EVP_PKEY_CTX), finds the algorithm. */