mirror of
https://github.com/github/codeql.git
synced 2026-04-26 01:05:15 +02:00
Add first sample JCA encryption model
This commit is contained in:
235
java/ql/lib/experimental/Quantum/Base.qll
Normal file
235
java/ql/lib/experimental/Quantum/Base.qll
Normal file
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* A language-independent library for reasoning about cryptography.
|
||||
*/
|
||||
|
||||
import codeql.util.Location
|
||||
import codeql.util.Option
|
||||
|
||||
signature module InputSig<LocationSig Location> {
|
||||
class LocatableElement {
|
||||
Location getLocation();
|
||||
}
|
||||
|
||||
class UnknownLocation instanceof Location;
|
||||
}
|
||||
|
||||
module CryptographyBase<LocationSig Location, InputSig<Location> Input> {
|
||||
final class LocatableElement = Input::LocatableElement;
|
||||
|
||||
final class UnknownLocation = Input::UnknownLocation;
|
||||
|
||||
final class UnknownPropertyValue extends string {
|
||||
UnknownPropertyValue() { this = "<unknown>" }
|
||||
}
|
||||
|
||||
abstract class NodeBase instanceof LocatableElement {
|
||||
/**
|
||||
* Returns a string representation of this node, usually the name of the operation/algorithm/property.
|
||||
*/
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Returns the location of this node in the code.
|
||||
*/
|
||||
Location getLocation() { result = super.getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the origin of this node, e.g., a string literal in source describing it.
|
||||
*/
|
||||
LocatableElement getOrigin(string value) { none() }
|
||||
|
||||
/**
|
||||
* Returns the child of this node with the given edge name.
|
||||
*
|
||||
* This predicate is used by derived classes to construct the graph of cryptographic operations.
|
||||
*/
|
||||
NodeBase getChild(string edgeName) { none() }
|
||||
|
||||
/**
|
||||
* Defines properties of this node by name and either a value or location or both.
|
||||
*
|
||||
* This predicate is used by derived classes to construct the graph of cryptographic operations.
|
||||
*/
|
||||
predicate properties(string key, string value, Location location) {
|
||||
key = "origin" and location = this.getOrigin(value).getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent of this node.
|
||||
*/
|
||||
final NodeBase getAParent() { result.getChild(_) = this }
|
||||
}
|
||||
|
||||
class Asset = NodeBase;
|
||||
|
||||
/**
|
||||
* A cryptographic operation, such as hashing or encryption.
|
||||
*/
|
||||
abstract class Operation extends Asset {
|
||||
/**
|
||||
* Gets the algorithm associated with this operation.
|
||||
*/
|
||||
abstract Algorithm getAlgorithm();
|
||||
|
||||
/**
|
||||
* Gets the name of this operation, e.g., "hash" or "encrypt".
|
||||
*/
|
||||
abstract string getOperationName();
|
||||
|
||||
final override string toString() { result = this.getOperationName() }
|
||||
|
||||
override NodeBase getChild(string edgeName) {
|
||||
result = super.getChild(edgeName)
|
||||
or
|
||||
edgeName = "uses" and
|
||||
if exists(this.getAlgorithm()) then result = this.getAlgorithm() else result = this
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Algorithm extends Asset {
|
||||
/**
|
||||
* Gets the name of this algorithm, e.g., "AES" or "SHA".
|
||||
*/
|
||||
abstract string getAlgorithmName();
|
||||
|
||||
final override string toString() { result = this.getAlgorithmName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A hashing operation that processes data to generate a hash value.
|
||||
* 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 override HashAlgorithm getAlgorithm();
|
||||
|
||||
override string getOperationName() { result = "HASH" }
|
||||
}
|
||||
|
||||
// Rule: no newtype representing a type of algorithm should be modelled with multiple interfaces
|
||||
//
|
||||
// Example: HKDF and PKCS12KDF are both key derivation algorithms.
|
||||
// However, PKCS12KDF also has a property: the iteration count.
|
||||
//
|
||||
// If we have HKDF and PKCS12KDF under TKeyDerivationType,
|
||||
// someone modelling a library might try to make a generic identification of both of those algorithms.
|
||||
//
|
||||
// They will therefore not use the specialized type for PKCS12KDF,
|
||||
// meaning "from PKCS12KDF algo select algo" will have no results.
|
||||
//
|
||||
newtype THashType =
|
||||
// We're saying by this that all of these have an identical interface / properties / edges
|
||||
MD5() or
|
||||
SHA1() or
|
||||
SHA256() or
|
||||
SHA512() or
|
||||
OtherHashType()
|
||||
|
||||
/**
|
||||
* A hashing algorithm that transforms variable-length input into a fixed-size hash value.
|
||||
*/
|
||||
abstract class HashAlgorithm extends Algorithm {
|
||||
final predicate hashTypeToNameMapping(THashType type, string name) {
|
||||
type instanceof MD5 and name = "MD5"
|
||||
or
|
||||
type instanceof SHA1 and name = "SHA-1"
|
||||
or
|
||||
type instanceof SHA256 and name = "SHA-256"
|
||||
or
|
||||
type instanceof SHA512 and name = "SHA-512"
|
||||
or
|
||||
type instanceof OtherHashType and name = this.getRawAlgorithmName()
|
||||
}
|
||||
|
||||
abstract THashType getHashType();
|
||||
|
||||
override string getAlgorithmName() { this.hashTypeToNameMapping(this.getHashType(), result) }
|
||||
|
||||
/**
|
||||
* Gets the raw name of this hash algorithm from source.
|
||||
*/
|
||||
abstract string getRawAlgorithmName();
|
||||
}
|
||||
|
||||
/**
|
||||
* An operation that derives one or more keys from an input value.
|
||||
*/
|
||||
abstract class KeyDerivationOperation extends Operation {
|
||||
override string getOperationName() { result = "KEY_DERIVATION" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An algorithm that derives one or more keys from an input value.
|
||||
*/
|
||||
abstract class KeyDerivationAlgorithm extends Algorithm {
|
||||
abstract override string getAlgorithmName();
|
||||
}
|
||||
|
||||
/**
|
||||
* HKDF key derivation function
|
||||
*/
|
||||
abstract class HKDF extends KeyDerivationAlgorithm {
|
||||
final override string getAlgorithmName() { result = "HKDF" }
|
||||
|
||||
abstract HashAlgorithm getHashAlgorithm();
|
||||
|
||||
override NodeBase getChild(string edgeName) {
|
||||
result = super.getChild(edgeName)
|
||||
or
|
||||
edgeName = "digest" and result = this.getHashAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PKCS #12 key derivation function
|
||||
*/
|
||||
abstract class PKCS12KDF extends KeyDerivationAlgorithm {
|
||||
final override string getAlgorithmName() { result = "PKCS12KDF" }
|
||||
|
||||
abstract HashAlgorithm getHashAlgorithm();
|
||||
|
||||
override NodeBase getChild(string edgeName) {
|
||||
result = super.getChild(edgeName)
|
||||
or
|
||||
edgeName = "digest" and result = this.getHashAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Elliptic curve algorithm
|
||||
*/
|
||||
abstract class EllipticCurve extends Algorithm {
|
||||
abstract string getVersion(Location location);
|
||||
|
||||
abstract string getKeySize(Location location);
|
||||
|
||||
override predicate properties(string key, string value, Location location) {
|
||||
super.properties(key, value, location)
|
||||
or
|
||||
key = "version" and
|
||||
if exists(this.getVersion(location))
|
||||
then value = this.getVersion(location)
|
||||
else (
|
||||
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
|
||||
)
|
||||
or
|
||||
key = "key_size" and
|
||||
if exists(this.getKeySize(location))
|
||||
then value = this.getKeySize(location)
|
||||
else (
|
||||
value instanceof UnknownPropertyValue and location instanceof UnknownLocation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An encryption operation that processes plaintext to generate a ciphertext.
|
||||
* This operation takes an input message (plaintext) of arbitrary content and length and produces a ciphertext as the output using a specified encryption algorithm (with a mode and padding).
|
||||
*/
|
||||
abstract class EncryptionOperation extends Operation {
|
||||
abstract override Algorithm getAlgorithm();
|
||||
|
||||
override string getOperationName() { result = "ENCRYPTION" }
|
||||
}
|
||||
}
|
||||
|
||||
105
java/ql/lib/experimental/Quantum/JCA.qll
Normal file
105
java/ql/lib/experimental/Quantum/JCA.qll
Normal file
@@ -0,0 +1,105 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
module JCAModel {
|
||||
import Language
|
||||
|
||||
abstract class EncryptionOperation extends Crypto::EncryptionOperation { }
|
||||
|
||||
//TODO PBEWith can have suffixes. how to do? enumerate? or match a pattern?
|
||||
predicate cipher_names(string algo) { algo = ["AES", "AESWrap", "AESWrapPad", "ARCFOUR", "Blowfish", "ChaCha20", "ChaCha20-Poly1305", "DES", "DESede", "DESedeWrap", "ECIES", "PBEWith", "RC2", "RC4", "RC5", "RSA"] }
|
||||
//TODO solve the fact that x is an int of various values. same as above... enumerate?
|
||||
predicate cipher_modes(string mode) {mode = ["NONE", "CBC", "CCM", "CFB", "CFBx", "CTR", "CTS", "ECB", "GCM", "KW", "KWP", "OFB", "OFBx", "PCBC"]}
|
||||
//todo same as above, OAEPWith has asuffix type
|
||||
predicate cipher_padding(string padding) {padding = ["NoPadding", "ISO10126Padding", "OAEPPadding", "OAEPWith", "PKCS1Padding", "PKCS5Padding", "SSL3Padding"]}
|
||||
|
||||
/**
|
||||
* Symmetric algorithms
|
||||
*/
|
||||
abstract class SymmetricAlgorithm extends Crypto::Algorithm {
|
||||
|
||||
|
||||
//TODO figure out how to get this from the Cipher interface, is it explicit?
|
||||
//abstract string getKeySize(Location location);
|
||||
|
||||
// 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 Crypto::UnknownPropertyValue and location instanceof UnknownLocation
|
||||
// )
|
||||
// // other properties, like field type are possible, but not modeled until considered necessary
|
||||
// }
|
||||
|
||||
abstract override string getAlgorithmName();
|
||||
}
|
||||
|
||||
////cipher specifics ----------------------------------------
|
||||
|
||||
class CipherInstance extends Call {
|
||||
CipherInstance() { this.getCallee().hasQualifiedName("javax.crypto","Cipher", "getInstance") }
|
||||
|
||||
Expr getAlgorithmArg() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
class CipherAlgorithmStringLiteral extends Crypto::NodeBase instanceof StringLiteral {
|
||||
CipherAlgorithmStringLiteral() { cipher_names(this.getValue().splitAt("/"))}
|
||||
|
||||
override string toString() { result = this.(StringLiteral).toString() }
|
||||
|
||||
string getValue() { result = this.(StringLiteral).getValue() }
|
||||
}
|
||||
|
||||
class CipherAlgorithmModeStringLiteral extends Crypto::NodeBase instanceof StringLiteral {
|
||||
CipherAlgorithmModeStringLiteral() { cipher_modes(this.getValue().splitAt("/"))}
|
||||
|
||||
override string toString() { result = this.(StringLiteral).toString() }
|
||||
|
||||
string getValue() { result = this.(StringLiteral).getValue() }
|
||||
}
|
||||
|
||||
class CipherAlgorithmPaddingStringLiteral extends Crypto::NodeBase instanceof StringLiteral {
|
||||
CipherAlgorithmPaddingStringLiteral() { cipher_padding(this.getValue().splitAt("/"))}
|
||||
|
||||
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 CipherAlgorithmStringLiteral }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(CipherInstance call | sink.asExpr() = call.getAlgorithmArg())
|
||||
}
|
||||
}
|
||||
|
||||
module AlgorithmStringToFetchFlow = DataFlow::Global<AlgorithmStringToFetchConfig>;
|
||||
|
||||
predicate algorithmStringToCipherInstanceArgFlow(string name, CipherAlgorithmStringLiteral origin, Expr arg) {
|
||||
exists(CipherInstance sinkCall |
|
||||
origin.getValue().toUpperCase() = name and
|
||||
arg = sinkCall.getAlgorithmArg() and
|
||||
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(origin), DataFlow::exprNode(arg))
|
||||
)
|
||||
}
|
||||
|
||||
class AES extends SymmetricAlgorithm instanceof Expr {
|
||||
CipherAlgorithmStringLiteral origin;
|
||||
|
||||
AES() { algorithmStringToCipherInstanceArgFlow("AES", origin, this) }
|
||||
|
||||
override Crypto::LocatableElement getOrigin(string name) {
|
||||
result = origin and name = origin.toString()
|
||||
}
|
||||
|
||||
override string getAlgorithmName(){ result = "AES"}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
30
java/ql/lib/experimental/Quantum/Language.qll
Normal file
30
java/ql/lib/experimental/Quantum/Language.qll
Normal file
@@ -0,0 +1,30 @@
|
||||
private import Base
|
||||
private import java as Lang
|
||||
|
||||
/**
|
||||
* A dummy location which is used when something doesn't have a location in
|
||||
* the source code but needs to have a `Location` associated with it. There
|
||||
* may be several distinct kinds of unknown locations. For example: one for
|
||||
* expressions, one for statements and one for other program elements.
|
||||
*/
|
||||
class UnknownLocation extends Location {
|
||||
UnknownLocation() { this.getFile().getAbsolutePath() = "" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy location which is used when something doesn't have a location in
|
||||
* the source code but needs to have a `Location` associated with it.
|
||||
*/
|
||||
class UnknownDefaultLocation extends UnknownLocation {
|
||||
UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) }
|
||||
}
|
||||
|
||||
module CryptoInput implements InputSig<Lang::Location> {
|
||||
class LocatableElement = Lang::Element;
|
||||
|
||||
class UnknownLocation = UnknownDefaultLocation;
|
||||
}
|
||||
|
||||
module Crypto = CryptographyBase<Lang::Location, CryptoInput>;
|
||||
|
||||
import JCA
|
||||
13
java/ql/src/experimental/Quantum/Test.ql
Normal file
13
java/ql/src/experimental/Quantum/Test.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @name "PQC Test"
|
||||
*/
|
||||
|
||||
import experimental.Quantum.Language
|
||||
//import java
|
||||
|
||||
from Crypto::NodeBase node
|
||||
select node
|
||||
|
||||
// from Class t
|
||||
// where t.hasQualifiedName("javax.crypto", "CipherSpi")
|
||||
// select t, t.getADescendant*()
|
||||
Reference in New Issue
Block a user