mirror of
https://github.com/github/codeql.git
synced 2026-04-23 07:45:17 +02:00
Modify model to use newtypes, expand modeling
This commit is contained in:
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user