mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
Added modeling infrastructure
This commit is contained in:
committed by
Josh Brown
parent
2d44724acd
commit
7256faa7eb
5
cpp/ql/lib/experimental/crypto/Concepts.qll
Normal file
5
cpp/ql/lib/experimental/crypto/Concepts.qll
Normal file
@@ -0,0 +1,5 @@
|
||||
import experimental.crypto.CryptoArtifact
|
||||
import experimental.crypto.CryptoAlgorithmNames
|
||||
|
||||
import experimental.crypto.modules.OpenSSL as OpenSSL
|
||||
|
||||
222
cpp/ql/lib/experimental/crypto/CryptoAlgorithmNames.qll
Normal file
222
cpp/ql/lib/experimental/crypto/CryptoAlgorithmNames.qll
Normal file
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* Names of known cryptographic algorithms.
|
||||
* The names are standardized into upper-case, no spaces, dashes or underscores.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a string to represent generally unknown algorithms.
|
||||
* Predicate is to be used to get a consistent string representation
|
||||
* for unknown algorithms.
|
||||
*/
|
||||
string unknownAlgorithm() { result = "UNKNOWN" }
|
||||
|
||||
string getHashType() { result = "HASH" }
|
||||
string getSymmetricEncryptionType() { result = "SYMMETRIC_ENCRYPTION" }
|
||||
string getAsymmetricEncryptionType() { result = "ASYMMETRIC_ENCRYPTION" }
|
||||
string getKeyDerivationType() { result = "KEY_DERIVATION" }
|
||||
string getCipherBlockModeType() { result = "BLOCK_MODE" }
|
||||
string getSymmetricPaddingType() { result = "SYMMETRIC_PADDING" }
|
||||
string getAsymmetricPaddingType() { result = "ASYMMETRIC_PADDING" }
|
||||
string getEllipticCurveType() { result = "ELLIPTIC_CURVE" }
|
||||
string getSignatureType() { result = "SIGNATURE" }
|
||||
string getKeyExchangeType() { result = "KEY_EXCHANGE" }
|
||||
|
||||
string getAsymmetricType(){
|
||||
result in [getAsymmetricEncryptionType(), getSignatureType(), getKeyExchangeType(), getEllipticCurveType()]
|
||||
}
|
||||
|
||||
predicate isKnownType(string algType){
|
||||
algType in [
|
||||
getHashType(), getSymmetricEncryptionType(), getAsymmetricEncryptionType(), getKeyDerivationType(),
|
||||
getCipherBlockModeType(), getSymmetricPaddingType(), getAsymmetricPaddingType(), getEllipticCurveType(),
|
||||
getSignatureType(), getKeyExchangeType()
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
predicate isKnownAlgorithm(string name) { isKnownAlgorithm(name, _) }
|
||||
|
||||
predicate isKnownAlgorithm(string name, string algType) {
|
||||
isHashingAlgorithm(name) and algType = "HASH"
|
||||
or
|
||||
isEncryptionAlgorithm(name, algType) and algType in ["SYMMETRIC_ENCRYPTION", "ASYMMETRIC_ENCRYPTION"]
|
||||
or
|
||||
isKeyDerivationAlgorithm(name) and algType = "KEY_DERIVATION"
|
||||
or
|
||||
isCipherBlockModeAlgorithm(name) and algType = "BLOCK_MODE"
|
||||
or
|
||||
isPaddingAlgorithm(name, algType) and algType in ["SYMMETRIC_PADDING", "ASYMMETRIC_PADDING"]
|
||||
or
|
||||
isEllipticCurveAlgorithm(name) and algType = "ELLIPTIC_CURVE"
|
||||
or
|
||||
isSignatureAlgorithm(name) and algType = "SIGNATURE"
|
||||
or
|
||||
isKeyExchangeAlgorithm(name) and algType = "KEY_EXCHANGE"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is a known hashing algorithm in the model/library.
|
||||
*/
|
||||
predicate isHashingAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"BLAKE2", "BLAKE2B", "BLAKE2S",
|
||||
"SHA2", "SHA224", "SHA256", "SHA384", "SHA512", "SHA512224", "SHA512256",
|
||||
"SHA3", "SHA3224", "SHA3256", "SHA3384", "SHA3512", "SHAKE128", "SHAKE256", "SM3",
|
||||
"WHIRLPOOL", "POLY1305", "HAVEL128", "MD2", "MD4", "MD5", "PANAMA", "RIPEMD", "RIPEMD128",
|
||||
"RIPEMD256", "RIPEMD160", "RIPEMD320", "SHA0", "SHA1", "SHA", "MGF1","MGF1SHA1", "MDC2", "SIPHASH"
|
||||
]
|
||||
}
|
||||
|
||||
predicate isEncryptionAlgorithm(string name, string algType) {
|
||||
isAsymmetricEncryptionAlgorithm(name) and algType = "ASYMMETRIC_ENCRYPTION"
|
||||
or
|
||||
isSymmetricEncryptionAlgorithm(name) and algType = "SYMMETRIC_ENCRYPTION"
|
||||
}
|
||||
|
||||
predicate isEncryptionAlgorithm(string name) { isEncryptionAlgorithm(name, _) }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known symmetric encryption algorithm.
|
||||
*/
|
||||
predicate isSymmetricEncryptionAlgorithm(string name) {
|
||||
// NOTE: AES is meant to caputure all possible key lengths
|
||||
name =
|
||||
[
|
||||
"AES", "AES128", "AES192", "AES256", "ARIA", "BLOWFISH", "BF", "ECIES", "CAST", "CAST5",
|
||||
"CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "CHACHA", "CHACHA20",
|
||||
"CHACHA20POLY1305", "GOST", "GOSTR34102001", "GOSTR341094", "GOSTR341194", "GOST2814789",
|
||||
"GOSTR341194", "GOST2814789", "GOST28147", "GOSTR341094", "GOST89", "GOST94", "GOST34102012",
|
||||
"GOST34112012", "IDEA", "RABBIT",
|
||||
"SEED", "SM4", "DES", "DESX", "3DES", "TDES", "2DES", "DES3", "TRIPLEDES", "TDEA", "TRIPLEDEA",
|
||||
"ARC2", "RC2", "ARC4", "RC4", "ARCFOUR", "ARC5", "RC5", "MAGMA", "KUZNYECHIK"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known key derivation algorithm.
|
||||
*/
|
||||
predicate isKeyDerivationAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"ARGON2", "CONCATKDF", "CONCATKDFHASH", "CONCATKDFHMAC", "KBKDFCMAC", "BCRYPT", "HKDF",
|
||||
"HKDFEXPAND", "KBKDF", "KBKDFHMAC", "PBKDF1", "PBKDF2", "PBKDF2HMAC", "PKCS5", "SCRYPT",
|
||||
"X963KDF", "EVPKDF"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known cipher block mode
|
||||
*/
|
||||
predicate isCipherBlockModeAlgorithm(string name) {
|
||||
name = ["CBC", "GCM", "CCM", "CFB", "OFB", "CFB8", "CTR", "OPENPGP", "XTS", "EAX", "SIV", "ECB"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known padding algorithm
|
||||
*/
|
||||
predicate isPaddingAlgorithm(string name, string algType) {
|
||||
isSymmetricPaddingAlgorithm(name) and algType = "SYMMETRIC_PADDING"
|
||||
or
|
||||
isAsymmetricPaddingAlgorithm(name) and algType = "ASYMMETRIC_PADDING"
|
||||
}
|
||||
|
||||
/**
|
||||
* holds if `name` corresponds to a known symmetric padding algorithm
|
||||
*/
|
||||
predicate isSymmetricPaddingAlgorithm(string name) { name = ["PKCS7", "ANSIX923"] }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known asymmetric padding algorithm
|
||||
*/
|
||||
predicate isAsymmetricPaddingAlgorithm(string name) { name = ["OAEP", "PKCS1V15", "PSS", "KEM"] }
|
||||
|
||||
predicate isBrainpoolCurve(string curveName, int keySize) {
|
||||
// ALL BRAINPOOL CURVES
|
||||
keySize in [160, 192, 224, 256, 320, 384, 512] and
|
||||
(
|
||||
curveName = "BRAINPOOLP" + keySize.toString() + "R1"
|
||||
or
|
||||
curveName = "BRAINPOOLP" + keySize.toString() + "T1"
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSecCurve(string curveName, int keySize) {
|
||||
// ALL SEC CURVES
|
||||
keySize in [112, 113, 128, 131, 160, 163, 192, 193, 224, 233, 239, 256, 283, 384, 409, 521, 571] and
|
||||
exists(string suff | suff in ["R1", "R2", "K1"] |
|
||||
curveName = "SECT" + keySize.toString() + suff or
|
||||
curveName = "SECP" + keySize.toString() + suff
|
||||
)
|
||||
}
|
||||
|
||||
predicate isC2Curve(string curveName, int keySize) {
|
||||
// ALL C2 CURVES
|
||||
keySize in [163, 176, 191, 208, 239, 272, 304, 359, 368, 431] and
|
||||
exists(string pre, string suff |
|
||||
pre in ["PNB", "ONB", "TNB"] and suff in ["V1", "V2", "V3", "V4", "V5", "W1", "R1"]
|
||||
|
|
||||
curveName = "C2" + pre + keySize.toString() + suff
|
||||
)
|
||||
}
|
||||
|
||||
predicate isPrimeCurve(string curveName, int keySize) {
|
||||
// ALL PRIME CURVES
|
||||
keySize in [192, 239, 256] and
|
||||
exists(string suff | suff in ["V1", "V2", "V3"] | curveName = "PRIME" + keySize.toString() + suff)
|
||||
}
|
||||
|
||||
predicate isEllipticCurveAlgorithm(string curveName) { isEllipticCurveAlgorithm(curveName, _) }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known elliptic curve.
|
||||
*/
|
||||
predicate isEllipticCurveAlgorithm(string curveName, int keySize) {
|
||||
isSecCurve(curveName, keySize)
|
||||
or
|
||||
isBrainpoolCurve(curveName, keySize)
|
||||
or
|
||||
isC2Curve(curveName, keySize)
|
||||
or
|
||||
isPrimeCurve(curveName, keySize)
|
||||
or
|
||||
curveName = "ES256" and keySize = 256
|
||||
or
|
||||
curveName = "CURVE25519" and keySize = 255
|
||||
or
|
||||
curveName = "X25519" and keySize = 255
|
||||
or
|
||||
curveName = "ED25519" and keySize = 255
|
||||
or
|
||||
curveName = "CURVE448" and keySize = 448 // TODO: need to check the key size
|
||||
or
|
||||
curveName = "ED448" and keySize = 448
|
||||
or
|
||||
curveName = "X448" and keySize = 448
|
||||
or
|
||||
curveName = "NUMSP256T1" and keySize = 256
|
||||
or
|
||||
curveName = "NUMSP384T1" and keySize = 384
|
||||
or
|
||||
curveName = "NUMSP512T1" and keySize = 512
|
||||
or
|
||||
curveName = "SM2" and keySize in [256, 512]
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known signature algorithm.
|
||||
*/
|
||||
predicate isSignatureAlgorithm(string name) {
|
||||
name = ["DSA", "ECDSA", "EDDSA", "ES256", "ES256K", "ES384", "ES512", "ED25519", "ED448", "ECDSA256", "ECDSA384", "ECDSA512"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is a key exchange algorithm.
|
||||
*/
|
||||
predicate isKeyExchangeAlgorithm(string name) { name = ["ECDH", "DH", "DIFFIEHELLMAN", "X25519", "X448"] }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known asymmetric encryption.
|
||||
*/
|
||||
predicate isAsymmetricEncryptionAlgorithm(string name) { name = ["RSA"] }
|
||||
384
cpp/ql/lib/experimental/crypto/CryptoArtifact.qll
Normal file
384
cpp/ql/lib/experimental/crypto/CryptoArtifact.qll
Normal file
@@ -0,0 +1,384 @@
|
||||
import cpp
|
||||
private import experimental.semmle.code.cpp.crypto.CryptoAlgorithmNames
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* A cryptographic artifact is a DataFlow::Node associated with some
|
||||
* operation, algorithm, or any other aspect of cryptography.
|
||||
*/
|
||||
abstract class CryptographicArtifact extends Expr {}
|
||||
|
||||
// /**
|
||||
// * Associates a symmetric encryption algorithm with a block mode.
|
||||
// * The DataFlow::Node representing this association should be the
|
||||
// * point where the algorithm and block mode are combined.
|
||||
// * This may be at the call to encryption or in the construction
|
||||
// * of an object prior to encryption.
|
||||
// */
|
||||
// abstract class SymmetricCipher extends CryptographicArtifact{
|
||||
// abstract SymmetricEncryptionAlgorithm getEncryptionAlgorithm();
|
||||
// abstract BlockMode getBlockMode();
|
||||
// final predicate hasBlockMode(){
|
||||
// exists(this.getBlockMode())
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * A cryptographic operation is a method call that invokes a cryptographic
|
||||
// * algorithm (encrypt/decrypt) or a function in support of a cryptographic algorithm
|
||||
// * (key generation).
|
||||
// *
|
||||
// * Since operations are related to or in support of algorithms, operations must
|
||||
// * provide a reference to their associated algorithm. Often operataions themselves
|
||||
// * encapsulate algorithms, so operations can also extend CryptographicAlgorithm
|
||||
// * and refer to themselves as the target algorithm.
|
||||
// */
|
||||
// abstract class CryptographicOperation extends CryptographicArtifact, Call{
|
||||
|
||||
// // bindingset[paramName, ind]
|
||||
// // final DataFlow::Node getParameterSource(int ind, string paramName){
|
||||
// // result = Utils::getUltimateSrcFromApiNode(this.(API::CallNode).getParameter(ind, paramName))
|
||||
// // }
|
||||
|
||||
// final string getAlgorithmName(){
|
||||
// if exists(this.getAlgorithm().getName())
|
||||
// then result = this.getAlgorithm().getName()
|
||||
// else result = unknownAlgorithm()
|
||||
// }
|
||||
|
||||
// final predicate hasAlgorithm(){
|
||||
// exists(this.getAlgorithm())
|
||||
// }
|
||||
|
||||
// final predicate isUnknownAlgorithm(){
|
||||
// this.getAlgorithmName() = unknownAlgorithm()
|
||||
// or
|
||||
// not this.hasAlgorithm()
|
||||
// }
|
||||
|
||||
// // TODO: this might have to be parameterized by a configuration source for
|
||||
// // situations where an operation is passed an algorithm
|
||||
// abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
// }
|
||||
|
||||
// /** A key generation operation for asymmetric keys */
|
||||
// abstract class KeyGen extends CryptographicOperation{
|
||||
|
||||
|
||||
// int getAKeySizeInBits(){
|
||||
// result = getKeySizeInBits(_)
|
||||
// }
|
||||
|
||||
// final predicate hasKeySize(Expr configSrc){
|
||||
// exists(this.getKeySizeInBits(configSrc))
|
||||
// }
|
||||
|
||||
// final predicate hasKeySize(){
|
||||
// exists(this.getAKeySizeInBits())
|
||||
// }
|
||||
|
||||
// abstract Expr getKeyConfigSrc();
|
||||
// abstract int getKeySizeInBits(Expr configSrc);
|
||||
|
||||
// }
|
||||
|
||||
|
||||
abstract class CryptographicOperation extends CryptographicArtifact, Call{}
|
||||
|
||||
abstract class KeyGeneration extends CryptographicOperation {
|
||||
|
||||
// TODO: what if the algorithm is UNKNOWN?
|
||||
|
||||
abstract Expr getKeyConfigurationSource(CryptographicAlgorithm alg);
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
int getKeySizeInBits(CryptographicAlgorithm alg){
|
||||
result = getKeyConfigurationSource(alg).(Literal).getValue().toInt()
|
||||
}
|
||||
|
||||
predicate hasConstantKeySize(CryptographicAlgorithm alg){
|
||||
exists(this.getKeySizeInBits(alg))
|
||||
}
|
||||
|
||||
predicate hasKeyConfigurationSource(CryptographicAlgorithm alg){
|
||||
exists(this.getKeyConfigurationSource(alg))
|
||||
}
|
||||
|
||||
Expr getAKeyConfigurationSource(){
|
||||
result = getKeyConfigurationSource(_)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AsymmetricKeyGeneration extends KeyGeneration{}
|
||||
abstract class SymmetricKeyGeneration extends KeyGeneration{}
|
||||
|
||||
|
||||
/**
|
||||
* A cryptographic algorithm is a `CryptographicArtifact`
|
||||
* representing a cryptographic algorithm (see `CryptoAlgorithmNames.qll`).
|
||||
* Cryptographic algorithms can be functions referencing common crypto algorithms (e.g., hashlib.md5)
|
||||
* or strings that are used in cryptographic operation configurations (e.g., hashlib.new("md5")).
|
||||
* Cryptogrpahic algorithms may also be operations that wrap or abstract one or
|
||||
* more algorithms (e.g., cyrptography.fernet.Fernet and AES, CBC and PKCS7).
|
||||
*
|
||||
* In principle, this class should model the location where an algorithm enters the program, not
|
||||
* necessarily where it is used.
|
||||
*/
|
||||
abstract class CryptographicAlgorithm extends CryptographicArtifact
|
||||
{
|
||||
abstract string getName();
|
||||
abstract string getAlgType();
|
||||
|
||||
// string getAlgType(){
|
||||
// if this instanceof HashAlgorithm then result = getHashType()
|
||||
// else if this instanceof KeyDerivationAlgorithm then result = getKeyDerivationType()
|
||||
// else if this instanceof SymmetricEncryptionAlgorithm then result = getSymmetricEncryptionType()
|
||||
// else if this instanceof AsymmetricEncryptionAlgorithm then result = getAsymmetricEncryptionType()
|
||||
// else if this instanceof SymmetricEncryptionAlgorithm then result = getSymmetricPaddingType()
|
||||
// else if this instanceof AsymmetricEncryptionAlgorithm then result = getAsymmetricPaddingType()
|
||||
// else if this instanceof EllipticCurveAlgorithm then result = getEllipticCurveType()
|
||||
// else if this instanceof BlockMode then result = getCipherBlockModeType()
|
||||
// else if this instanceof KeyExchangeAlgorithm then result = getKeyExchangeType()
|
||||
// else if this instanceof SigningAlgorithm then result = getSignatureType()
|
||||
// else result = unknownAlgorithm()
|
||||
// }
|
||||
|
||||
// TODO: handle case where name isn't known, not just unknown?
|
||||
|
||||
/**
|
||||
* Normalizes a raw name into a normalized name as found in `CryptoAlgorithmNames.qll`.
|
||||
* Subclassess should override for more api-specific normalization.
|
||||
* By deafult, converts a raw name to upper-case with no hyphen, underscore, hash, or space.
|
||||
*/
|
||||
bindingset[s]
|
||||
string normalizeName(string s)
|
||||
{
|
||||
exists(string normStr |
|
||||
normStr = s.toUpperCase().regexpReplaceAll("[-_ ]|/", "")
|
||||
|
|
||||
(result = normStr and isKnownAlgorithm(result))
|
||||
or
|
||||
(result = unknownAlgorithm() and not isKnownAlgorithm(normStr))
|
||||
)
|
||||
}
|
||||
|
||||
abstract Expr configurationSink();
|
||||
|
||||
predicate hasConfigurationSink(){
|
||||
exists(this.configurationSink())
|
||||
}
|
||||
}
|
||||
|
||||
abstract class HashAlgorithm extends CryptographicAlgorithm{
|
||||
final string getHashName(){
|
||||
if exists(string n | n = this.getName() and isHashingAlgorithm(n))
|
||||
then (isHashingAlgorithm(result) and result = this.getName())
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType(){
|
||||
result = getHashType()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract class KeyDerivationAlgorithm extends CryptographicAlgorithm{
|
||||
final string getKDFName(){
|
||||
if exists(string n | n = this.getName() and isKeyDerivationAlgorithm(n))
|
||||
then (isKeyDerivationAlgorithm(result) and result = this.getName())
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType(){
|
||||
result = getKeyDerivationType()
|
||||
}
|
||||
}
|
||||
|
||||
// abstract class KeyDerivationOperation extends CryptographicOperation{
|
||||
// DataFlow::Node getIterationSizeSrc(){
|
||||
// none()
|
||||
// }
|
||||
|
||||
// DataFlow::Node getSaltConfigSrc(){
|
||||
// none()
|
||||
// }
|
||||
|
||||
// DataFlow::Node getHashConfigSrc(){
|
||||
// none()
|
||||
// }
|
||||
|
||||
// // TODO: get encryption algorithm for CBC-based KDF?
|
||||
|
||||
// DataFlow::Node getDerivedKeySizeSrc(){
|
||||
// none()
|
||||
// }
|
||||
|
||||
// DataFlow::Node getModeSrc(){
|
||||
// none()
|
||||
// }
|
||||
|
||||
// // TODO: add more to cover all the parameters of most KDF operations? Perhaps subclass for each type?
|
||||
|
||||
// abstract predicate requiresIteration();
|
||||
// abstract predicate requiresSalt();
|
||||
// abstract predicate requiresHash();
|
||||
// //abstract predicate requiresKeySize(); // Going to assume all requires a size
|
||||
// abstract predicate requiresMode();
|
||||
|
||||
// }
|
||||
|
||||
|
||||
abstract class EncryptionAlgorithm extends CryptographicAlgorithm
|
||||
{
|
||||
final predicate isAsymmetric() { this instanceof AsymmetricEncryptionAlgorithm }
|
||||
final predicate isSymmetric() { not this.isAsymmetric() }
|
||||
|
||||
// NOTE: DO_NOT add getEncryptionName here, we rely on the fact the parent
|
||||
// class does not have this common predicate.
|
||||
}
|
||||
|
||||
/**
|
||||
* A parent class to represent any algorithm for which
|
||||
* asymmetric cryptography is involved.
|
||||
* Intended to be distinct from AsymmetricEncryptionAlgorithm
|
||||
* which is intended only for asymmetric algorithms that specifically encrypt.
|
||||
*/
|
||||
abstract class AsymmetricAlgorithm extends CryptographicAlgorithm{}
|
||||
|
||||
/**
|
||||
* Algorithms directly or indirectly related to asymmetric encryption,
|
||||
* e.g., RSA, DSA, but also RSA padding algorithms
|
||||
*/
|
||||
abstract class AsymmetricEncryptionAlgorithm extends AsymmetricAlgorithm, EncryptionAlgorithm{
|
||||
final string getEncryptionName(){
|
||||
if exists(string n | n = this.getName() and isAsymmetricEncryptionAlgorithm(n))
|
||||
then (isAsymmetricEncryptionAlgorithm(result) and result = this.getName())
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType(){
|
||||
result = getAsymmetricEncryptionType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithms directly or indirectly related to symmetric encryption,
|
||||
* e.g., AES, DES, but also block modes and padding
|
||||
*/
|
||||
abstract class SymmetricEncryptionAlgorithm extends EncryptionAlgorithm
|
||||
{
|
||||
final string getEncryptionName(){
|
||||
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
|
||||
then (isSymmetricEncryptionAlgorithm(result) and result = this.getName())
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
// TODO: add a stream cipher predicate?
|
||||
|
||||
override string getAlgType(){
|
||||
result = getSymmetricEncryptionType()
|
||||
}
|
||||
}
|
||||
|
||||
// Used only to categorize all padding into a single object,
|
||||
// DO_NOT add predicates here. Only for categorization purposes.
|
||||
abstract class PaddingAlgorithm extends CryptographicAlgorithm{}
|
||||
|
||||
abstract class SymmetricPadding extends PaddingAlgorithm {
|
||||
final string getPaddingName(){
|
||||
if exists(string n | n = this.getName() and isSymmetricPaddingAlgorithm(n))
|
||||
then (isSymmetricPaddingAlgorithm(result) and result = this.getName())
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType(){
|
||||
result = getSymmetricPaddingType()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AsymmetricPadding extends PaddingAlgorithm {
|
||||
final string getPaddingName(){
|
||||
if exists(string n | n = this.getName() and isAsymmetricPaddingAlgorithm(n))
|
||||
then (isAsymmetricPaddingAlgorithm(result) and result = this.getName())
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType(){
|
||||
result = getAsymmetricPaddingType()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class EllipticCurveAlgorithm extends AsymmetricAlgorithm{
|
||||
final string getCurveName(){
|
||||
if exists(string n | n = this.getName() and isEllipticCurveAlgorithm(n))
|
||||
then (isEllipticCurveAlgorithm(result) and result = this.getName())
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
final int getCurveBitSize(){
|
||||
isEllipticCurveAlgorithm(this.getCurveName(), result)
|
||||
}
|
||||
|
||||
override string getAlgType(){
|
||||
result = getEllipticCurveType()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
abstract class BlockModeAlgorithm extends CryptographicAlgorithm{
|
||||
final string getBlockModeName(){
|
||||
if exists(string n | n = this.getName() and isCipherBlockModeAlgorithm(n))
|
||||
then (isCipherBlockModeAlgorithm(result) and result = this.getName())
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source of the IV configuration.
|
||||
*/
|
||||
abstract Expr getIVorNonce();
|
||||
|
||||
final predicate hasIVorNonce() { exists(this.getIVorNonce()) }
|
||||
|
||||
override string getAlgType(){
|
||||
result = getCipherBlockModeType()
|
||||
}
|
||||
}
|
||||
|
||||
// abstract class KeyWrapOperation extends CryptographicOperation{
|
||||
// }
|
||||
|
||||
abstract class AuthenticatedEncryptionAlgorithm extends SymmetricEncryptionAlgorithm{
|
||||
final string getAuthticatedEncryptionName(){
|
||||
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
|
||||
then (isSymmetricEncryptionAlgorithm(result) and result = this.getName())
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class KeyExchangeAlgorithm extends AsymmetricAlgorithm{
|
||||
final string getKeyExchangeName(){
|
||||
if exists(string n | n = this.getName() and isKeyExchangeAlgorithm(n))
|
||||
then (isKeyExchangeAlgorithm(result) and result = this.getName())
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType(){
|
||||
result = getKeyExchangeType()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SigningAlgorithm extends AsymmetricAlgorithm{
|
||||
final string getSigningName(){
|
||||
if exists(string n | n = this.getName() and isSignatureAlgorithm(n))
|
||||
then (isSignatureAlgorithm(result) and result = this.getName())
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override string getAlgType(){
|
||||
result = getSignatureType()
|
||||
}
|
||||
}
|
||||
|
||||
742
cpp/ql/lib/experimental/crypto/modules/OpenSSL.qll
Normal file
742
cpp/ql/lib/experimental/crypto/modules/OpenSSL.qll
Normal file
@@ -0,0 +1,742 @@
|
||||
import cpp
|
||||
import experimental.crypto.CryptoAlgorithmNames
|
||||
import experimental.crypto.CryptoArtifact
|
||||
import experimental.crypto.utils.OpenSSL.CryptoFunction
|
||||
import experimental.crypto.utils.OpenSSL.AlgorithmSink
|
||||
import experimental.crypto.utils.OpenSSL.PassthroughFunction
|
||||
import experimental.crypto.utils.OpenSSL.CryptoAlgorithm
|
||||
import experimental.crypto.CryptoArtifact
|
||||
// import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
|
||||
/**
|
||||
Problematic case in OpenSSL speed.c
|
||||
static const char *names[ALGOR_NUM] = {
|
||||
"md2", "mdc2", "md4", "md5", "sha1", "rmd160",
|
||||
"sha256", "sha512", "whirlpool", "hmac(md5)",
|
||||
"des-cbc", "des-ede3", "rc4", "idea-cbc", "seed-cbc",
|
||||
"rc2-cbc", "rc5-cbc", "blowfish", "cast-cbc",
|
||||
"aes-128-cbc", "aes-192-cbc", "aes-256-cbc",
|
||||
"camellia-128-cbc", "camellia-192-cbc", "camellia-256-cbc",
|
||||
"evp", "ghash", "rand", "cmac"
|
||||
};
|
||||
|
||||
Every entry is considered a block mode, hash, and symmetric encryption algorithm
|
||||
getEncryptionName for example, will return unknown
|
||||
|
||||
*/
|
||||
|
||||
predicate nodeToExpr(DataFlow::Node node, Expr e){
|
||||
e = node.asExpr() or e = node.asIndirectArgument()
|
||||
}
|
||||
|
||||
Expr getExprFromNode(DataFlow::Node node){
|
||||
nodeToExpr(node, result)
|
||||
}
|
||||
|
||||
DataFlow::Node getNodeFromExpr(Expr e){
|
||||
nodeToExpr(result, e)
|
||||
}
|
||||
|
||||
predicate isEVP_PKEY_CTX(Type t){
|
||||
t.getUnderlyingType().stripType().getName() = "evp_pkey_ctx_st"
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression representing an EVP_PKEY_CTX* at the location of a
|
||||
* known AlgorithmSinkArgument.
|
||||
* The EVP_PKEY_CTX* represents the location where the CTX is tied to the algorithm,
|
||||
* and can be used as a source for tracing EVP_PKEY_CTX to other operations.
|
||||
*/
|
||||
class Known_EVP_PKEY_CTX_Ptr_Source extends Expr{
|
||||
Known_EVP_PKEY_CTX_Ptr_Source(){
|
||||
isEVP_PKEY_CTX(this.getUnderlyingType()) and
|
||||
this.getUnderlyingType() instanceof PointerType and
|
||||
exists(AlgorithmSinkArgument arg, Call sinkCall |
|
||||
arg.getSinkCall() = sinkCall and
|
||||
sinkCall.getAnArgument() = this or this = sinkCall)
|
||||
}
|
||||
}
|
||||
|
||||
// module CTXFlow implements DataFlow::ConfigSig{
|
||||
// predicate isSource(DataFlow::Node source) {
|
||||
// // ASSUMPTION: at a sink, an algorithm is converted into a CTX through a return of the call only
|
||||
// // and is the primary source of interest for CTX tracing
|
||||
// source.asExpr() instanceof AlgorithmSinkArgument
|
||||
// }
|
||||
|
||||
// predicate isSink(DataFlow::Node sink){
|
||||
// sink.asExpr() instanceof CTXSink
|
||||
// }
|
||||
|
||||
// predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// // cls.getName() = "asn1_object_st" flow out on any EVP_PKEY_CTX which is "evp_pkey_ctx_st"
|
||||
// exists(Call c |
|
||||
// isEVP_PKEY_CTX(c.getUnderlyingType()) and
|
||||
// node1.asExpr() = c.getAnArgument() and c = node2.asExpr())
|
||||
|
||||
// }
|
||||
|
||||
// }
|
||||
// module CTXFlowConfig = DataFlow::Global<CTXFlow>;
|
||||
|
||||
|
||||
// TODO: currently only handles tracing from literals to sinks
|
||||
module LiteralAlgorithmTracerConfig implements DataFlow::ConfigSig
|
||||
{
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr() instanceof Literal
|
||||
and
|
||||
// Optimization to reduce literal tracing on integers to only those that are known/relevant NIDs.
|
||||
(exists(source.asExpr().getValue().toInt()) implies source.asExpr().getValue().toInt() < getNIDMax())
|
||||
and
|
||||
// False positives observed inside OBJ_nid2* and OBJ_sn2* functions where NULL is a possible assignment.
|
||||
// While this is a concern, it only occurs if the object being referenced is NULL to begin with
|
||||
// Perhaps a different query should be used to find these caes if they represent a threat.
|
||||
// Filter out any open ssl function source in a function namae Obj_*
|
||||
// False positives in OpenSSL also observed for CRYPTO_strndup (filtering any CRYPTO_* function)
|
||||
// due to setting a null byte in the string
|
||||
(isPossibleOpenSSLFunction(source.getEnclosingCallable()) implies
|
||||
not source.getEnclosingCallable().getName().matches("OBJ_%") and not source.getEnclosingCallable().getName().matches("CRYPTO_%"))
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink){
|
||||
// A sink is a call to a function that takes an algorithm as an argument
|
||||
// must include checks for asIndirectArgument since the input may be a pointer to an object
|
||||
// and the member of the object holds the algorithm on the trace.
|
||||
getExprFromNode(sink) instanceof AlgorithmSinkArgument
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
knownPassThroughStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// If the node is the 'next' argument of a isCallPassThrough, it is only allowed if it is an out parameter
|
||||
// i.e., a defining argument. This barrier says that if the node is an expression not an out parameter, it is filtered.
|
||||
// Out arguments will not be filtered.
|
||||
exists(Call c | knownPassthoughCall(c, _, node.asExpr()) and c.getAnArgument() = node.asExpr())
|
||||
or
|
||||
// False positive reducer, don't flow out through argv
|
||||
node.asVariable().hasName("argv")
|
||||
or
|
||||
node.asIndirectVariable().hasName("argv")
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
// Assume a read on crypto identifying field for any object of type asn1_object_st (i.e., ASN1_OBJECT)
|
||||
exists(Class cls | cls.getName() = "asn1_object_st" |
|
||||
node.getType().getUnspecifiedType().stripType() = cls
|
||||
and
|
||||
c.(DataFlow::FieldContent).getField() = cls.getAMember()
|
||||
and
|
||||
c.(DataFlow::FieldContent).getField().getName() in ["nid", "sn", "ln"]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module LiteralAlgorithmTracer = DataFlow::Global<LiteralAlgorithmTracerConfig>;
|
||||
|
||||
/**
|
||||
* `source` is an expression that is a source of an algorithm of type `algType`.
|
||||
* `algType` may be `UNKONWN`.
|
||||
* See CryptoAlgorithmNames for other possible values of `algType`.
|
||||
*/
|
||||
bindingset[sinkAlgType]
|
||||
predicate hasLiteralPathToAlgSink(DataFlow::Node source, DataFlow::Node sink, string sinkAlgType){
|
||||
LiteralAlgorithmTracer::flow(source, sink) and getExprFromNode(sink).(AlgorithmSinkArgument).algType() = sinkAlgType
|
||||
}
|
||||
|
||||
private predicate knownTracedAlgorithm(Literal e, string srcSinkType){
|
||||
knownTracedAlgorithm(e, srcSinkType, srcSinkType)
|
||||
}
|
||||
|
||||
private predicate knownTracedAlgorithm(Literal e, string srcType, string sinkType){
|
||||
resolveAlgorithmFromLiteral(e, _, srcType)
|
||||
and hasLiteralPathToAlgSink(DataFlow::exprNode(e), _, sinkType)
|
||||
and isKnownType(sinkType)
|
||||
and isKnownType(srcType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedLiteralAlgorithm(Literal e, string srcSinkType){
|
||||
// Asymmetric special case:
|
||||
// Since asymmetric algorithm sinks are used for various categories of asymmetric algorithms
|
||||
// an asymmetric algorithm is only unknown if there is no trace from any asymmetric type to the given srcSinkType sink
|
||||
if getAsymmetricType() = srcSinkType
|
||||
then forall(string t | t =getAsymmetricType() | unknownTracedLiteralAlgorithm(e, t, srcSinkType))
|
||||
else unknownTracedLiteralAlgorithm(e, srcSinkType, srcSinkType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedLiteralAlgorithm(Literal e, string srcType, string sinkType){
|
||||
|
||||
// the literal resolves to an algorithm, but not to the sinktype
|
||||
// or generally doesn't resolve to any algorithm type
|
||||
// this case covers 'nonsense' cases e.g., use RSA for symmetric encryption
|
||||
not resolveAlgorithmFromLiteral(e, _, srcType)
|
||||
and isValidAlgorithmLiteral(e)
|
||||
and hasLiteralPathToAlgSink(DataFlow::exprNode(e), _, sinkType)
|
||||
and isKnownType(sinkType)
|
||||
and isKnownType(srcType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedNonLiteralAlgorithm(AlgorithmSinkArgument e, string srcSinkType){
|
||||
// Asymmetric special case:
|
||||
// Since asymmetric algorithm sinks are used for various categories of asymmetric algorithms
|
||||
// an asymmetric algorithm is only unknown if there is no trace from any asymmetric type to the given srcSinkType sink
|
||||
if getAsymmetricType() = srcSinkType then
|
||||
forall(string t | t =getAsymmetricType() | unknownTracedNonLiteralAlgorithm(e, t, srcSinkType))
|
||||
else
|
||||
unknownTracedNonLiteralAlgorithm(e, srcSinkType, srcSinkType)
|
||||
}
|
||||
|
||||
private predicate unknownTracedNonLiteralAlgorithm(AlgorithmSinkArgument e, string srcType, string sinkType){
|
||||
not hasLiteralPathToAlgSink(_, getNodeFromExpr(e), srcType)
|
||||
and LiteralAlgorithmTracerConfig::isSink(getNodeFromExpr(e))
|
||||
and e.algType() = sinkType
|
||||
and isKnownType(srcType)
|
||||
and isKnownType(sinkType)
|
||||
}
|
||||
|
||||
|
||||
private predicate functionAlgorithm(Call c, string algType){
|
||||
isOpenSSLCryptoFunctionCall(c , _ , algType)
|
||||
}
|
||||
|
||||
abstract class OpenSSLTracedAlgorithm extends CryptographicAlgorithm {
|
||||
override string getName(){
|
||||
resolveAlgorithmFromLiteral(this, result, this.getAlgType())
|
||||
}
|
||||
|
||||
override Expr configurationSink(){
|
||||
exists(DataFlow::Node sink |
|
||||
hasLiteralPathToAlgSink(DataFlow::exprNode(this), sink, this.getAlgType()) |
|
||||
result = getExprFromNode(sink)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OpenSSLFunctionAlgorithm extends CryptographicAlgorithm{
|
||||
override string getName(){
|
||||
isOpenSSLCryptoFunctionCall(this , result , this.getAlgType())
|
||||
}
|
||||
|
||||
override Expr configurationSink(){
|
||||
result = this
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OpenSSLUnknownTracedLiteralAlgorithm extends CryptographicAlgorithm{
|
||||
|
||||
override string getName(){
|
||||
result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override Expr configurationSink(){
|
||||
exists(DataFlow::Node sink |
|
||||
hasLiteralPathToAlgSink(DataFlow::exprNode(this), sink, this.getAlgType()) |
|
||||
result = getExprFromNode(sink)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract class OpenSSLUnknownTracedNonLiteralAlgorithm extends CryptographicAlgorithm{
|
||||
override string getName(){
|
||||
result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override Expr configurationSink(){
|
||||
result = this
|
||||
}
|
||||
}
|
||||
|
||||
module SymmetricEncryption{
|
||||
abstract class OpenSSLSymmetricEncryptionAlgorithm extends SymmetricEncryptionAlgorithm
|
||||
{}
|
||||
|
||||
class OpenSSLSymmetricEncryptionTracedAlgorithm extends OpenSSLTracedAlgorithm, OpenSSLSymmetricEncryptionAlgorithm{
|
||||
OpenSSLSymmetricEncryptionTracedAlgorithm(){
|
||||
knownTracedAlgorithm(this, getSymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSymmetricEncryptionFunctionAlgorithm extends OpenSSLFunctionAlgorithm, OpenSSLSymmetricEncryptionAlgorithm{
|
||||
OpenSSLSymmetricEncryptionFunctionAlgorithm(){
|
||||
functionAlgorithm(this , getSymmetricEncryptionType())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OpenSSLSymmetricEncryptionTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm, OpenSSLSymmetricEncryptionAlgorithm{
|
||||
OpenSSLSymmetricEncryptionTracedUnknownLiteralAlgorithm(){
|
||||
unknownTracedLiteralAlgorithm(this, getSymmetricEncryptionType())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OpenSSLSymmetricEncryptionUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm, OpenSSLSymmetricEncryptionAlgorithm{
|
||||
OpenSSLSymmetricEncryptionUnknownNonLiteralTracedAlgorithm(){
|
||||
unknownTracedNonLiteralAlgorithm(this, getSymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module BlockModes{
|
||||
|
||||
/**
|
||||
* In OpenSSL, block modes are associated directly with symmetric encryption algorithms.
|
||||
* As such, OpenSSLBLockModes are modeled as extensions of any openssl symmetric encryption algorithm
|
||||
*/
|
||||
class OpenSSLBlockModeAlgorithm extends BlockModeAlgorithm, Expr instanceof SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm{
|
||||
OpenSSLBlockModeAlgorithm(){
|
||||
//two cases, either the block mode is a literal or it is a function call
|
||||
resolveAlgorithmFromLiteral(this, _, "BLOCK_MODE")
|
||||
or
|
||||
isOpenSSLCryptoFunctionCall(this, _, "BLOCK_MODE")
|
||||
}
|
||||
|
||||
override string getName(){
|
||||
resolveAlgorithmFromLiteral(this, result, "BLOCK_MODE")
|
||||
or
|
||||
isOpenSSLCryptoFunctionCall(this, result, "BLOCK_MODE")
|
||||
|
||||
}
|
||||
|
||||
override Expr configurationSink(){
|
||||
result = this.(SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm).configurationSink()
|
||||
}
|
||||
|
||||
override Expr getIVorNonce(){
|
||||
// TODO
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
class UnknownOpenSSLBlockModeAlgorithm extends BlockModeAlgorithm, Expr instanceof SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm{
|
||||
UnknownOpenSSLBlockModeAlgorithm(){
|
||||
//two cases, either the block mode is a literal or it is a function call
|
||||
not resolveAlgorithmFromLiteral(this, _, "BLOCK_MODE")
|
||||
and
|
||||
not isOpenSSLCryptoFunctionCall(this, _, "BLOCK_MODE")
|
||||
}
|
||||
|
||||
override string getName(){
|
||||
result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override Expr configurationSink(){
|
||||
result = this.(SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm).configurationSink()
|
||||
}
|
||||
|
||||
override Expr getIVorNonce(){
|
||||
none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module Hashes{
|
||||
abstract class OpenSSLHashAlgorithm extends HashAlgorithm
|
||||
{}
|
||||
|
||||
class OpenSSLHashTracedAlgorithm extends OpenSSLTracedAlgorithm, OpenSSLHashAlgorithm{
|
||||
OpenSSLHashTracedAlgorithm(){
|
||||
knownTracedAlgorithm(this, getHashType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLHashFunctionAlgorithm extends OpenSSLFunctionAlgorithm, OpenSSLHashAlgorithm{
|
||||
OpenSSLHashFunctionAlgorithm(){
|
||||
functionAlgorithm(this, getHashType())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OpenSSLHashTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm, OpenSSLHashAlgorithm{
|
||||
OpenSSLHashTracedUnknownLiteralAlgorithm(){
|
||||
unknownTracedLiteralAlgorithm(this, getHashType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLHashUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm, OpenSSLHashAlgorithm{
|
||||
OpenSSLHashUnknownNonLiteralTracedAlgorithm(){
|
||||
unknownTracedNonLiteralAlgorithm(this, getHashType())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class OpenSSLNullHash extends HashAlgorithm{
|
||||
OpenSSLNullHash(){
|
||||
exists(Call c | this = c and isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
c.getTarget().getName() in ["EVP_md_null"])
|
||||
}
|
||||
|
||||
override string getName(){
|
||||
result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override Expr configurationSink(){
|
||||
result = this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module EllipticCurves{
|
||||
// TODO: need to address EVP_PKEY_Q_keygen where the type is "EC" but the curve is UNKNOWN?
|
||||
class OpenSSLEllipticCurveTracedAlgorithm extends OpenSSLTracedAlgorithm, EllipticCurveAlgorithm{
|
||||
OpenSSLEllipticCurveTracedAlgorithm(){
|
||||
knownTracedAlgorithm(this, getEllipticCurveType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLEllipticCurveFunctionAlgorithm extends OpenSSLFunctionAlgorithm, EllipticCurveAlgorithm{
|
||||
OpenSSLEllipticCurveFunctionAlgorithm(){
|
||||
functionAlgorithm(this, getEllipticCurveType())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OpenSSLEllipticCurveTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm, EllipticCurveAlgorithm{
|
||||
OpenSSLEllipticCurveTracedUnknownLiteralAlgorithm(){
|
||||
unknownTracedLiteralAlgorithm(this, getEllipticCurveType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLEllipticCurvehUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm, EllipticCurveAlgorithm{
|
||||
OpenSSLEllipticCurvehUnknownNonLiteralTracedAlgorithm(){
|
||||
unknownTracedNonLiteralAlgorithm(this, getEllipticCurveType())
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_ex.html
|
||||
class OpenSSLNullEllipticCurve extends EllipticCurveAlgorithm{
|
||||
OpenSSLNullEllipticCurve(){
|
||||
exists(Call c | this = c and isPossibleOpenSSLFunction(c.getTarget()) and c.getTarget().getName() in ["EC_KEY_new", "EC_KEY_new_ex"])
|
||||
}
|
||||
|
||||
override string getName(){
|
||||
result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
override Expr configurationSink(){
|
||||
result = this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module AsymmetricEncryption{
|
||||
class OpenSSLAsymmetricEncryptionTracedAlgorithm extends OpenSSLTracedAlgorithm, AsymmetricEncryptionAlgorithm{
|
||||
OpenSSLAsymmetricEncryptionTracedAlgorithm(){
|
||||
knownTracedAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLAsymmetricEncryptionFunctionAlgorithm extends OpenSSLFunctionAlgorithm, AsymmetricEncryptionAlgorithm{
|
||||
OpenSSLAsymmetricEncryptionFunctionAlgorithm(){
|
||||
functionAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLAsymmetricEncryptionTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm, AsymmetricEncryptionAlgorithm{
|
||||
OpenSSLAsymmetricEncryptionTracedUnknownLiteralAlgorithm(){
|
||||
unknownTracedLiteralAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLAsymmetricEncryptionUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm, AsymmetricEncryptionAlgorithm{
|
||||
OpenSSLAsymmetricEncryptionUnknownNonLiteralTracedAlgorithm(){
|
||||
unknownTracedNonLiteralAlgorithm(this, getAsymmetricEncryptionType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module SigningAlgorithms{
|
||||
class OpenSSLSignatureTracedAlgorithm extends OpenSSLTracedAlgorithm, SigningAlgorithm{
|
||||
OpenSSLSignatureTracedAlgorithm(){
|
||||
knownTracedAlgorithm(this, getSignatureType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSignatureFunctionAlgorithm extends OpenSSLFunctionAlgorithm, SigningAlgorithm{
|
||||
OpenSSLSignatureFunctionAlgorithm(){
|
||||
functionAlgorithm(this, getSignatureType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSignatureTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm, SigningAlgorithm{
|
||||
OpenSSLSignatureTracedUnknownLiteralAlgorithm(){
|
||||
unknownTracedLiteralAlgorithm(this, getSignatureType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLSignatureUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm, SigningAlgorithm{
|
||||
OpenSSLSignatureUnknownNonLiteralTracedAlgorithm(){
|
||||
unknownTracedNonLiteralAlgorithm(this, getSignatureType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module KeyExchange{
|
||||
class OpenSSLKeyExchangeTracedAlgorithm extends OpenSSLTracedAlgorithm, KeyExchangeAlgorithm{
|
||||
OpenSSLKeyExchangeTracedAlgorithm(){
|
||||
knownTracedAlgorithm(this, getKeyExchangeType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLKeyExchangeFunctionAlgorithm extends OpenSSLFunctionAlgorithm, KeyExchangeAlgorithm{
|
||||
OpenSSLKeyExchangeFunctionAlgorithm(){
|
||||
functionAlgorithm(this, getKeyExchangeType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLKeyExchangeTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm, KeyExchangeAlgorithm{
|
||||
OpenSSLKeyExchangeTracedUnknownLiteralAlgorithm(){
|
||||
unknownTracedLiteralAlgorithm(this, getKeyExchangeType())
|
||||
}
|
||||
}
|
||||
|
||||
class OpenSSLKeyExchangeUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm, KeyExchangeAlgorithm{
|
||||
OpenSSLKeyExchangeUnknownNonLiteralTracedAlgorithm(){
|
||||
unknownTracedNonLiteralAlgorithm(this, getKeyExchangeType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module KeyGeneration{
|
||||
|
||||
/**
|
||||
* Functions that explicitly set key generation parameters.
|
||||
* `sizeInd` is the parameter specifying the size of the key.
|
||||
* `outInd` is the parameter or return value that the key is written to.
|
||||
* `outInd` is -1 if the key is written to the return value.
|
||||
*/
|
||||
predicate isAsymmetricKeyGenExplicitAlgorithm(Function func, int sizeInd, int outInd){
|
||||
isPossibleOpenSSLFunction(func) and
|
||||
exists(string name | func.hasGlobalName(name) |
|
||||
(
|
||||
name in [
|
||||
"EVP_PKEY_CTX_set_dsa_paramgen_bits", "DSA_generate_parameters_ex",
|
||||
"EVP_PKEY_CTX_set_rsa_keygen_bits", "RSA_generate_key_ex", "RSA_generate_key_fips",
|
||||
"EVP_PKEY_CTX_set_dh_paramgen_prime_len", "DH_generate_parameters_ex"
|
||||
] and
|
||||
sizeInd = 1 and outInd = 0
|
||||
)
|
||||
or
|
||||
(
|
||||
name in ["DSA_generate_parameters", "RSA_generate_key", "DH_generate_parameters"] and
|
||||
sizeInd = 0 and outInd = -1
|
||||
)
|
||||
)
|
||||
and
|
||||
exists(Type t |
|
||||
(if sizeInd = -1
|
||||
then t = func.getType().getUnderlyingType()
|
||||
else t = func.getParameter(sizeInd).getUnderlyingType())
|
||||
and
|
||||
t instanceof IntegralType and
|
||||
not t instanceof CharType
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
module AsymExplicitAlgKeyLengthFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
// Optimizations to avoid tracing all integers
|
||||
node.asExpr().(Literal).getValue().toInt() > 0 and // exclude sentinel values
|
||||
node.asExpr().(Literal).getValue().toInt() < 8500
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(FunctionCall c, int sizeInd |
|
||||
isAsymmetricKeyGenExplicitAlgorithm(c.getTarget(), sizeInd, _) and
|
||||
c.getArgument(sizeInd) = node.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
module AsymExplicitAlgKeyLengthFlow = DataFlow::Global<AsymExplicitAlgKeyLengthFlowConfig>;
|
||||
|
||||
class OpenSSLAsymmetricKeyGenTiedToAlgorithm extends AsymmetricKeyGeneration{
|
||||
OpenSSLAsymmetricKeyGenTiedToAlgorithm(){
|
||||
exists(Call c |
|
||||
this = c and isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
isAsymmetricKeyGenExplicitAlgorithm(c.getTarget(), _, _))
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm(){
|
||||
result = this
|
||||
}
|
||||
|
||||
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg){
|
||||
alg = this and
|
||||
exists(int sizeInd | isAsymmetricKeyGenExplicitAlgorithm(this.getTarget(), sizeInd, _) and
|
||||
AsymExplicitAlgKeyLengthFlow::flow(DataFlow::exprNode(result), DataFlow::exprNode(this.getArgument(sizeInd))))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module Length_to_RSA_EVP_PKEY_Q_keygen_Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
// Optimizations to avoid tracing all integers
|
||||
node.asExpr().(Literal).getValue().toInt() > 0 and // exclude sentinel values
|
||||
node.asExpr().(Literal).getValue().toInt() < 5000
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(FunctionCall c|
|
||||
c.getTarget().getName() = "EVP_PKEY_Q_keygen" and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
c.getArgument(3) = node.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
module Length_to_RSA_EVP_PKEY_Q_keygen_Flow = DataFlow::Global<Length_to_RSA_EVP_PKEY_Q_keygen_Config>;
|
||||
|
||||
|
||||
class OpenSSL_RSA_EVP_PKEY_Q_keygen extends AsymmetricKeyGeneration{
|
||||
OpenSSL_RSA_EVP_PKEY_Q_keygen(){
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isPossibleOpenSSLFunction(c.getTarget()) and
|
||||
this.getTarget().getName() = "EVP_PKEY_Q_keygen" and
|
||||
this.getArgument(3).getUnderlyingType() instanceof IntegralType)
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm(){
|
||||
result.configurationSink().(AlgorithmSinkArgument).getSinkCall() = this
|
||||
}
|
||||
|
||||
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg){
|
||||
alg = this.getAlgorithm() and
|
||||
Length_to_RSA_EVP_PKEY_Q_keygen_Flow::flow(DataFlow::exprNode(result), DataFlow::exprNode(this.getArgument(3)))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
predicate isKeyGenOperationWithNoSize(Function func){
|
||||
isPossibleOpenSSLFunction(func) and
|
||||
exists(string name | func.hasGlobalName(name) |
|
||||
name in ["EVP_PKEY_keygen", "DSA_generate_key", "DH_generate_key", "EVP_PKEY_generate"])
|
||||
}
|
||||
|
||||
module KeyGenKeySizeInitToKeyGenConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
exists(Call c, Function func, int outInd |
|
||||
isAsymmetricKeyGenExplicitAlgorithm(func, _, outInd) and
|
||||
c.getTarget() = func
|
||||
|
|
||||
if outInd = -1
|
||||
then node.asExpr() = c
|
||||
else node.asExpr() = c.getArgument(outInd)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(Call c | isKeyGenOperationWithNoSize(c.getTarget()) and c.getAnArgument() = node.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
module KeyGenKeySizeInitToKeyGenFlow = DataFlow::Global<KeyGenKeySizeInitToKeyGenConfig>;
|
||||
|
||||
predicate isEVP_PKEY_CTX_Source(DataFlow::Node node, CryptographicAlgorithm alg){
|
||||
exists(Call c |
|
||||
alg.configurationSink().(AlgorithmSinkArgument).getSinkCall() = c and
|
||||
(
|
||||
node.asExpr() = c
|
||||
or
|
||||
node.asExpr() = c.getAnArgument()
|
||||
or
|
||||
node.asDefiningArgument() = c.getAnArgument()
|
||||
)
|
||||
)
|
||||
and
|
||||
(
|
||||
node.asExpr() instanceof Known_EVP_PKEY_CTX_Ptr_Source
|
||||
or
|
||||
node.asDefiningArgument() instanceof Known_EVP_PKEY_CTX_Ptr_Source
|
||||
)
|
||||
}
|
||||
|
||||
predicate isKeyGen_EVP_PKEY_CTX_Sink(DataFlow::Node node, Call c){
|
||||
isKeyGenOperationWithNoSize(c.getTarget()) and nodeToExpr(node, c.getAnArgument())
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace from EVP_PKEY_CTX* at algorithm sink to keygen,
|
||||
* users can then extrapolatae the matching algorithm from the alg sink to the keygen
|
||||
*/
|
||||
module EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize implements DataFlow::ConfigSig{
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
isEVP_PKEY_CTX_Source(source, _)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink){
|
||||
isKeyGen_EVP_PKEY_CTX_Sink(sink, _)
|
||||
}
|
||||
}
|
||||
|
||||
module EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize_Flow = DataFlow::Global<EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize>;
|
||||
|
||||
/**
|
||||
* UNKNOWN key sizes to general purpose key generation functions (i.e., that take in no key size and assume
|
||||
* is it set on context prior to the call). No path from a key configuration to these operations
|
||||
* means the key size is UNKNOWN, or more precisely the key size is DEFAULT but
|
||||
* the defaults can change with each version of OpenSSL, we simply assume the size is generally UNKNOWN.
|
||||
* ASSUMPTION/TODO: we currently model all known locations where a key size is set explicitly.
|
||||
* When a key is set implicitly, this usually means a key generation operation
|
||||
* is called where the operation takes in no key size, and no flow to this operation
|
||||
* initializes the context with a key size.
|
||||
* Currently, without a definitive source (set of sources) to start tracing from, we cannot determine
|
||||
* determine if a single path exists that initializes the context with a key size and another that doesn't.
|
||||
* Rather than attempt to model all possible sources, we assume that if no path
|
||||
* from a key config location reaches a generic key generation operation, then the key size is not set.
|
||||
* NOTE: while this is true, it is possible a key size is set in one path, but not in another
|
||||
* meaning this approach (and other similar approaches used in this model for UNKNOWN)
|
||||
* can produce false negatives.
|
||||
*/
|
||||
class OpenSSLDefaultKeyGeneration extends AsymmetricKeyGeneration{
|
||||
OpenSSLDefaultKeyGeneration(){
|
||||
// this is a call to a function matching isKeyGenOperationWithNoSize
|
||||
// and there is no flow from a key configuration source to this call
|
||||
exists(Call c |
|
||||
this = c and
|
||||
isKeyGenOperationWithNoSize(this.getTarget()) and
|
||||
not exists(DataFlow::Node src, DataFlow::Node sink |
|
||||
KeyGenKeySizeInitToKeyGenFlow::flow(src, sink) and
|
||||
nodeToExpr(sink, this.getAnArgument()))
|
||||
)
|
||||
}
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm(){
|
||||
if this.getTarget().getName() in ["DSA_generate_key", "DH_generate_key"] then
|
||||
result = this
|
||||
else
|
||||
(
|
||||
// NOTE/ASSUMPTION: EVP_PKEY_keygen, EVP_PKEY_generate assume only other possibilities,
|
||||
// each take in a CTX as the first arg, need to trace from an alg sink from this CTX param
|
||||
|
||||
|
||||
// get every alg sink, get the corresponding call, trace out on any CTX type variable
|
||||
// to the key gen
|
||||
// NOTE: looking for any cryptographic algorithm tracing to the keygen to handle
|
||||
// any odd cases we aren't awaare of where keygen can be used for other algorithm types
|
||||
exists(DataFlow::Node src, DataFlow::Node sink |
|
||||
EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize_Flow::flow(src,sink)
|
||||
and
|
||||
isEVP_PKEY_CTX_Source(src, result)
|
||||
and
|
||||
isKeyGen_EVP_PKEY_CTX_Sink(sink, this)
|
||||
// TODO: what if there is no CTX source? then the keygen becomes an UNKNOWN sink
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* For this class, there is no known configuration source for any algorithm
|
||||
*/
|
||||
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg){
|
||||
none()
|
||||
}
|
||||
}
|
||||
}
|
||||
283
cpp/ql/lib/experimental/crypto/utils/OpenSSL/AlgorithmSink.qll
Normal file
283
cpp/ql/lib/experimental/crypto/utils/OpenSSL/AlgorithmSink.qll
Normal file
@@ -0,0 +1,283 @@
|
||||
/**
|
||||
* Predicates/classes for identifying algorithm sinks.
|
||||
* An Algorithm Sink is a function that takes an algorithm as an argument.
|
||||
* In particular, any function that takes in an algorithm that until the call
|
||||
* the algorithm is not definitely known to be an algorithm (e.g., an integer used as an identifier to fetch an algorithm)
|
||||
*/
|
||||
|
||||
//TODO: enforce a hierarchy of AlgorithmSinkArgument, e.g., so I can get all Asymmetric SinkArguments that includes all the strictly RSA etc.
|
||||
import cpp
|
||||
import experimental.crypto.utils.OpenSSL.LibraryFunction
|
||||
import experimental.crypto.CryptoAlgorithmNames
|
||||
|
||||
predicate isAlgorithmSink(AlgorithmSinkArgument arg, string algType){
|
||||
arg.algType() = algType
|
||||
}
|
||||
|
||||
|
||||
abstract class AlgorithmSinkArgument extends Expr {
|
||||
AlgorithmSinkArgument() {
|
||||
exists(Call c| c.getAnArgument() = this and openSSLLibraryFunc(c.getTarget()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function call in which the argument exists
|
||||
*/
|
||||
Call getSinkCall(){
|
||||
result.getAnArgument() = this
|
||||
}
|
||||
|
||||
abstract string algType();
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html
|
||||
predicate cipherAlgorithmSink(string funcName, int argInd){
|
||||
(funcName in ["EVP_get_cipherbyname", "EVP_get_cipherbynid", "EVP_get_cipherbyobj"] and argInd = 0)
|
||||
or
|
||||
(funcName = "EVP_CIPHER_fetch" and argInd = 1)
|
||||
}
|
||||
|
||||
class CipherAlgorithmSink extends AlgorithmSinkArgument {
|
||||
CipherAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
|
||||
cipherAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getSymmetricEncryptionType() }
|
||||
}
|
||||
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_MAC_fetch
|
||||
predicate macAlgorithmSink(string funcName, int argInd){
|
||||
(funcName = "EVP_MAC_fetch" and argInd = 1)
|
||||
}
|
||||
|
||||
class MACAlgorithmSink extends AlgorithmSinkArgument {
|
||||
MACAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
|
||||
macAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "TBD" }
|
||||
}
|
||||
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_MD_fetch
|
||||
predicate messageDigestAlgorithmSink(string funcName, int argInd){
|
||||
(funcName in ["EVP_get_digestbyname", "EVP_get_digestbynid", "EVP_get_digestbyobj"] and argInd = 0)
|
||||
or
|
||||
funcName = "EVP_MD_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class MessageDigestAlgorithmSink extends AlgorithmSinkArgument {
|
||||
MessageDigestAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
|
||||
messageDigestAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getHashType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KEYEXCH_fetch
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KEM_fetch
|
||||
predicate keyExchangeAlgorithmSink(string funcName, int argInd){
|
||||
funcName = "EVP_KEYEXCH_fetch" and argInd = 1
|
||||
or
|
||||
funcName = "EVP_KEM_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class KeyExchangeAlgorithmSink extends AlgorithmSinkArgument {
|
||||
KeyExchangeAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
|
||||
keyExchangeAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getKeyExchangeType() }
|
||||
}
|
||||
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KEYMGMT_fetch
|
||||
predicate keyManagementAlgorithmSink(string funcName, int argInd){
|
||||
funcName = "EVP_KEYMGMT_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class KeyManagementAlgorithmSink extends AlgorithmSinkArgument {
|
||||
KeyManagementAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
|
||||
keyManagementAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "TBD" }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_KDF
|
||||
predicate keyDerivationAlgorithmSink(string funcName, int argInd){
|
||||
funcName = "EVP_KDF_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class KeyDerivationAlgorithmSink extends AlgorithmSinkArgument {
|
||||
KeyDerivationAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
|
||||
keyDerivationAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getKeyDerivationType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_ASYM_CIPHER_fetch
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new_CMAC_key.html
|
||||
predicate asymmetricCipherAlgorithmSink(string funcName, int argInd){
|
||||
(funcName = "EVP_ASYM_CIPHER_fetch" and argInd = 1)
|
||||
or
|
||||
(funcName = "EVP_PKEY_new_CMAC_key" and argInd = 3)
|
||||
// NOTE: other cases are handled by AsymmetricAlgorithmSink
|
||||
}
|
||||
|
||||
class AsymmetricCipherAlgorithmSink extends AlgorithmSinkArgument {
|
||||
AsymmetricCipherAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
|
||||
asymmetricCipherAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "ASYMMETRIC_ENCRYPTION"}
|
||||
}
|
||||
|
||||
class AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
|
||||
AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen() {
|
||||
exists(Call c, string funcName |
|
||||
funcName = c.getTarget().getName() and
|
||||
this = c.getArgument(3) |
|
||||
funcName = "EVP_PKEY_Q_keygen" and
|
||||
c.getArgument(3).getType().getUnderlyingType() instanceof IntegralType
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "ASYMMETRIC_ENCRYPTION"}
|
||||
}
|
||||
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_RAND_fetch
|
||||
predicate randomAlgorithmSink(string funcName, int argInd){
|
||||
funcName = "EVP_RAND_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class RandomAlgorithmSink extends AlgorithmSinkArgument {
|
||||
RandomAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
|
||||
randomAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = "TBD" }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_SIGNATURE_fetch
|
||||
predicate signatureAlgorithmSink(string funcName, int argInd){
|
||||
funcName = "EVP_SIGNATURE_fetch" and argInd = 1
|
||||
}
|
||||
|
||||
class SignatureAlgorithmSink extends AlgorithmSinkArgument {
|
||||
SignatureAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
|
||||
signatureAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getSignatureType() }
|
||||
}
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_by_curve_name.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_ec_paramgen_curve_nid.html
|
||||
predicate ellipticCurveAlgorithmSink(string funcName, int argInd){
|
||||
funcName in ["EC_KEY_new_by_curve_name", "EVP_EC_gen"] and argInd = 0
|
||||
or
|
||||
funcName = "EC_KEY_new_by_curve_name_ex" and argInd = 2
|
||||
or
|
||||
funcName in ["EVP_PKEY_CTX_set_ec_paramgen_curve_nid"] and argInd = 1
|
||||
|
||||
}
|
||||
|
||||
class EllipticCurveAlgorithmSink extends AlgorithmSinkArgument {
|
||||
EllipticCurveAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
|
||||
ellipticCurveAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getEllipticCurveType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Special cased to address the fact that arg index 3 (zero offset based) is the curve name.
|
||||
* ASSUMPTION: if the arg ind 3 is a char* assume it is an elliptic curve
|
||||
*/
|
||||
class EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
|
||||
EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen() {
|
||||
exists(Call c, string funcName |
|
||||
funcName = c.getTarget().getName() and
|
||||
this = c.getArgument(3) |
|
||||
funcName = "EVP_PKEY_Q_keygen" and
|
||||
c.getArgument(3).getType().getUnderlyingType() instanceof PointerType and
|
||||
c.getArgument(3).getType().getUnderlyingType().stripType() instanceof CharType
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getEllipticCurveType() }
|
||||
}
|
||||
|
||||
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id.html
|
||||
// https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_new_raw_private_key.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_Q_keygen.html
|
||||
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
|
||||
predicate asymmetricAlgorithmSink(string funcName, int argInd){
|
||||
|
||||
funcName = "EVP_PKEY_CTX_new_id" and argInd = 0
|
||||
or
|
||||
funcName = "EVP_PKEY_CTX_new_from_name" and argInd = 1
|
||||
or
|
||||
funcName in ["EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_new_mac_key"] and argInd = 0
|
||||
or
|
||||
funcName in ["EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex"] and argInd = 1
|
||||
|
||||
// special casing this as arg index 3 must be specified depending on if RSA or ECC, and otherwise not specified for other algs
|
||||
// funcName = "EVP_PKEY_Q_keygen" and argInd = 2
|
||||
or
|
||||
funcName in ["EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"] and argInd = 1
|
||||
// TODO consider void cases EVP_PKEY_new
|
||||
}
|
||||
|
||||
class AsymmetricAlgorithmSink extends AlgorithmSinkArgument {
|
||||
AsymmetricAlgorithmSink() {
|
||||
exists(Call c, string funcName, int argInd | funcName = c.getTarget().getName() and this = c.getArgument(argInd) |
|
||||
asymmetricAlgorithmSink(funcName, argInd)
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getAsymmetricType() }
|
||||
}
|
||||
|
||||
|
||||
class AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
|
||||
AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen() {
|
||||
exists(Call c, string funcName |
|
||||
funcName = c.getTarget().getName() and
|
||||
this = c.getArgument(2) |
|
||||
funcName = "EVP_PKEY_Q_keygen" and
|
||||
not exists(c.getArgument(3))
|
||||
)
|
||||
}
|
||||
|
||||
override string algType() { result = getAsymmetricType() }
|
||||
}
|
||||
|
||||
1986
cpp/ql/lib/experimental/crypto/utils/OpenSSL/CryptoAlgorithm.qll
Normal file
1986
cpp/ql/lib/experimental/crypto/utils/OpenSSL/CryptoAlgorithm.qll
Normal file
File diff suppressed because it is too large
Load Diff
112
cpp/ql/lib/experimental/crypto/utils/OpenSSL/CryptoFunction.qll
Normal file
112
cpp/ql/lib/experimental/crypto/utils/OpenSSL/CryptoFunction.qll
Normal file
@@ -0,0 +1,112 @@
|
||||
import cpp
|
||||
import experimental.crypto.utils.OpenSSL.LibraryFunction
|
||||
import experimental.crypto.CryptoAlgorithmNames
|
||||
|
||||
predicate inferredOpenSSLCryptoFunctionCall(Call c , string normalized, string algType){
|
||||
inferredOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
|
||||
}
|
||||
|
||||
predicate inferredOpenSSLCryptoFunction(Function f, string normalized, string algType){
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
normalizeFunctionName(f, algType) = normalized
|
||||
}
|
||||
|
||||
|
||||
predicate isOpenSSLCryptoFunction(Function f, string normalized, string algType){
|
||||
// NOTE: relying on inference as there are thousands of functions for crypto
|
||||
// enumerating them all and maintaining the list seems problematic.
|
||||
// For now, we will rely on dynamically inferring algorithms for function names.
|
||||
// This has been seen to be reasonably efficient and accurate.
|
||||
inferredOpenSSLCryptoFunction(f, normalized, algType)
|
||||
}
|
||||
|
||||
predicate isOpenSSLCryptoFunctionCall(Call c, string normalized, string algType){
|
||||
isOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
|
||||
}
|
||||
|
||||
|
||||
private string basicNormalizeFunctionName(Function f, string algType) {
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
isKnownAlgorithm(result, algType) and
|
||||
exists(string normStr |
|
||||
normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "")
|
||||
|
|
||||
normStr.matches("%" + result + "%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
|
||||
*
|
||||
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
|
||||
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
|
||||
*
|
||||
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
|
||||
*
|
||||
* `f` is the function name to normalize.
|
||||
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
|
||||
*/
|
||||
private string privateNormalizeFunctionName(Function f, string algType) {
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
(
|
||||
result = basicNormalizeFunctionName(f, algType)
|
||||
) and
|
||||
not exists(string res2 |
|
||||
result != res2 and
|
||||
res2 = basicNormalizeFunctionName(f, algType) and
|
||||
res2.length() > result.length()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Normalizes a function name to a known algorithm name, similar to `normalizeName`.
|
||||
* A function is not, however, allowed to be UNKNOWN. The function either
|
||||
* normalizes to a known algorithm name, or the predicate does not hold (no result).
|
||||
*
|
||||
* The predicate attempts to restrict normalization to what looks like an openssl
|
||||
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
|
||||
* This may give false postive functions if a directory erronously appears to be openssl;
|
||||
* however, we take the stance that if a function
|
||||
* exists strongly mapping to a known function name in a directory such as these,
|
||||
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.
|
||||
*/
|
||||
private string normalizeFunctionName(Function f, string algType) {
|
||||
algType != "UNKNOWN" and
|
||||
isPossibleOpenSSLFunction(f) and
|
||||
result = privateNormalizeFunctionName(f, algType) and
|
||||
// Addressing false positives
|
||||
// For algorithm names less than or equal to 4, we must see the algorithm name
|
||||
// in the original function as upper case (it can't be split between tokens)
|
||||
// One exception found is DES_xcbc_encrypt, this is DESX
|
||||
((result.length() <= 4 and result != "DESX") implies (f.getName().toUpperCase().matches("%" + result + "%")))
|
||||
and
|
||||
((result.length() <= 4 and result = "DESX") implies (f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%")))
|
||||
and
|
||||
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
|
||||
// and
|
||||
// DES specific false positives
|
||||
(result.matches("DES") implies (not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")))
|
||||
and
|
||||
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
|
||||
// f.getName().toUpperCase().matches("%" + s + "%"))) and
|
||||
// SEED specific false positives
|
||||
((result.matches("SEED")) implies not exists(string s | s in ["SEED_SRC_GENERATE", "RAND","NEW_SEED", "GEN_SEED", "SEED_GEN", "SET_SEED", "GET_SEED", "GET0_SEED", "RESEED", "SEEDING"] |
|
||||
f.getName().toUpperCase().matches("%" + s + "%"))) and
|
||||
// ARIA specific false positives
|
||||
((result.matches("ARIA")) implies not f.getName().toUpperCase().matches("%VARIANT%")) and
|
||||
// CTR false positives
|
||||
((result.matches("CTR") implies not f.getName().toUpperCase().matches("%CTRL%"))) and
|
||||
// ES false positives (e.g., ES256 from AES256)
|
||||
((result.matches("ES%")) implies not f.getName().toUpperCase().matches("%AES%")) and
|
||||
// RSA false positives
|
||||
(((result.matches("RSA")) implies not f.getName().toUpperCase().matches("%UNIVERSAL%")))
|
||||
and
|
||||
//rsaz functions deemed to be too low level, and can be ignored
|
||||
not f.getLocation().getFile().getBaseName().matches("rsaz_exp.c") and
|
||||
// General False positives
|
||||
// Functions that 'get' do not set an algorithm, and therefore are considered ignorable
|
||||
not f.getName().toLowerCase().matches("%get%")
|
||||
}
|
||||
|
||||
|
||||
139
cpp/ql/lib/experimental/crypto/utils/OpenSSL/DataBuilders.qll
Normal file
139
cpp/ql/lib/experimental/crypto/utils/OpenSSL/DataBuilders.qll
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* This file contains predicates create to build up initial data sets for OpenSSL
|
||||
* predicates. E.g., These predicates were used to assist in associating all
|
||||
* openSSL functions with their known crypto algorithms.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import experimental.crypto.CryptoAlgorithmNames
|
||||
import experimental.crypto.utils.OpenSSL.CryptoFunction
|
||||
|
||||
|
||||
private string basicNormalizeFunctionName(Function f, string algType) {
|
||||
isKnownAlgorithm(result, algType) and
|
||||
exists(string normStr |
|
||||
normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "")
|
||||
|
|
||||
normStr.matches("%" + result + "%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
|
||||
*
|
||||
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
|
||||
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
|
||||
*
|
||||
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
|
||||
*
|
||||
* `f` is the function name to normalize.
|
||||
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
|
||||
*/
|
||||
private string privateNormalizeFunctionName(Function f, string algType) {
|
||||
(
|
||||
result = basicNormalizeFunctionName(f, algType)
|
||||
) and
|
||||
not exists(string res2 |
|
||||
result != res2 and
|
||||
res2 = basicNormalizeFunctionName(f, algType) and
|
||||
res2.length() > result.length()
|
||||
) and
|
||||
// Addressing bad normalization case-by-case
|
||||
// CASE: ES256 being identified when the algorithm is AES256
|
||||
(result.matches("ES256") implies not exists(string res2 | res2 = basicNormalizeFunctionName(f, _) and res2.matches("AES%")))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Normalizes a function name to a known algorithm name, similar to `normalizeName`.
|
||||
* A function is not, however, allowed to be UNKNOWN. The function either
|
||||
* normalizes to a known algorithm name, or the predicate does not hold (no result).
|
||||
*
|
||||
* The predicate attempts to restrict normalization to what looks like an openssl
|
||||
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
|
||||
* This may give false postive functions if a directory erronously appears to be openssl;
|
||||
* however, we take the stance that if a function
|
||||
* exists strongly mapping to a known function name in a directory such as these,
|
||||
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.
|
||||
*/
|
||||
string normalizeFunctionName(Function f, string algType) {
|
||||
algType != "UNKNOWN" and
|
||||
result = privateNormalizeFunctionName(f, algType) and
|
||||
openSSLLibraryFunc(f) and
|
||||
// Addressing false positives
|
||||
// For algorithm names less than or equal to 4, we must see the algorithm name
|
||||
// in the original function as upper case (it can't be split between tokens)
|
||||
// One exception found is DES_xcbc_encrypt, this is DESX
|
||||
((result.length() <= 4 and result != "DESX") implies (f.getName().toUpperCase().matches("%" + result + "%")))
|
||||
and
|
||||
((result.length() <= 4 and result = "DESX") implies (f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%")))
|
||||
and
|
||||
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
|
||||
// and
|
||||
// DES specific false positives
|
||||
(result.matches("DES") implies (not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")))
|
||||
and
|
||||
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
|
||||
// f.getName().toUpperCase().matches("%" + s + "%"))) and
|
||||
// SEED specific false positives
|
||||
((result.matches("%SEED%")) implies not not exists(string s | s in ["NEW_SEED", "GEN_SEED", "SET_SEED", "GET_SEED", "GET0_SEED", "RESEED", "SEEDING"] |
|
||||
f.getName().toUpperCase().matches("%" + s + "%"))) and
|
||||
// ARIA specific false positives
|
||||
((result.matches("%ARIA%")) implies not f.getName().toUpperCase().matches("%VARIANT%"))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Predicate to support name normalization.
|
||||
* Converts the raw name upper-case with no hyphen, slash, underscore, hash, or space.
|
||||
* Looks for substrings that are known algorithms, and normalizes the name.
|
||||
* If the algorithm cannot be determined or is in the ignorable list (`isIgnorableOpenSSLAlgorithm`)
|
||||
* this predicate will not resolve a name.
|
||||
*
|
||||
* Rationale for private: For normalization, we want to get the longest string for a normalized name match
|
||||
* for a given algorithm type. I found this easier to express if the public normalizeName
|
||||
* checks that the name is the longest, and that UNKNOWN is reserved if there exists no
|
||||
* result from this predicate that is known.
|
||||
*/
|
||||
bindingset[name]
|
||||
string privateNormalizeName(string name, string algType) {
|
||||
//not isIgnorableOpenSSLAlgorithm(name, _, _) and
|
||||
// targetOpenSSLAlgorithm(name, _) and
|
||||
isKnownAlgorithm(result, algType) and
|
||||
exists(string normStr |
|
||||
normStr = name.toUpperCase().regexpReplaceAll("[-_ ]|/", "")
|
||||
|
|
||||
normStr.matches("%" + result + "%")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
|
||||
*
|
||||
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
|
||||
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
|
||||
*
|
||||
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
|
||||
*
|
||||
* `name` is the name to normalize.
|
||||
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
|
||||
*/
|
||||
bindingset[name]
|
||||
string normalizeName(string name, string algType) {
|
||||
(
|
||||
if exists(privateNormalizeName(name, _))
|
||||
then result = privateNormalizeName(name, algType)
|
||||
else (
|
||||
result = unknownAlgorithm() and algType = "UNKNOWN"
|
||||
)
|
||||
) and
|
||||
not exists(string res2 |
|
||||
result != res2 and
|
||||
res2 = privateNormalizeName(name, algType) and
|
||||
res2.length() > result.length()
|
||||
) and
|
||||
// Addressing bad normalization case-by-case
|
||||
// CASE: ES256 being identified when the algorithm is AES256
|
||||
(result.matches("ES256") implies not exists(string res2 | res2 = privateNormalizeName(name, _) and res2.matches("AES%")))
|
||||
}
|
||||
11295
cpp/ql/lib/experimental/crypto/utils/OpenSSL/LibraryFunction.qll
Normal file
11295
cpp/ql/lib/experimental/crypto/utils/OpenSSL/LibraryFunction.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
||||
import cpp
|
||||
import experimental.crypto.utils.OpenSSL.LibraryFunction
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
|
||||
// TODO: possible use of extensible predicates here
|
||||
// NOTE: -1 for outInd represents the return value
|
||||
predicate knownPassthroughFunction(Function f, int inInd, int outInd){
|
||||
// Trace through functions
|
||||
// See https://www.openssl.org/docs/man1.1.1/man3/OBJ_obj2txt
|
||||
// https://www.openssl.org/docs/man3.0/man3/EVP_CIPHER_get0_name
|
||||
openSSLLibraryFunc(f)
|
||||
and
|
||||
(
|
||||
(
|
||||
f.getName() in [
|
||||
"OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn",
|
||||
"OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid",
|
||||
"OBJ_txt2nid","OBJ_txt2obj", "OBJ_dup",
|
||||
"EVP_CIPHER_get0_name"]
|
||||
and inInd = 0 and outInd = -1)
|
||||
or
|
||||
(
|
||||
f.getName() in ["OBJ_obj2txt","i2t_ASN1_OBJECT"] and
|
||||
inInd = 2 and outInd = 0
|
||||
)
|
||||
or
|
||||
// Dup/copy pattern occurs in more places,
|
||||
//see: https://www.openssl.org/docs/manmaster/man3/EC_KEY_copy.html and https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_dup.html
|
||||
f.getName().matches("%_dup") and inInd = 0 and outInd = -1
|
||||
or
|
||||
f.getName().matches("%_copy") and inInd = 0 and outInd = -1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* `c` is a call to a function that preserves the algorithm but changes its form.
|
||||
* `onExpr` is the input argument passing through to, `outExpr` is the next expression in a dataflow step associated with `c`
|
||||
*/
|
||||
predicate knownPassthoughCall(Call c, Expr inExpr, Expr outExpr){
|
||||
exists(int inInd, int outInd |
|
||||
knownPassthroughFunction(c.getTarget(), inInd, outInd) and
|
||||
inExpr = c.getArgument(inInd) and
|
||||
if(outInd = -1)
|
||||
then outExpr = c
|
||||
else outExpr = c.getArgument(outInd)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* Explicitly add flow through openssl functions that preserve the algorithm but alter the form (e.g., from NID to string)
|
||||
*/
|
||||
predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2){
|
||||
exists(Expr cur, Expr next |
|
||||
(cur = node1.asExpr() or cur = node1.asIndirectArgument()) and
|
||||
(next = node2.asExpr() or next = node2.asIndirectArgument() or next = node2.asDefiningArgument())
|
||||
|
|
||||
exists(Call c | knownPassthoughCall(c, cur, next)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user