Modify model to use newtypes, expand modeling

This commit is contained in:
Nicolas Will
2025-02-12 17:58:15 +01:00
parent 4d44755945
commit 874e3b5e06
4 changed files with 676 additions and 167 deletions

View File

@@ -4,78 +4,172 @@ import semmle.code.cpp.dataflow.new.DataFlow
module OpenSSLModel {
import Language
abstract class KeyDerivationOperation extends Crypto::KeyDerivationOperation { }
class FunctionCallOrMacroAccess extends Element {
FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess }
class SHA1Algo extends Crypto::HashAlgorithm instanceof MacroAccess {
SHA1Algo() { this.getMacro().getName() = "SN_sha1" }
override string getRawAlgorithmName() { result = "SN_sha1" }
override Crypto::THashType getHashType() { result instanceof Crypto::SHA1 }
string getTargetName() {
result = this.(FunctionCall).getTarget().getName()
or
result = this.(MacroAccess).getMacroName()
}
}
/**
* Hash function references in OpenSSL.
*/
predicate hash_ref_type_mapping_known(string name, Crypto::THashType algo) {
// `ma` name has an LN_ or SN_ prefix, which we want to ignore
// capture any name after the _ prefix using regex matching
name = ["sha1", "sha160"] and algo instanceof Crypto::SHA1
or
name = ["sha224", "sha256", "sha384", "sha512"] and algo instanceof Crypto::SHA2
or
name = ["sha3-224", "sha3-256", "sha3-384", "sha3-512"] and algo instanceof Crypto::SHA3
or
name = "md2" and algo instanceof Crypto::MD2
or
name = "md4" and algo instanceof Crypto::MD4
or
name = "md5" and algo instanceof Crypto::MD5
or
name = "ripemd160" and algo instanceof Crypto::RIPEMD160
or
name = "whirlpool" and algo instanceof Crypto::WHIRLPOOL
}
predicate hash_ref_type_mapping(FunctionCallOrMacroAccess ref, string name, Crypto::THashType algo) {
name = ref.getTargetName().regexpCapture("(?:SN_|LN_|EVP_)([a-z0-9]+)", 1) and
hash_ref_type_mapping_known(name, algo)
}
class HashAlgorithmRef extends Crypto::HashAlgorithm {
FunctionCallOrMacroAccess instance;
HashAlgorithmRef() {
this = Crypto::THashAlgorithm(instance) and
hash_ref_type_mapping(instance, _, _)
}
override string getSHA2OrSHA3DigestSize(Location location) {
(
this.getHashType() instanceof Crypto::SHA2 or
this.getHashType() instanceof Crypto::SHA3
) and
exists(string name |
hash_ref_type_mapping(instance, name, this.getHashType()) and
result = name.regexpFind("\\d{3}", 0, _) and
location = instance.getLocation()
)
}
override string getRawAlgorithmName() { result = instance.getTargetName() }
override Crypto::THashType getHashType() { hash_ref_type_mapping(instance, _, result) }
Element getInstance() { result = instance }
override Location getLocation() { result = instance.getLocation() }
}
/**
* Data-flow configuration for key derivation algorithm flow to EVP_KDF_derive.
*/
module AlgorithmToEVPKeyDeriveConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof KeyDerivationAlgorithm }
predicate isSource(DataFlow::Node source) {
source.asExpr() = any(KeyDerivationAlgorithm a).getInstance()
}
predicate isSink(DataFlow::Node sink) {
exists(EVP_KDF_derive kdo | sink.asExpr() = kdo.getAlgorithmArg())
exists(EVP_KDF_derive kdo |
sink.asExpr() = kdo.getAlgorithmArg()
or
sink.asExpr() = kdo.getContextArg() // via `EVP_KDF_CTX_set_params`
)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
none() // TODO
}
}
module AlgorithmToEVPKeyDeriveFlow = DataFlow::Global<AlgorithmToEVPKeyDeriveConfig>;
predicate algorithm_to_EVP_KDF_derive(Crypto::Algorithm algo, EVP_KDF_derive derive) {
algo.(Expr).getEnclosingFunction() = derive.(Expr).getEnclosingFunction()
predicate algorithm_to_EVP_KDF_derive(KeyDerivationAlgorithm algo, EVP_KDF_derive derive) {
none()
}
class EVP_KDF_derive extends KeyDerivationOperation instanceof FunctionCall {
EVP_KDF_derive() { this.getTarget().getName() = "EVP_KDF_derive" }
/**
* Key derivation operation (e.g., `EVP_KDF_derive`)
*/
abstract class KeyDerivationOperation extends Crypto::KeyDerivationOperation { }
class EVP_KDF_derive extends KeyDerivationOperation {
FunctionCall instance;
EVP_KDF_derive() {
this = Crypto::TKeyDerivationOperation(instance) and
instance.getTarget().getName() = "EVP_KDF_derive"
}
override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) }
Expr getAlgorithmArg() { result = this.(FunctionCall).getArgument(3) }
Expr getAlgorithmArg() { result = instance.getArgument(3) }
Expr getContextArg() { result = instance.getArgument(0) }
}
abstract class KeyDerivationAlgorithm extends Crypto::KeyDerivationAlgorithm { }
/**
* Key derivation algorithm nodes
*/
abstract class KeyDerivationAlgorithm extends Crypto::KeyDerivationAlgorithm {
abstract Expr getInstance();
}
/**
* `EVP_KDF_fetch` returns a key derivation algorithm.
*/
class EVP_KDF_fetch_Call extends FunctionCall {
EVP_KDF_fetch_Call() { this.getTarget().getName() = "EVP_KDF_fetch" }
Expr getAlgorithmArg() { result = this.getArgument(1) }
}
predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF"] }
class EVP_KDF_fetch_AlgorithmArg extends Expr {
EVP_KDF_fetch_AlgorithmArg() { exists(EVP_KDF_fetch_Call call | this = call.getAlgorithmArg()) }
}
class KDFAlgorithmStringLiteral extends Crypto::NodeBase instanceof StringLiteral {
predicate kdf_names(string algo) { algo = ["HKDF", "PKCS12KDF", "PBKDF2"] }
class KDFAlgorithmStringLiteral extends StringLiteral {
KDFAlgorithmStringLiteral() { kdf_names(this.getValue().toUpperCase()) }
override string toString() { result = this.(StringLiteral).toString() }
string getValue() { result = this.(StringLiteral).getValue() }
}
private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KDFAlgorithmStringLiteral }
predicate isSink(DataFlow::Node sink) {
exists(EVP_KDF_fetch_Call call | sink.asExpr() = call.getAlgorithmArg())
}
predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof EVP_KDF_fetch_AlgorithmArg }
}
module AlgorithmStringToFetchFlow = DataFlow::Global<AlgorithmStringToFetchConfig>;
predicate algorithmStringToKDFFetchArgFlow(string name, KDFAlgorithmStringLiteral origin, Expr arg) {
exists(EVP_KDF_fetch_Call sinkCall |
origin.getValue().toUpperCase() = name and
arg = sinkCall.getAlgorithmArg() and
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg))
)
predicate algorithmStringToKDFFetchArgFlow(
string name, KDFAlgorithmStringLiteral origin, EVP_KDF_fetch_AlgorithmArg arg
) {
origin.getValue().toUpperCase() = name and
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg))
}
class HKDF extends KeyDerivationAlgorithm, Crypto::HKDF instanceof Expr {
/**
* HKDF key derivation algorithm.
*/
class HKDF extends KeyDerivationAlgorithm, Crypto::HKDF {
KDFAlgorithmStringLiteral origin;
EVP_KDF_fetch_AlgorithmArg instance;
HKDF() { algorithmStringToKDFFetchArgFlow("HKDF", origin, this) }
HKDF() {
this = Crypto::TKeyDerivationAlgorithm(instance) and
algorithmStringToKDFFetchArgFlow("HKDF", origin, instance)
}
override string getRawAlgorithmName() { result = origin.getValue() }
@@ -84,19 +178,61 @@ module OpenSSLModel {
override Crypto::LocatableElement getOrigin(string name) {
result = origin and name = origin.toString()
}
override Expr getInstance() { result = origin }
}
class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF instanceof Expr {
/**
* PBKDF2 key derivation algorithm.
*/
class PBKDF2 extends KeyDerivationAlgorithm, Crypto::PBKDF2 {
KDFAlgorithmStringLiteral origin;
EVP_KDF_fetch_AlgorithmArg instance;
PKCS12KDF() { algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, this) }
PBKDF2() {
this = Crypto::TKeyDerivationAlgorithm(instance) and
algorithmStringToKDFFetchArgFlow("PBKDF2", origin, instance)
}
override string getRawAlgorithmName() { result = origin.getValue() }
override Crypto::HashAlgorithm getHashAlgorithm() { none() }
override string getIterationCount(Location location) { none() } // TODO
override Crypto::NodeBase getOrigin(string name) {
override string getKeyLength(Location location) { none() } // TODO
override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO
override Crypto::LocatableElement getOrigin(string name) {
result = origin and name = origin.toString()
}
override Expr getInstance() { result = instance }
}
/**
* PKCS12KDF key derivation algorithm.
*/
class PKCS12KDF extends KeyDerivationAlgorithm, Crypto::PKCS12KDF {
KDFAlgorithmStringLiteral origin;
EVP_KDF_fetch_AlgorithmArg instance;
PKCS12KDF() {
this = Crypto::TKeyDerivationAlgorithm(instance) and
algorithmStringToKDFFetchArgFlow("PKCS12KDF", origin, instance)
}
override string getRawAlgorithmName() { result = origin.getValue() }
override string getIterationCount(Location location) { none() } // TODO
override string getIDByte(Location location) { none() } // TODO
override Crypto::HashAlgorithm getHashAlgorithm() { none() } // TODO
override Crypto::LocatableElement getOrigin(string name) {
result = origin and name = origin.toString()
}
override Expr getInstance() { result = instance }
}
}

View File

@@ -35,6 +35,19 @@ module JCAModel {
]
}
/**
* this may be specified either in the ALG/MODE/PADDING or just ALG format
*/
class CipherStringLiteral extends StringLiteral {
CipherStringLiteral() { cipher_names(this.getValue().splitAt("/")) }
string getAlgorithmName() { result = this.getValue().splitAt("/", 0) }
string getMode() { result = this.getValue().splitAt("/", 1) }
string getPadding() { result = this.getValue().splitAt("/", 2) }
}
class CipherGetInstanceCall extends Call {
CipherGetInstanceCall() {
this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance")
@@ -43,41 +56,48 @@ module JCAModel {
Expr getAlgorithmArg() { result = this.getArgument(0) }
}
/**
* this may be specified either in the ALG/MODE/PADDING or just ALG format
*/
class CipherAlgorithmStringLiteral extends StringLiteral {
CipherAlgorithmStringLiteral() { cipher_names(this.getValue().splitAt("/")) }
}
private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral }
class ModeOfOperationStringLiteral extends StringLiteral {
ModeOfOperationStringLiteral() { cipher_modes(this.(StringLiteral).getValue().splitAt("/")) }
string getRawAlgorithmName() { result = this.getValue().regexpCapture(".*/(.*)/.*", 1) }
}
class ECBMode extends Crypto::ModeOfOperation {
ModeOfOperationStringLiteral mode;
ECBMode() { modeStringToCipherInstanceArgFlow("ECB", mode, this) }
override string getRawAlgorithmName() { result = mode.getRawAlgorithmName() }
predicate modeToNameMapping(Crypto::TModeOperation type, string name) {
name = "ECB" and type instanceof Crypto::ECB
}
override Crypto::TModeOperation getModeType() {
this.modeToNameMapping(result, this.getRawAlgorithmName())
}
override Crypto::LocatableElement getOrigin(string name) {
result = mode and name = mode.toString()
predicate isSink(DataFlow::Node sink) {
exists(CipherGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg())
}
}
abstract class CipherAlgorithmPadding extends Crypto::NodeBase {
string getValue() { result = "" }
module AlgorithmStringToFetchFlow = DataFlow::Global<AlgorithmStringToFetchConfig>;
class CipherGetInstanceAlgorithmArg extends Expr {
CipherGetInstanceAlgorithmArg() {
exists(CipherGetInstanceCall call | this = call.getArgument(0))
}
StringLiteral getOrigin() {
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result), DataFlow::exprNode(this))
}
}
class ModeStringLiteral extends Crypto::ModeOfOperation {
CipherStringLiteral instance;
ModeStringLiteral() {
this = Crypto::TModeOfOperationAlgorithm(instance) and
exists(instance.getMode()) and
instance = any(CipherGetInstanceAlgorithmArg call).getOrigin()
}
override Location getLocation() { result = instance.getLocation() }
override string getRawAlgorithmName() { result = instance.getMode() }
predicate modeToNameMapping(Crypto::TModeOperationType type, string name) {
super.modeToNameMapping(type, name)
}
override Crypto::TModeOperationType getModeType() {
this.modeToNameMapping(result, instance.getMode().toUpperCase())
}
CipherStringLiteral getInstance() { result = instance }
}
//todo refactor
@@ -90,39 +110,6 @@ module JCAModel {
// result = this.(StringLiteral).getValue().regexpCapture(".*/.*/(.*)", 1)
// }
// }
private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherAlgorithmStringLiteral }
predicate isSink(DataFlow::Node sink) {
exists(CipherGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg())
}
}
module AlgorithmStringToFetchFlow = DataFlow::Global<AlgorithmStringToFetchConfig>;
predicate algorithmStringToCipherInstanceArgFlow(
string name, CipherAlgorithmStringLiteral origin, Expr arg
) {
exists(CipherGetInstanceCall sinkCall |
origin.getValue().splitAt("/") = name and
arg = sinkCall and
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin),
DataFlow::exprNode(sinkCall.getAlgorithmArg()))
)
}
predicate modeStringToCipherInstanceArgFlow(
string name, ModeOfOperationStringLiteral mode, Expr arg
) {
exists(CipherGetInstanceCall sinkCall |
//consider if this should be a more specific predicate
mode.getRawAlgorithmName() = name and
arg = sinkCall and
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(mode),
DataFlow::exprNode(sinkCall.getAlgorithmArg()))
)
}
/**
* A class to represent when AES is used
* AND currently it has literal mode and padding provided
@@ -130,32 +117,48 @@ module JCAModel {
* this currently does not capture the use without a literal
* though should be extended to
*/
class AESAlgo extends Crypto::SymmetricAlgorithm instanceof Expr {
CipherAlgorithmStringLiteral alg;
class CipherAlgorithm extends Crypto::SymmetricAlgorithm {
CipherStringLiteral origin;
CipherGetInstanceAlgorithmArg instance;
AESAlgo() { algorithmStringToCipherInstanceArgFlow("AES", alg, this) }
CipherAlgorithm() {
this = Crypto::TSymmetricAlgorithm(instance) and
instance.getOrigin() = origin
}
override Location getLocation() { result = instance.getLocation() }
//todo this is really not correct yet
override Crypto::ModeOfOperation getModeOfOperation() {
none()
//exists(Crypto::ModeOfOperation mode | mode = this and result = this)
result.(ModeStringLiteral).getInstance() = origin
}
override Crypto::LocatableElement getOrigin(string name) {
result = alg and name = alg.toString()
result = origin and name = origin.toString()
}
override string getAlgorithmName() { result = "AES" }
override string getRawAlgorithmName() { result = origin.getValue() }
override string getRawAlgorithmName() { result = alg.getValue() }
override Crypto::TSymmetricCipherFamilyType getSymmetricCipherFamilyType() {
result instanceof Crypto::AES
override Crypto::TSymmetricCipherType getCipherFamily() {
this.cipherNameMapping(result, origin.getAlgorithmName())
}
//temp hacks for testing
override string getKeySize(Location location) { result = "" }
override string getKeySize(Location location) { none() }
override Crypto::TCipherStructure getCipherType() { none() }
bindingset[name]
private predicate cipherNameMappingKnown(Crypto::TSymmetricCipherType type, string name) {
name = "AES" and
type instanceof Crypto::AES
or
name = "RC4" and
type instanceof Crypto::RC4
}
bindingset[name]
predicate cipherNameMapping(Crypto::TSymmetricCipherType type, string name) {
this.cipherNameMappingKnown(type, name)
or
not this.cipherNameMappingKnown(_, name) and
type instanceof Crypto::OtherSymmetricCipherType
}
}
}

View File

@@ -2,7 +2,8 @@
* @name "PQC Test"
*/
import experimental.Quantum.Language
import experimental.Quantum.Language
from JCAModel::AESLiteral l
select l, l.getAlg(), l.getMode().getValue(), l.getPadding().getValue()
from Crypto::SymmetricAlgorithm a, Crypto::ModeOfOperation mode
where a.getModeOfOperation() = mode
select a, a.getAlgorithmName(), a.getRawAlgorithmName(), mode, mode.getAlgorithmName()

View File

@@ -56,12 +56,22 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
not source = target
}
newtype TNode =
THashOperation(LocatableElement e) or
THashAlgorithm(LocatableElement e) or
TKeyDerivationOperation(LocatableElement e) or
TKeyDerivationAlgorithm(LocatableElement e) or
TEncryptionOperation(LocatableElement e) or
TSymmetricAlgorithm(LocatableElement e) or
TEllipticCurveAlgorithm(LocatableElement e) or
TModeOfOperationAlgorithm(LocatableElement e)
/**
* The base class for all cryptographic assets, such as operations and algorithms.
*
* Each `NodeBase` is a node in a graph of cryptographic operations, where the edges are the relationships between the nodes.
*/
abstract class NodeBase instanceof LocatableElement {
abstract class NodeBase extends TNode {
/**
* Returns a string representation of this node, usually the name of the operation/algorithm/property.
*/
@@ -70,7 +80,7 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
/**
* Returns the location of this node in the code.
*/
Location getLocation() { result = super.getLocation() }
abstract Location getLocation();
/**
* Gets the origin of this node, e.g., a string literal in source describing it.
@@ -128,6 +138,8 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
}
abstract class Algorithm extends Asset {
final override string toString() { result = this.getAlgorithmType() }
/**
* Gets the name of this algorithm, e.g., "AES" or "SHA".
*/
@@ -138,7 +150,20 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
*/
abstract string getRawAlgorithmName();
final override string toString() { result = this.getAlgorithmName() }
/**
* Gets the type of this algorithm, e.g., "hash" or "key derivation".
*/
abstract string getAlgorithmType();
override predicate properties(string key, string value, Location location) {
super.properties(key, value, location)
or
// [ONLY_KNOWN]
key = "name" and value = this.getAlgorithmName() and location = this.getLocation()
or
// [ONLY_KNOWN]
key = "raw_name" and value = this.getRawAlgorithmName() and location = this.getLocation()
}
}
/**
@@ -147,81 +172,318 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
* This operation takes an input message of arbitrary content and length and produces a fixed-size
* hash value as the output using a specified hashing algorithm.
*/
abstract class HashOperation extends Operation {
abstract class HashOperation extends Operation, THashOperation {
abstract override HashAlgorithm getAlgorithm();
override string getOperationName() { result = "HASH" }
override string getOperationName() { result = "HashOperation" }
}
newtype THashType =
MD2() or
MD4() or
MD5() or
SHA1() or
SHA256() or
SHA512() or
SHA2() or
SHA3() or
RIPEMD160() or
WHIRLPOOL() or
OtherHashType()
/**
* A hashing algorithm that transforms variable-length input into a fixed-size hash value.
*/
abstract class HashAlgorithm extends Algorithm {
abstract class HashAlgorithm extends Algorithm, THashAlgorithm {
override string getAlgorithmType() { result = "HashAlgorithm" }
final predicate hashTypeToNameMapping(THashType type, string name) {
type instanceof MD2 and name = "MD2"
or
type instanceof MD4 and name = "MD4"
or
type instanceof MD5 and name = "MD5"
or
type instanceof SHA1 and name = "SHA-1"
type instanceof SHA1 and name = "SHA1"
or
type instanceof SHA256 and name = "SHA-256"
type instanceof SHA2 and name = "SHA2"
or
type instanceof SHA512 and name = "SHA-512"
type instanceof SHA3 and name = "SHA3"
or
type instanceof RIPEMD160 and name = "RIPEMD160"
or
type instanceof WHIRLPOOL and name = "WHIRLPOOL"
or
type instanceof OtherHashType and name = this.getRawAlgorithmName()
}
/**
* Gets the type of this hashing algorithm, e.g., MD5 or SHA.
*
* When modeling a new hashing algorithm, use this predicate to specify the type of the algorithm.
*/
abstract THashType getHashType();
override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) }
/**
* Gets the digest size of SHA2 or SHA3 algorithms.
*
* This predicate does not need to hold for other algorithms,
* as the digest size is already known based on the algorithm itself.
*
* For `OtherHashType` algorithms where a digest size should be reported, `THashType`
* should be extended to explicitly model that algorithm. If the algorithm has variable
* or multiple digest size variants, a similar predicate to this one must be defined
* for that algorithm to report the digest size.
*/
abstract string getSHA2OrSHA3DigestSize(Location location);
bindingset[type]
private string getDigestSize(THashType type, Location location) {
type instanceof MD2 and result = "128"
or
type instanceof MD4 and result = "128"
or
type instanceof MD5 and result = "128"
or
type instanceof SHA1 and result = "160"
or
type instanceof SHA2 and result = this.getSHA2OrSHA3DigestSize(location)
or
type instanceof SHA3 and result = this.getSHA2OrSHA3DigestSize(location)
or
type instanceof RIPEMD160 and result = "160"
or
type instanceof WHIRLPOOL and result = "512"
}
final override predicate properties(string key, string value, Location location) {
super.properties(key, value, location)
or
// [KNOWN_OR_UNKNOWN]
key = "digest_size" and
if exists(this.getDigestSize(this.getHashType(), location))
then value = this.getDigestSize(this.getHashType(), location)
else (
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
)
}
}
/**
* An operation that derives one or more keys from an input value.
*/
abstract class KeyDerivationOperation extends Operation {
override string getOperationName() { result = "KEY_DERIVATION" }
abstract class KeyDerivationOperation extends Operation, TKeyDerivationOperation {
final override Location getLocation() {
exists(LocatableElement le | this = TKeyDerivationOperation(le) and result = le.getLocation())
}
override string getOperationName() { result = "KeyDerivationOperation" }
}
/**
* An algorithm that derives one or more keys from an input value.
*
* Only use this class to model UNKNOWN key derivation algorithms.
*
* For known algorithms, use the specialized classes, e.g., `HKDF` and `PKCS12KDF`.
*/
abstract class KeyDerivationAlgorithm extends Algorithm {
abstract override string getAlgorithmName();
abstract class KeyDerivationAlgorithm extends Algorithm, TKeyDerivationAlgorithm {
final override Location getLocation() {
exists(LocatableElement le | this = TKeyDerivationAlgorithm(le) and result = le.getLocation())
}
override string getAlgorithmType() { result = "KeyDerivationAlgorithm" }
override string getAlgorithmName() { result = this.getRawAlgorithmName() }
}
/**
* An algorithm that derives one or more keys from an input value, using a configurable digest algorithm.
*/
abstract private class KeyDerivationWithDigestParameter extends KeyDerivationAlgorithm {
abstract HashAlgorithm getHashAlgorithm();
override NodeBase getChild(string edgeName) {
result = super.getChild(edgeName)
or
(
// [KNOWN_OR_UNKNOWN]
edgeName = "uses" and
if exists(this.getHashAlgorithm()) then result = this.getHashAlgorithm() else result = this
)
}
}
/**
* HKDF key derivation function
*/
abstract class HKDF extends KeyDerivationAlgorithm {
abstract class HKDF extends KeyDerivationWithDigestParameter {
final override string getAlgorithmName() { result = "HKDF" }
}
abstract HashAlgorithm getHashAlgorithm();
/**
* PBKDF2 key derivation function
*/
abstract class PBKDF2 extends KeyDerivationWithDigestParameter {
final override string getAlgorithmName() { result = "PBKDF2" }
override NodeBase getChild(string edgeName) {
result = super.getChild(edgeName)
/**
* Gets the iteration count of this key derivation algorithm.
*/
abstract string getIterationCount(Location location);
/**
* Gets the bit-length of the derived key.
*/
abstract string getKeyLength(Location location);
override predicate properties(string key, string value, Location location) {
super.properties(key, value, location)
or
edgeName = "digest" and result = this.getHashAlgorithm()
(
// [KNOWN_OR_UNKNOWN]
key = "iterations" and
if exists(this.getIterationCount(location))
then value = this.getIterationCount(location)
else (
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
)
)
or
(
// [KNOWN_OR_UNKNOWN]
key = "key_len" and
if exists(this.getKeyLength(location))
then value = this.getKeyLength(location)
else (
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
)
)
}
}
/**
* PKCS #12 key derivation function
* PKCS12KDF key derivation function
*/
abstract class PKCS12KDF extends KeyDerivationAlgorithm {
final override string getAlgorithmName() { result = "PKCS12KDF" }
abstract class PKCS12KDF extends KeyDerivationWithDigestParameter {
override string getAlgorithmName() { result = "PKCS12KDF" }
abstract HashAlgorithm getHashAlgorithm();
/**
* Gets the iteration count of this key derivation algorithm.
*/
abstract string getIterationCount(Location location);
override NodeBase getChild(string edgeName) {
result = super.getChild(edgeName)
/**
* Gets the raw ID argument specifying the intended use of the derived key.
*
* The intended use is defined in RFC 7292, appendix B.3, as follows:
*
* This standard specifies 3 different values for the ID byte mentioned above:
*
* 1. If ID=1, then the pseudorandom bits being produced are to be used
* as key material for performing encryption or decryption.
*
* 2. If ID=2, then the pseudorandom bits being produced are to be used
* as an IV (Initial Value) for encryption or decryption.
*
* 3. If ID=3, then the pseudorandom bits being produced are to be used
* as an integrity key for MACing.
*/
abstract string getIDByte(Location location);
override predicate properties(string key, string value, Location location) {
super.properties(key, value, location)
or
edgeName = "digest" and result = this.getHashAlgorithm()
(
// [KNOWN_OR_UNKNOWN]
key = "iterations" and
if exists(this.getIterationCount(location))
then value = this.getIterationCount(location)
else (
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
)
)
or
(
// [KNOWN_OR_UNKNOWN]
key = "id_byte" and
if exists(this.getIDByte(location))
then value = this.getIDByte(location)
else (
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
)
)
}
}
/**
* scrypt key derivation function
*/
abstract class SCRYPT extends KeyDerivationAlgorithm {
final override string getAlgorithmName() { result = "scrypt" }
/**
* Gets the iteration count (`N`) argument
*/
abstract string get_N(Location location);
/**
* Gets the block size (`r`) argument
*/
abstract string get_r(Location location);
/**
* Gets the parallelization factor (`p`) argument
*/
abstract string get_p(Location location);
/**
* Gets the derived key length argument
*/
abstract string getDerivedKeyLength(Location location);
override predicate properties(string key, string value, Location location) {
super.properties(key, value, location)
or
(
// [KNOWN_OR_UNKNOWN]
key = "N" and
if exists(this.get_N(location))
then value = this.get_N(location)
else (
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
)
)
or
(
// [KNOWN_OR_UNKNOWN]
key = "r" and
if exists(this.get_r(location))
then value = this.get_r(location)
else (
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
)
)
or
(
// [KNOWN_OR_UNKNOWN]
key = "p" and
if exists(this.get_p(location))
then value = this.get_p(location)
else (
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
)
)
or
(
// [KNOWN_OR_UNKNOWN]
key = "key_len" and
if exists(this.getDerivedKeyLength(location))
then value = this.getDerivedKeyLength(location)
else (
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
)
)
}
}
@@ -246,7 +508,7 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
/**
* Elliptic curve algorithms
*/
newtype TEllipticCurveFamily =
newtype TEllipticCurveType =
NIST() or
SEC() or
NUMS() or
@@ -257,16 +519,17 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
C2() or
SM2() or
ES() or
OtherEllipticCurveFamilyType()
OtherEllipticCurveType()
abstract class EllipticCurve extends Algorithm {
abstract class EllipticCurve extends Algorithm, TEllipticCurveAlgorithm {
abstract string getKeySize(Location location);
abstract TEllipticCurveFamily getCurveFamilyType();
abstract TEllipticCurveType getCurveFamily();
override predicate properties(string key, string value, Location location) {
super.properties(key, value, location)
or
// [KNOWN_OR_UNKNOWN]
key = "key_size" and
if exists(this.getKeySize(location))
then value = this.getKeySize(location)
@@ -306,62 +569,168 @@ module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
/**
* Block cipher modes of operation algorithms
*/
newtype TModeOperation =
newtype TModeOperationType =
ECB() or
CBC() or
CFB() or
OFB() or
CTR() or
GCM() or
CCM() or
XTS() or
OtherMode()
abstract class ModeOfOperation extends Algorithm {
final private predicate modeToNameMapping(TModeOperation type, string name) {
override string getAlgorithmType() { result = "ModeOfOperation" }
/**
* Gets the type of this mode of operation, e.g., "ECB" or "CBC".
*
* When modeling a new mode of operation, use this predicate to specify the type of the mode.
*/
abstract TModeOperationType getModeType();
bindingset[type]
final predicate modeToNameMapping(TModeOperationType type, string name) {
type instanceof ECB and name = "ECB"
or
type instanceof CBC and name = "CBC"
or
type instanceof CFB and name = "CFB"
or
type instanceof OFB and name = "OFB"
or
type instanceof CTR and name = "CTR"
or
type instanceof GCM and name = "GCM"
or
type instanceof CCM and name = "CCM"
or
type instanceof XTS and name = "XTS"
or
type instanceof OtherMode and name = this.getRawAlgorithmName()
}
abstract TModeOperation getModeType();
override string getAlgorithmName() { this.modeToNameMapping(this.getModeType(), result) }
}
/**
* A helper type for distinguishing between block and stream ciphers.
*/
newtype TCipherStructure =
newtype TCipherStructureType =
Block() or
Stream()
Stream() or
UnknownCipherStructureType()
private string getCipherStructureTypeString(TCipherStructureType type) {
type instanceof Block and result = "Block"
or
type instanceof Stream and result = "Stream"
or
type instanceof UnknownCipherStructureType and result instanceof UnknownPropertyValue
}
/**
* Symmetric algorithms
*/
newtype TSymmetricCipherFamilyType =
newtype TSymmetricCipherType =
AES() or
OtherSymmetricCipherFamilyType()
Camellia() or
DES() or
TripleDES() or
IDEA() or
CAST5() or
ChaCha20() or
RC4() or
RC5() or
OtherSymmetricCipherType()
abstract class SymmetricAlgorithm extends Algorithm {
abstract TSymmetricCipherFamilyType getSymmetricCipherFamilyType();
final TCipherStructureType getCipherStructure() {
this.cipherFamilyToNameAndStructure(this.getCipherFamily(), _, result)
}
final override string getAlgorithmName() {
this.cipherFamilyToNameAndStructure(this.getCipherFamily(), result, _)
}
final override string getAlgorithmType() { result = "SymmetricAlgorithm" }
/**
* Gets the key size of this symmetric cipher, e.g., "128" or "256".
*/
abstract string getKeySize(Location location);
abstract TCipherStructure getCipherType();
/**
* Gets the type of this symmetric cipher, e.g., "AES" or "ChaCha20".
*/
abstract TSymmetricCipherType getCipherFamily();
/**
* Gets the mode of operation of this symmetric cipher, e.g., "GCM" or "CBC".
*/
abstract ModeOfOperation getModeOfOperation();
bindingset[type]
final private predicate cipherFamilyToNameAndStructure(
TSymmetricCipherType type, string name, TCipherStructureType s
) {
type instanceof AES and name = "AES" and s = Block()
or
type instanceof Camellia and name = "Camellia" and s = Block()
or
type instanceof DES and name = "DES" and s = Block()
or
type instanceof TripleDES and name = "TripleDES" and s = Block()
or
type instanceof IDEA and name = "IDEA" and s = Block()
or
type instanceof CAST5 and name = "CAST5" and s = Block()
or
type instanceof ChaCha20 and name = "ChaCha20" and s = Stream()
or
type instanceof RC4 and name = "RC4" and s = Stream()
or
type instanceof RC5 and name = "RC5" and s = Block()
or
type instanceof OtherSymmetricCipherType and
name = this.getRawAlgorithmName() and
s = UnknownCipherStructureType()
}
//mode, padding scheme, keysize, block/stream, auth'd
//nodes = mode, padding scheme
//properties = keysize, block/stream, auth'd
//leave authd to lang specific
override NodeBase getChild(string edgeName) {
result = super.getChild(edgeName)
or
(
// [KNOWN_OR_UNKNOWN]
edgeName = "mode" and
if exists(this.getModeOfOperation())
then result = this.getModeOfOperation()
else result = this
)
}
override predicate properties(string key, string value, Location location) {
super.properties(key, value, location)
or
key = "key_size" and
if exists(this.getKeySize(location))
then value = this.getKeySize(location)
else (
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
// [ALWAYS_KNOWN]: unknown case is handled in `getCipherStructureTypeString`
key = "structure" and
getCipherStructureTypeString(this.getCipherStructure()) = value and
location instanceof UnknownLocation
or
(
// [KNOWN_OR_UNKNOWN]
key = "key_size" and
if exists(this.getKeySize(location))
then value = this.getKeySize(location)
else (
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
)
)
//add more keys to index props
}
abstract ModeOfOperation getModeOfOperation();
}
}