overlay[local?] module; import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking import semmle.code.java.controlflow.Dominance module JCAModel { import Language import codeql.quantum.experimental.Standardization::Types::KeyOpAlg as KeyOpAlg abstract class CipherAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } abstract class EllipticCurveAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } abstract class HashAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } abstract class KeyAgreementAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { } // TODO: Verify that the PBEWith% case works correctly bindingset[algo] predicate cipher_names(string algo) { algo.toUpperCase() .matches([ "AES", "AESWrap", "AESWrapPad", "ARCFOUR", "Blowfish", "ChaCha20", "ChaCha20-Poly1305", "DES", "DESede", "DESedeWrap", "ECIES", "PBEWith%", "RC2", "RC4", "RC5", "RSA" ].toUpperCase()) } // TODO: Verify that the CFB% case works correctly bindingset[mode] predicate cipher_modes(string mode) { mode.toUpperCase() .matches([ "NONE", "CBC", "CCM", "CFB", "CFB%", "CTR", "CTS", "ECB", "GCM", "KW", "KWP", "OFB", "OFB%", "PCBC" ].toUpperCase()) } // TODO: Verify that the OAEPWith% case works correctly bindingset[padding] predicate cipher_padding(string padding) { padding .toUpperCase() .matches([ "NoPadding", "ISO10126Padding", "OAEPPadding", "OAEPWith%", "PKCS1Padding", "PKCS5Padding", "SSL3Padding" ].toUpperCase()) } bindingset[hash] predicate hash_names(string hash) { hash.toUpperCase() .matches(["SHA-%", "SHA3-%", "BLAKE2b%", "BLAKE2s%", "MD5", "RIPEMD160", "Whirlpool"] .toUpperCase()) } bindingset[kdf] predicate kdf_names(string kdf) { kdf.toUpperCase().matches(["PBKDF2With%", "PBEWith%"].toUpperCase()) } bindingset[name] predicate elliptic_curve_names(string name) { // Note: as a one-off exception, we use the internal Crypto module implementation of `isEllipticCurveAlgorithmName` Crypto::isEllipticCurveAlgorithmName(name) } /* * TODO: * * MAC Algorithms possible (JCA Default + BouncyCastle Extensions) * * Name Type Description * --------------------------------------------------------------------------- * "HmacMD5" HMAC HMAC with MD5 (not recommended) * "HmacSHA1" HMAC HMAC with SHA-1 (not recommended) * "HmacSHA224" HMAC HMAC with SHA-224 * "HmacSHA256" HMAC HMAC with SHA-256 * "HmacSHA384" HMAC HMAC with SHA-384 * "HmacSHA512" HMAC HMAC with SHA-512 * * (BouncyCastle and Other Provider Extensions) * "AESCMAC" CMAC Cipher-based MAC using AES * "DESCMAC" CMAC CMAC with DES (legacy) * "GMAC" GCM-based MAC Authenticates AAD only (GCM-style) * "Poly1305" AEAD-style MAC Used with ChaCha20 * "SipHash" Hash-based MAC Fast MAC for short inputs * "BLAKE2BMAC" HMAC-style BLAKE2b MAC (cryptographic hash) * "HmacRIPEMD160" HMAC HMAC with RIPEMD160 hash */ bindingset[name] predicate mac_names(string name) { name.toUpperCase() .matches([ "HMAC%", "AESCMAC", "DESCMAC", "GMAC", "Poly1305", "SipHash", "BLAKE2BMAC", "HMACRIPEMD160" ].toUpperCase()) } /** * Names that match known signature algorithms. * https://docs.oracle.com/en/java/javase/25/docs/specs/security/standard-names.html */ bindingset[name] predicate signature_names(string name) { name.toUpperCase().splitAt("WITH", 1).matches(["RSA%", "ECDSA%", "DSA%"]) or name.toUpperCase().matches(["RSASSA-PSS", "ED25519", "ED448", "EDDSA", "ML-DSA%", "HSS/LMS"]) } bindingset[name] predicate key_agreement_names(string name) { name.toUpperCase() .matches(["DH", "EDH", "ECDH", "X25519", "X448", "ML-KEM%", "XDH"].toUpperCase()) } bindingset[name] Crypto::TKeyDerivationType kdf_name_to_kdf_type(string name, string withSubstring) { name.matches("PBKDF2With%") and result instanceof Crypto::PBKDF2 and withSubstring = name.regexpCapture("PBKDF2With(.*)", 1) or name.matches("PBEWith%") and result instanceof Crypto::PBES and withSubstring = name.regexpCapture("PBEWith(.*)", 1) // TODO: add additional } bindingset[name] Crypto::HashType hash_name_to_type_known(string name, int digestLength) { name in ["SHA-1", "SHA1"] and result instanceof Crypto::SHA1 and digestLength = 160 or name in ["SHA-256", "SHA-384", "SHA-512", "SHA256", "SHA384", "SHA512"] and result instanceof Crypto::SHA2 and digestLength = name.replaceAll("-", "").splitAt("SHA", 1).toInt() or name in ["SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512", "SHA3256", "SHA3384", "SHA3512"] and result instanceof Crypto::SHA3 and digestLength = name.replaceAll("-", "").splitAt("SHA3", 1).toInt() or ( name.matches("BLAKE2b%") and result instanceof Crypto::BLAKE2B or name = "BLAKE2s" and result instanceof Crypto::BLAKE2S ) and ( if exists(name.indexOf("-")) then name.splitAt("-", 1).toInt() = digestLength else digestLength = 512 ) or name = "MD5" and result instanceof Crypto::MD5 and digestLength = 128 or name = "RIPEMD160" and result instanceof Crypto::RIPEMD160 and digestLength = 160 or name = "Whirlpool" and result instanceof Crypto::WHIRLPOOL and digestLength = 512 // TODO: verify } bindingset[name] private predicate mode_name_to_type_known(KeyOpAlg::ModeOfOperationType type, string name) { type = KeyOpAlg::ECB() and name = "ECB" or type = KeyOpAlg::CBC() and name = "CBC" or type = KeyOpAlg::GCM() and name = "GCM" or type = KeyOpAlg::CTR() and name = "CTR" or type = KeyOpAlg::XTS() and name = "XTS" or type = KeyOpAlg::CCM() and name = "CCM" or type = KeyOpAlg::SIV() and name = "SIV" or type = KeyOpAlg::OCB() and name = "OCB" } bindingset[name] private predicate cipher_name_to_type_known(KeyOpAlg::TAlgorithm type, string name) { exists(string upper | upper = name.toUpperCase() | upper.matches("AES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::AES()) or upper = "DES" and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DES()) or upper = "TRIPLEDES" and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::TRIPLE_DES()) or upper = "IDEA" and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::IDEA()) or upper = "CAST5" and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CAST5()) or upper = "CHACHA20" and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CHACHA20()) or upper = "RC4" and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::RC4()) or upper = "RC5" and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::RC5()) or upper = "RSA" and type = KeyOpAlg::TAsymmetricCipher(KeyOpAlg::RSA()) ) } bindingset[name] predicate key_agreement_name_to_type_known(Crypto::TKeyAgreementType type, string name) { type = Crypto::DH() and name.toUpperCase() in ["DH", "XDH"] or type = Crypto::EDH() and name.toUpperCase() = "EDH" or type = Crypto::ECDH() and name.toUpperCase() in ["ECDH", "X25519", "X448"] or type = Crypto::OtherKeyAgreementType() and name.toUpperCase().matches("ML-KEM%") } /** * Maps a signature algorithm name to its type, if known. * see https://docs.oracle.com/en/java/javase/25/docs/specs/security/standard-names.html */ bindingset[name] predicate signature_name_to_type_known(Crypto::KeyOpAlg::TAlgorithm type, string name) { name.toUpperCase().splitAt("with".toUpperCase(), 1).matches("RSA%") and type = KeyOpAlg::TAsymmetricCipher(KeyOpAlg::RSA()) or name.toUpperCase().splitAt("with".toUpperCase(), 1).matches("ECDSA%") and type = KeyOpAlg::TSignature(KeyOpAlg::ECDSA()) or name.toUpperCase().splitAt("with".toUpperCase(), 1).matches("DSA%") and type = KeyOpAlg::TSignature(KeyOpAlg::DSA()) or name.toUpperCase() = "RSASSA-PSS" and type = KeyOpAlg::TAsymmetricCipher(KeyOpAlg::RSA()) or name.toUpperCase().matches(["EDDSA", "ED25519", "ED448"]) and type = KeyOpAlg::TSignature(KeyOpAlg::EDDSA()) or name.toUpperCase().matches("ML-DSA%") and type = KeyOpAlg::TSignature(KeyOpAlg::DSA()) or name.toUpperCase() = "HSS/LMS" and type = KeyOpAlg::TSignature(KeyOpAlg::HSS_LMS()) } bindingset[name] Crypto::HashType signature_name_to_hash_type_known(string name, int digestLength) { result = hash_name_to_type_known(name.splitAt("with", 0), digestLength) } /** * A `StringLiteral` in the `"ALG/MODE/PADDING"` or `"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 MethodCall { CipherGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance") } Expr getAlgorithmArg() { result = this.getArgument(0) } Expr getProviderArg() { result = this.getArgument(1) } } // TODO: handle key artifact produced by unwrap private class CipherOperationCall extends MethodCall { CipherOperationCall() { this.getMethod() .hasQualifiedName("javax.crypto", "Cipher", ["update", "doFinal", "wrap", "unwrap"]) } predicate isIntermediate() { this.getMethod().getName() = "update" } Expr getInput() { result = this.getArgument(0) } Expr getOutput() { exists(int outputIndex | this.getMethod().getParameter(outputIndex).getName() = "output" | result = this.getArgument(outputIndex) ) or this.getMethod().getReturnType().hasName("byte[]") and result = this } DataFlow::Node getMessageArg() { result.asExpr() = this.getInput() } } /** * Data-flow configuration modeling flow from a cipher string literal to a cipher algorithm consumer. */ private module CipherAlgorithmStringToCipherConsumerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral } predicate isSink(DataFlow::Node sink) { sink = any(CipherAlgorithmValueConsumer call).getInputNode() } } module CipherAlgorithmStringToFetchFlow = TaintTracking::Global; /** * Note: padding and a mode of operation will only exist when the padding / mode (*and its type*) are determinable. * This is because the mode will always be specified alongside the algorithm and never independently. * Therefore, we can always assume that a determinable algorithm will have a determinable mode. * * In the case that only an algorithm is specified, e.g., "AES", the provider provides a default mode. * * TODO: Model the case of relying on a provider default, but alert on it as a bad practice. */ class CipherStringLiteralPaddingAlgorithmInstance extends CipherStringLiteralAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral { CipherStringLiteralPaddingAlgorithmInstance() { exists(super.getPadding()) } // TODO: provider defaults override string getRawPaddingAlgorithmName() { result = super.getPadding() } bindingset[name] private predicate paddingToNameMappingKnown(KeyOpAlg::PaddingSchemeType type, string name) { type instanceof KeyOpAlg::NoPadding and name = "NOPADDING" or type instanceof KeyOpAlg::PKCS7 and name = ["PKCS5Padding", "PKCS7Padding"] // TODO: misnomer in the JCA? or type instanceof KeyOpAlg::OAEP and name.matches("OAEP%") // TODO: handle OAEPWith% } override KeyOpAlg::PaddingSchemeType getPaddingType() { if this.paddingToNameMappingKnown(_, super.getPadding()) then this.paddingToNameMappingKnown(result, super.getPadding()) else result instanceof KeyOpAlg::OtherPadding } } class CipherStringLiteralModeAlgorithmInstance extends CipherStringLiteralPaddingAlgorithmInstance, Crypto::ModeOfOperationAlgorithmInstance instanceof CipherStringLiteral { CipherStringLiteralModeAlgorithmInstance() { exists(super.getMode()) } // TODO: provider defaults override string getRawModeAlgorithmName() { result = super.getMode() } override KeyOpAlg::ModeOfOperationType getModeType() { if mode_name_to_type_known(_, super.getMode()) then mode_name_to_type_known(result, super.getMode()) else result instanceof KeyOpAlg::OtherMode } } class CipherStringLiteralAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance instanceof CipherStringLiteral { CipherAlgorithmValueConsumer consumer; CipherStringLiteralAlgorithmInstance() { CipherAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } CipherAlgorithmValueConsumer getConsumer() { result = consumer } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { result = this // TODO: provider defaults } override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { result = this // TODO: provider defaults } override string getRawAlgorithmName() { result = super.getValue() } override KeyOpAlg::AlgorithmType getAlgorithmType() { if cipher_name_to_type_known(_, super.getAlgorithmName()) then cipher_name_to_type_known(result, super.getAlgorithmName()) else result instanceof KeyOpAlg::TOtherKeyOperationAlgorithmType } override int getKeySizeFixed() { none() // TODO: implement to handle variants such as AES-128 } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } } bindingset[input] predicate oaep_padding_string_components(string input, string hash, string mfg) { exists(string regex | regex = "OAEPWith(.*)And(.*)Padding" and hash = input.regexpCapture(regex, 1) and mfg = input.regexpCapture(regex, 2) ) } predicate oaep_padding_string_components_eval(string hash, string mfg) { oaep_padding_string_components(any(CipherStringLiteral s).getPadding(), hash, mfg) } class OaepPaddingHashAlgorithmInstance extends OaepPaddingAlgorithmInstance, Crypto::HashAlgorithmInstance instanceof CipherStringLiteral { string hashName; OaepPaddingHashAlgorithmInstance() { oaep_padding_string_components(super.getPadding(), hashName, _) } override string getRawHashAlgorithmName() { result = super.getPadding() } override Crypto::THashType getHashType() { result = hash_name_to_type_known(hashName, _) } override int getFixedDigestLength() { exists(hash_name_to_type_known(hashName, result)) } } class OaepPaddingAlgorithmInstance extends Crypto::OaepPaddingAlgorithmInstance, CipherStringLiteralPaddingAlgorithmInstance { override Crypto::HashAlgorithmInstance getOaepEncodingHashAlgorithm() { result = this } override Crypto::HashAlgorithmInstance getMgf1HashAlgorithm() { none() } // TODO } /** * The cipher algorithm argument to a `CipherGetInstanceCall`. * * For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`. */ class CipherGetInstanceAlgorithmArg extends CipherAlgorithmValueConsumer instanceof Expr { CipherGetInstanceAlgorithmArg() { this = any(CipherGetInstanceCall call).getAlgorithmArg() } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this } } /** * An access to the `javax.crypto.Cipher` class. */ private class CipherAccess extends TypeAccess { CipherAccess() { this.getType().(Class).hasQualifiedName("javax.crypto", "Cipher") } } /** * An access to a cipher mode field of the `javax.crypto.Cipher` class, * specifically `ENCRYPT_MODE`, `DECRYPT_MODE`, `WRAP_MODE`, or `UNWRAP_MODE`. */ private class JavaxCryptoCipherOperationModeAccess extends FieldAccess { JavaxCryptoCipherOperationModeAccess() { this.getQualifier() instanceof CipherAccess and this.getField().getName().toUpperCase() in [ "ENCRYPT_MODE", "DECRYPT_MODE", "WRAP_MODE", "UNWRAP_MODE" ] } } signature class GetInstanceCallSig instanceof MethodCall; signature class InitCallSig instanceof MethodCall; signature class UseCallSig instanceof MethodCall { /** * Holds if the use is not a final use, such as an `update()` call before `doFinal()` */ predicate isIntermediate(); } /** * An generic analysis module for analyzing the `getInstance` to `initialize` to `doOperation` pattern in the JCA. * * For example: * ``` * kpg = KeyPairGenerator.getInstance(); * kpg.initialize(...); * kpg.generate(...); * ``` */ module GetInstanceInitUseFlowAnalysis< GetInstanceCallSig GetInstance, InitCallSig Init, UseCallSig Use> { newtype TFlowState = TUninitialized() or TInitialized(Init call) or TIntermediateUse(Use call) abstract class InitFlowState extends TFlowState { string toString() { this = TUninitialized() and result = "Uninitialized" or this = TInitialized(_) and result = "Initialized" // TODO: add intermediate use } } class UninitializedFlowState extends InitFlowState, TUninitialized { } class InitializedFlowState extends InitFlowState, TInitialized { Init call; DataFlow::Node node1; DataFlow::Node node2; InitializedFlowState() { this = TInitialized(call) and node2.asExpr() = call.(MethodCall).getQualifier() and DataFlow::localFlowStep(node1, node2) and node1 != node2 } Init getInitCall() { result = call } DataFlow::Node getFstNode() { result = node1 } DataFlow::Node getSndNode() { result = node2 } } class IntermediateUseState extends InitFlowState, TIntermediateUse { Use call; DataFlow::Node node1; DataFlow::Node node2; IntermediateUseState() { this = TIntermediateUse(call) and call.isIntermediate() and node1.asExpr() = call.(MethodCall).getQualifier() and node2 = node1 } Use getUseCall() { result = call } DataFlow::Node getFstNode() { result = node1 } DataFlow::Node getSndNode() { result = node2 } } module GetInstanceToInitToUseConfig implements DataFlow::StateConfigSig { class FlowState = InitFlowState; predicate isSource(DataFlow::Node src, FlowState state) { state instanceof UninitializedFlowState and src.asExpr() instanceof GetInstance or src = state.(InitializedFlowState).getSndNode() or src = state.(IntermediateUseState).getSndNode() } // TODO: document this, but this is intentional (avoid cross products?) predicate isSink(DataFlow::Node sink, FlowState state) { none() } predicate isSink(DataFlow::Node sink) { exists(Init c | c.(MethodCall).getQualifier() = sink.asExpr()) or exists(Use c | not c.isIntermediate() and c.(MethodCall).getQualifier() = sink.asExpr()) } predicate isAdditionalFlowStep( DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 ) { state1 = state1 and ( node1 = state2.(InitializedFlowState).getFstNode() and node2 = state2.(InitializedFlowState).getSndNode() or node1 = state2.(IntermediateUseState).getFstNode() and node2 = state2.(IntermediateUseState).getSndNode() ) } predicate isBarrier(DataFlow::Node node, FlowState state) { exists(CipherInitCall call | node.asExpr() = call.getQualifier() | state instanceof UninitializedFlowState or state.(InitializedFlowState).getInitCall() != call ) } } module GetInstanceToInitToUseFlow = DataFlow::GlobalWithState; GetInstance getInstantiationFromUse( Use use, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink ) { src.getNode().asExpr() = result and sink.getNode().asExpr() = use.(MethodCall).getQualifier() and GetInstanceToInitToUseFlow::flowPath(src, sink) } GetInstance getInstantiationFromInit( Init init, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink ) { src.getNode().asExpr() = result and sink.getNode().asExpr() = init.(MethodCall).getQualifier() and GetInstanceToInitToUseFlow::flowPath(src, sink) } Init getInitFromUse( Use use, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink ) { src.getNode().asExpr() = result.(MethodCall).getQualifier() and sink.getNode().asExpr() = use.(MethodCall).getQualifier() and GetInstanceToInitToUseFlow::flowPath(src, sink) } predicate hasInit(Use use) { exists(getInitFromUse(use, _, _)) } Use getAnIntermediateUseFromFinalUse( Use final, GetInstanceToInitToUseFlow::PathNode src, GetInstanceToInitToUseFlow::PathNode sink ) { not final.isIntermediate() and result.isIntermediate() and src.getNode().asExpr() = result.(MethodCall).getQualifier() and sink.getNode().asExpr() = final.(MethodCall).getQualifier() and GetInstanceToInitToUseFlow::flowPath(src, sink) } } module CipherFlowAnalysisImpl = GetInstanceInitUseFlowAnalysis; module CipherFlow = CipherFlowAnalysisImpl::GetInstanceToInitToUseFlow; Crypto::KeyOperationSubtype getKeyOperationSubtypeFromState( CipherFlowAnalysisImpl::InitFlowState state ) { state instanceof CipherFlowAnalysisImpl::UninitializedFlowState and result = Crypto::TUnknownKeyOperationMode() or exists(CipherInitCall call | state = CipherFlowAnalysisImpl::TInitialized(call) | result = call.getCipherOperationModeType() ) } class CipherOperationInstance extends Crypto::KeyOperationInstance instanceof CipherOperationCall { CipherOperationInstance() { not this.isIntermediate() } override Crypto::KeyOperationSubtype getKeyOperationSubtype() { if CipherFlowAnalysisImpl::hasInit(this) then result = CipherFlowAnalysisImpl::getInitFromUse(this, _, _).getCipherOperationModeType() else result = Crypto::TUnknownKeyOperationMode() } CipherGetInstanceCall getInstantiationCall() { result = CipherFlowAnalysisImpl::getInstantiationFromUse(this, _, _) } CipherInitCall getInitCall() { result = CipherFlowAnalysisImpl::getInitFromUse(this, _, _) } override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { result.asExpr() = this.getInitCall().getNonceArg() } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = super.getMessageArg() or result = CipherFlowAnalysisImpl::getAnIntermediateUseFromFinalUse(this, _, _).getMessageArg() } override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { result.asExpr() = this.getInitCall().getKeyArg() } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result = this.getInstantiationCall().getAlgorithmArg() } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result.asExpr() = super.getOutput() } } /** * Initialization vectors and other nonce artifacts */ abstract class NonceParameterInstantiation extends ClassInstanceExpr { DataFlow::Node getOutputNode() { result.asExpr() = this } abstract DataFlow::Node getInputNode(); } class IvParameterSpecInstance extends NonceParameterInstantiation { IvParameterSpecInstance() { super.getConstructedType().hasQualifiedName("javax.crypto.spec", "IvParameterSpec") } override DataFlow::Node getInputNode() { result.asExpr() = super.getArgument(0) } } // TODO: this also specifies the tag length for GCM class GCMParameterSpecInstance extends NonceParameterInstantiation { GCMParameterSpecInstance() { super.getConstructedType().hasQualifiedName("javax.crypto.spec", "GCMParameterSpec") } override DataFlow::Node getInputNode() { result.asExpr() = super.getArgument(1) } } class IvParameterSpecGetIvCall extends MethodCall { IvParameterSpecGetIvCall() { this.getMethod().hasQualifiedName("javax.crypto.spec", "IvParameterSpec", "getIV") } } class KeyAdditionalFlowSteps extends MethodCall { KeyAdditionalFlowSteps() { this.getCallee().hasQualifiedName("java.security", "KeyPair", "getPublic") or this.getCallee().hasQualifiedName("java.security", "KeyPair", "getPrivate") or this.getCallee().hasQualifiedName("java.security", "Key", "getEncoded") } DataFlow::Node getInputNode() { result.asExpr() = this.getQualifier() } DataFlow::Node getOutputNode() { result.asExpr() = this } } predicate additionalFlowSteps(DataFlow::Node node1, DataFlow::Node node2) { exists(IvParameterSpecGetIvCall m | node1.asExpr() = m.getQualifier() and node2.asExpr() = m ) or exists(NonceParameterInstantiation n | node1 = n.getInputNode() and node2 = n.getOutputNode() ) or exists(KeyAdditionalFlowSteps call | node1 = call.getInputNode() and node2 = call.getOutputNode() ) } class ArtifactAdditionalFlowStep extends AdditionalFlowInputStep { DataFlow::Node output; ArtifactAdditionalFlowStep() { additionalFlowSteps(this, output) } override DataFlow::Node getOutput() { result = output } } /** * A data-flow configuration to track flow from a mode field access to * the mode argument of the `init` method of the `javax.crypto.Cipher` class. */ private module JavaxCipherModeAccessToInitConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof JavaxCryptoCipherOperationModeAccess } predicate isSink(DataFlow::Node sink) { exists(CipherInitCall c | c.getModeArg() = sink.asExpr()) } } module JavaxCipherModeAccessToInitFlow = DataFlow::Global; private predicate cipher_mode_str_to_cipher_mode_known( string mode, Crypto::KeyOperationSubtype cipher_mode ) { mode = "ENCRYPT_MODE" and cipher_mode = Crypto::TEncryptMode() or mode = "WRAP_MODE" and cipher_mode = Crypto::TWrapMode() or mode = "DECRYPT_MODE" and cipher_mode = Crypto::TDecryptMode() or mode = "UNWRAP_MODE" and cipher_mode = Crypto::TUnwrapMode() } class CipherInitCall extends MethodCall { CipherInitCall() { this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "init") } /** * Returns the mode argument to the `init` method * that is used to determine the cipher operation mode. * Note this is the raw expr and not necessarily a direct access * of a mode. Use `getModeOrigin()` to get the field access origin * flowing to this argument, if one exists (is known). */ Expr getModeArg() { result = this.getArgument(0) } JavaxCryptoCipherOperationModeAccess getModeOrigin() { exists(DataFlow::Node src, DataFlow::Node sink | JavaxCipherModeAccessToInitFlow::flow(src, sink) and src.asExpr() = result and this.getModeArg() = sink.asExpr() ) } Crypto::KeyOperationSubtype getCipherOperationModeType() { if cipher_mode_str_to_cipher_mode_known(this.getModeOrigin().getField().getName(), _) then cipher_mode_str_to_cipher_mode_known(this.getModeOrigin().getField().getName(), result) else result = Crypto::TUnknownKeyOperationMode() } Expr getKeyArg() { result = this.getArgument(1) and this.getMethod().getParameterType(1).hasName("Key") } Expr getNonceArg() { result = this.getArgument(2) and this.getMethod().getParameterType(2).hasName("AlgorithmParameterSpec") } } class CipherInitCallKeyConsumer extends Crypto::ArtifactConsumer { CipherInitCallKeyConsumer() { this = any(CipherInitCall call).getKeyArg() } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } } /* * Hash Functions */ /** * Flow from a known hash algorithm name to a `MessageDigest.getInstance(sink)` call. */ module KnownHashAlgorithmLiteralToMessageDigestConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { hash_names(src.asExpr().(StringLiteral).getValue()) } predicate isSink(DataFlow::Node sink) { exists(HashAlgorithmValueConsumer consumer | sink = consumer.getInputNode()) } } module KnownHashAlgorithmLiteralToMessageDigestFlow = DataFlow::Global; class KnownHashAlgorithm extends Crypto::HashAlgorithmInstance instanceof StringLiteral { HashAlgorithmValueConsumer consumer; KnownHashAlgorithm() { hash_names(super.getValue()) and KnownHashAlgorithmLiteralToMessageDigestFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } HashAlgorithmValueConsumer getConsumer() { result = consumer } override string getRawHashAlgorithmName() { result = super.getValue() } override Crypto::THashType getHashType() { result = hash_name_to_type_known(this.getRawHashAlgorithmName(), _) } override int getFixedDigestLength() { exists(hash_name_to_type_known(this.getRawHashAlgorithmName(), result)) } } module MessageDigestFlowAnalysisImpl = GetInstanceInitUseFlowAnalysis; class MessageDigestGetInstanceAlgorithmValueConsumer extends HashAlgorithmValueConsumer { MessageDigestGetInstanceCall call; MessageDigestGetInstanceAlgorithmValueConsumer() { this = call.getAlgorithmArg() } MessageDigestGetInstanceCall getInstantiationCall() { result = call } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { exists(KnownHashAlgorithm l | l.getConsumer() = this and result = l) } } class MessageDigestGetInstanceCall extends MethodCall { MessageDigestGetInstanceCall() { this.getCallee().hasQualifiedName("java.security", "MessageDigest", "getInstance") } Expr getAlgorithmArg() { result = this.getArgument(0) } } class DigestCall extends MethodCall { DigestCall() { this.getCallee().hasQualifiedName("java.security", "MessageDigest", ["update", "digest"]) } Expr getDigestArtifactOutput() { result = this } Expr getInputArg() { result = this.getArgument(0) } predicate isIntermediate() { this.getMethod().getName() = "update" } } // flow config from MessageDigest.getInstance to MessageDigest.digest module DigestGetInstanceToDigestConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof MessageDigestGetInstanceCall } predicate isSink(DataFlow::Node sink) { exists(DigestCall c | c.getQualifier() = sink.asExpr()) } } module DigestGetInstanceToDigestFlow = DataFlow::Global; class DigestHashOperation extends Crypto::HashOperationInstance instanceof DigestCall { DigestHashOperation() { not super.isIntermediate() } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result.asExpr() = super.getDigestArtifactOutput() } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { MessageDigestFlowAnalysisImpl::getInstantiationFromUse(this, _, _) = result.(MessageDigestGetInstanceAlgorithmValueConsumer).getInstantiationCall() } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result.asExpr() = super.getInputArg() or result.asExpr() = MessageDigestFlowAnalysisImpl::getAnIntermediateUseFromFinalUse(this, _, _).getInputArg() } } /* * Key Generation */ module KeyGeneratorFlowAnalysisImpl = GetInstanceInitUseFlowAnalysis; module KeyGeneratorFlow = KeyGeneratorFlowAnalysisImpl::GetInstanceToInitToUseFlow; abstract class KeyGeneratorParameterSpecClassInstanceExpr extends ClassInstanceExpr { KeyGeneratorInitCall getAnInitCallUse() { exists(DataFlow::Node sink | KeyGeneratorParameterSpecToInitializeFlow::flow(DataFlow::exprNode(this), sink) and result.getAlgorithmParameterSpecArg() = sink.asExpr() ) } } class DHGenParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { DHGenParameterSpecInstance() { super.getConstructedType().hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") } Expr getPrimeSizeArg() { result = this.getArgument(0) } Expr getExponentSizeArg() { result = this.getArgument(1) } } class DSAParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { DSAParameterSpecInstance() { super.getConstructedType().hasQualifiedName("java.security.spec", "DSAParameterSpec") } Expr getPArg() { result = this.getArgument(0) } Expr getQArg() { result = this.getArgument(1) } Expr getSeedLenArg() { result = this.getArgument(2) } } class ECGenParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { ECGenParameterSpecInstance() { super.getConstructedType().hasQualifiedName("java.security.spec", "ECGenParameterSpec") } Expr getCurveNameArg() { result = this.getArgument(0) } Expr getRandomNumberGeneratorArg() { result = this.getArgument(1) } } class RSAGenParameterSpecInstance extends KeyGeneratorParameterSpecClassInstanceExpr { RSAGenParameterSpecInstance() { super.getConstructedType().hasQualifiedName("java.security.spec", "RSAGenParameterSpec") } Expr getKeySizeArg() { result = this.getArgument(0) } } private module KeyGeneratorParameterSpecToInitializeConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KeyGeneratorParameterSpecClassInstanceExpr } predicate isSink(DataFlow::Node sink) { exists(KeyGeneratorInitCall c | c.getKeyType() = Crypto::TAsymmetricKeyType() and c.getArgument(0) = sink.asExpr() ) } } module KeyGeneratorParameterSpecToInitializeFlow = DataFlow::Global; class ECGenParameterSpecClassInstanceExpr extends KeyGeneratorParameterSpecClassInstanceExpr { ECGenParameterSpecClassInstanceExpr() { super.getConstructedType().hasQualifiedName("java.security.spec", "ECGenParameterSpec") } Expr getAlgorithmArg() { result = this.getArgument(0) } } class KeyGenerationAlgorithmValueConsumer extends CipherAlgorithmValueConsumer, KeyAgreementAlgorithmValueConsumer, EllipticCurveAlgorithmValueConsumer instanceof Expr { KeyGeneratorGetInstanceCall instantiationCall; KeyGenerationAlgorithmValueConsumer() { // This is only an algorithm value consumer if it accepts a spec rather than a key size (integral) this = instantiationCall.getAlgorithmArg() and not super.getType() instanceof IntegralType or // However, for general elliptic curves, getInstance("EC") is used // and java.security.spec.ECGenParameterSpec("") is what sets the specific curve. // If init is not specified, the default (P-) // The result of ECGenParameterSpec is passed to KeyPairGenerator.initialize // If the curve is not specified, the default is used. // We would trace the use of this inside a KeyPairGenerator.initialize exists(KeyGeneratorInitCall initCall, ECGenParameterSpecClassInstanceExpr spec | KeyGeneratorFlow::flow(DataFlow::exprNode(instantiationCall), DataFlow::exprNode(initCall.getQualifier())) and spec.getAnInitCallUse() = initCall and spec.getAlgorithmArg() = this ) } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this or result.(KeyAgreementStringLiteralAlgorithmInstance).getConsumer() = this or result.(EllipticCurveStringLiteralInstance).getConsumer() = this or result.(SignatureStringLiteralAlgorithmInstance).getConsumer() = this } KeyGeneratorGetInstanceCall getInstantiationCall() { result = instantiationCall } } //TODO: Link getAlgorithm from KeyPairGenerator to algorithm instances or AVCs? High priority. class KeyGeneratorGetInstanceCall extends MethodCall { KeyGeneratorGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance") or this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "getInstance") } Expr getAlgorithmArg() { result = super.getArgument(0) } } class KeyGeneratorInitCall extends MethodCall { Crypto::TKeyArtifactType keyType; KeyGeneratorInitCall() { this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "initialize") and keyType = Crypto::TAsymmetricKeyType() or this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", ["init", "initialize"]) and keyType = Crypto::TSymmetricKeyType() } Crypto::TKeyArtifactType getKeyType() { result = keyType } Expr getAlgorithmParameterSpecArg() { result = this.getArgument(0) and this.getMethod().getParameterType(0).hasName("AlgorithmParameterSpec") } Expr getKeySizeArg() { result = this.getArgument(0) and this.getMethod().getParameterType(0) instanceof IntegralType } Expr getRandomnessSourceArg() { exists(int index | this.getMethod().getParameterType(index).hasName("SecureRandom") and result = this.getArgument(index) ) } } class KeyGeneratorGenerateCall extends Crypto::KeyGenerationOperationInstance instanceof MethodCall { Crypto::KeyArtifactType type; KeyGeneratorGenerateCall() { this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "generateKey") and type instanceof Crypto::TSymmetricKeyType or this.getCallee() .hasQualifiedName("java.security", "KeyPairGenerator", ["generateKeyPair", "genKeyPair"]) and type instanceof Crypto::TAsymmetricKeyType } predicate isIntermediate() { none() } override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result.asExpr() = this } override Crypto::KeyArtifactType getOutputKeyType() { result = type } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { KeyGeneratorFlowAnalysisImpl::getInstantiationFromUse(this, _, _) = result.(KeyGenerationAlgorithmValueConsumer).getInstantiationCall() } Crypto::AlgorithmInstance getAKnownAlgorithm() { result = this.getAnAlgorithmValueConsumer().getAKnownAlgorithmSource() } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { KeyGeneratorFlowAnalysisImpl::getInitFromUse(this, _, _).getKeySizeArg() = result.asExpr() } override int getKeySizeFixed() { none() } override Crypto::ConsumerInputDataFlowNode getKeyValueConsumer() { none() } override predicate hasKeyValueConsumer() { none() } } class KeyGeneratorCipherAlgorithm extends CipherStringLiteralAlgorithmInstance { KeyGeneratorCipherAlgorithm() { consumer instanceof KeyGenerationAlgorithmValueConsumer } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { exists(KeyGeneratorGetInstanceCall getInstance, KeyGeneratorInitCall init | getInstance = this.getConsumer().(KeyGenerationAlgorithmValueConsumer).getInstantiationCall() and getInstance = KeyGeneratorFlowAnalysisImpl::getInstantiationFromInit(init, _, _) and init.getKeySizeArg() = result.asExpr() ) } predicate isOnlyConsumedByKeyGen() { forall(Crypto::AlgorithmValueConsumer c | c = this.getConsumer() and c instanceof KeyGenerationAlgorithmValueConsumer ) } override predicate shouldHaveModeOfOperation() { this.isOnlyConsumedByKeyGen() } override predicate shouldHavePaddingScheme() { this.isOnlyConsumedByKeyGen() } } /* * Key Derivation Functions (KDFs) */ class KeySpecInstantiation extends ClassInstanceExpr { KeySpecInstantiation() { this.getConstructedType() .hasQualifiedName("javax.crypto.spec", ["PBEKeySpec", "SecretKeySpec", "PBEKeySpec", "DESedeKeySpec"]) } Expr getPasswordArg() { result = this.getArgument(0) } } class PBEKeySpecInstantiation extends KeySpecInstantiation { PBEKeySpecInstantiation() { this.getConstructedType().hasName("PBEKeySpec") } Expr getSaltArg() { result = this.getArgument(1) } Expr getIterationCountArg() { result = this.getArgument(2) } Expr getKeyLengthArg() { result = this.getArgument(3) } } module KeySpecInstantiationToGenerateSecretFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KeySpecInstantiation } predicate isSink(DataFlow::Node sink) { exists(SecretKeyFactoryGenerateSecretCall call | sink.asExpr() = call.getKeySpecArg()) } } module KDFAlgorithmStringToGetInstanceConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { kdf_names(src.asExpr().(StringLiteral).getValue()) } predicate isSink(DataFlow::Node sink) { exists(SecretKeyFactoryGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) } } module KeySpecInstantiationToGenerateSecretFlow = DataFlow::Global; module KDFAlgorithmStringToGetInstanceFlow = DataFlow::Global; class DUMMY_UNUSED_METHODCALL extends MethodCall { DUMMY_UNUSED_METHODCALL() { none() } } module SecretKeyFactoryFlowAnalysisImpl = GetInstanceInitUseFlowAnalysis; module SecretKeyFactoryFlow = SecretKeyFactoryFlowAnalysisImpl::GetInstanceToInitToUseFlow; class SecretKeyFactoryGetInstanceCall extends MethodCall { SecretKeyFactoryGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "SecretKeyFactory", "getInstance") } Expr getAlgorithmArg() { result = this.getArgument(0) } } class SecretKeyFactoryGenerateSecretCall extends MethodCall { SecretKeyFactoryGenerateSecretCall() { this.getCallee().hasQualifiedName("javax.crypto", "SecretKeyFactory", "generateSecret") } Expr getKeySpecArg() { result = this.getArgument(0) } predicate isIntermediate() { none() } } class KdfAlgorithmStringLiteral extends Crypto::KeyDerivationAlgorithmInstance instanceof StringLiteral { SecretKeyFactoryKDFAlgorithmValueConsumer consumer; KdfAlgorithmStringLiteral() { kdf_names(this.getValue()) and KDFAlgorithmStringToGetInstanceFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } override string getRawKdfAlgorithmName() { result = super.getValue() } override Crypto::TKeyDerivationType getKdfType() { result = kdf_name_to_kdf_type(super.getValue(), _) } SecretKeyFactoryKDFAlgorithmValueConsumer getConsumer() { result = consumer } } class Pbkdf2WithHmac_KeyOperationAlgorithmStringLiteral extends Crypto::KeyOperationAlgorithmInstance instanceof KdfAlgorithmStringLiteral { Pbkdf2WithHmac_KeyOperationAlgorithmStringLiteral() { this.(StringLiteral).getValue().toUpperCase().matches("PBKDF2WithHmac%".toUpperCase()) } override Crypto::KeyOpAlg::AlgorithmType getAlgorithmType() { result = KeyOpAlg::TMac(KeyOpAlg::HMAC()) } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { // TODO: trace to any key size initializer? none() } override int getKeySizeFixed() { // TODO: are there known fixed key sizes to consider? none() } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } override string getRawAlgorithmName() { result = this.(StringLiteral).getValue() } } class Pbkdf2WithHmac_HashAlgorithmStringLiteral extends Crypto::HashAlgorithmInstance instanceof Pbkdf2WithHmac_KeyOperationAlgorithmStringLiteral { string hashName; Pbkdf2WithHmac_HashAlgorithmStringLiteral() { hashName = this.(StringLiteral).getValue().splitAt("WithHmac", 1) } override string getRawHashAlgorithmName() { result = this.(StringLiteral).getValue() } override Crypto::THashType getHashType() { result = hash_name_to_type_known(hashName, _) } override int getFixedDigestLength() { exists(hash_name_to_type_known(hashName, result)) } } //TODO: handle PBE "with" cases class Pbkdf2WithHmac_Pbkdf2AlgorithmInstance extends Crypto::Pbkdf2AlgorithmInstance, KdfAlgorithmStringLiteral, // this is a parent already, but extending to have immediate access to 'getConsumer()' Pbkdf2WithHmac_KeyOperationAlgorithmStringLiteral { override Crypto::AlgorithmValueConsumer getHmacAlgorithmValueConsumer() { result = this.getConsumer() } } // NOTE: must use instanceof to avoid non-monotonic recursion class Pbkdf2WithHmac_HmacAlgorithmInstance extends Crypto::HmacAlgorithmInstance instanceof Pbkdf2WithHmac_KeyOperationAlgorithmStringLiteral { override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() { result = this.(KdfAlgorithmStringLiteral).getConsumer() } override int getKeySizeFixed() { // already defined by parent key operation algorithm, but extending an instance // still requires we override this method result = super.getKeySizeFixed() } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { // already defined by parent key operation algorithm, but extending an instance // still requires we override this method result = super.getKeySizeConsumer() } override string getRawAlgorithmName() { // already defined by parent key operation algorithm, but extending an instance // still requires we override this method result = super.getRawAlgorithmName() } override Crypto::KeyOpAlg::AlgorithmType getAlgorithmType() { result = KeyOpAlg::TMac(KeyOpAlg::HMAC()) } } class SecretKeyFactoryKDFAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof Expr { SecretKeyFactoryGetInstanceCall call; SecretKeyFactoryKDFAlgorithmValueConsumer() { this = call.getAlgorithmArg() } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { exists(KdfAlgorithmStringLiteral l | l.getConsumer() = this and result = l) } SecretKeyFactoryGetInstanceCall getInstantiation() { result = call } } class KeyDerivationOperationCall extends Crypto::KeyDerivationOperationInstance instanceof SecretKeyFactoryGenerateSecretCall { KeyDerivationOperationCall() { not super.isIntermediate() } KeySpecInstantiation getKeySpecInstantiation() { KeySpecInstantiationToGenerateSecretFlow::flow(DataFlow::exprNode(result), DataFlow::exprNode(super.getKeySpecArg())) } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result.(SecretKeyFactoryKDFAlgorithmValueConsumer).getInstantiation() = SecretKeyFactoryFlowAnalysisImpl::getInstantiationFromUse(this, _, _) } override Crypto::ConsumerInputDataFlowNode getSaltConsumer() { result.asExpr() = this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getSaltArg() } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result.asExpr() = this.getKeySpecInstantiation().getPasswordArg() } override Crypto::ConsumerInputDataFlowNode getIterationCountConsumer() { result.asExpr() = this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getIterationCountArg() } override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result.asExpr() = this } override Crypto::ConsumerInputDataFlowNode getOutputKeySizeConsumer() { result.asExpr() = this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getKeyLengthArg() } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { result.asExpr() = this.getKeySpecInstantiation().(PBEKeySpecInstantiation).getKeyLengthArg() } override int getKeySizeFixed() { none() } override string getOutputKeySizeFixed() { none() } override string getIterationCountFixed() { none() } } /* * Key agreement */ module KeyAgreementFlowAnalysisImpl = GetInstanceInitUseFlowAnalysis; class KeyAgreementStringLiteral extends StringLiteral { KeyAgreementStringLiteral() { key_agreement_names(this.getValue()) } } /** * Data-flow configuration modeling flow from a key agreement string literal to a key agreement algorithm consumer. */ private module KeyAgreementAlgorithmStringToConsumerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KeyAgreementStringLiteral } predicate isSink(DataFlow::Node sink) { sink = any(KeyAgreementAlgorithmValueConsumer consumer).getInputNode() } } module KeyAgreementAlgorithmStringToConsumerFlow = TaintTracking::Global; class KeyAgreementInitCall extends MethodCall { KeyAgreementInitCall() { this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "init") } Expr getServerKeyArg() { result = this.getArgument(0) } } class KeyAgreementGetInstanceCall extends MethodCall { KeyAgreementGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "KeyAgreement", "getInstance") } Expr getAlgorithmArg() { result = super.getArgument(0) } } private class KeyAgreementGetInstanceAlgorithmArgValueConsumer extends KeyAgreementAlgorithmValueConsumer { KeyAgreementGetInstanceCall call; KeyAgreementGetInstanceAlgorithmArgValueConsumer() { this = call.getAlgorithmArg() } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result.(KeyAgreementStringLiteralAlgorithmInstance).getConsumer() = this } KeyAgreementGetInstanceCall getInstantiationCall() { result = call } } class KeyAgreementStringLiteralAlgorithmInstance extends Crypto::KeyAgreementAlgorithmInstance instanceof KeyAgreementStringLiteral { KeyAgreementAlgorithmValueConsumer consumer; KeyAgreementStringLiteralAlgorithmInstance() { KeyAgreementAlgorithmStringToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } override string getRawKeyAgreementAlgorithmName() { result = super.getValue() } override Crypto::TKeyAgreementType getKeyAgreementType() { if key_agreement_name_to_type_known(_, super.getValue()) then key_agreement_name_to_type_known(result, super.getValue()) else result = Crypto::OtherKeyAgreementType() } KeyAgreementAlgorithmValueConsumer getConsumer() { result = consumer } } class KeyAgreementCall extends MethodCall { KeyAgreementCall() { this.getCallee() .hasQualifiedName("javax.crypto", "KeyAgreement", ["generateSecret", "doPhase"]) } predicate isIntermediate() { this.getCallee().getName() = "doPhase" } DataFlow::Node getOutputNode() { result.asExpr() = this and not this.isIntermediate() } Expr getPeerKeyArg() { this.isIntermediate() and result = this.getArgument(0) and this.getCallee().getName() = "doPhase" } } class KeyAgreementSecretGenerationOperationInstance extends Crypto::KeyAgreementSecretGenerationOperationInstance instanceof KeyAgreementCall { KeyAgreementSecretGenerationOperationInstance() { // exclude doPhase (only include generateSecret) not super.isIntermediate() } override Crypto::ConsumerInputDataFlowNode getServerKeyConsumer() { result.asExpr() = KeyAgreementFlowAnalysisImpl::getInitFromUse(this, _, _).getServerKeyArg() } override Crypto::ConsumerInputDataFlowNode getPeerKeyConsumer() { result.asExpr() = KeyAgreementFlowAnalysisImpl::getAnIntermediateUseFromFinalUse(this, _, _).getPeerKeyArg() } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result.(KeyAgreementGetInstanceAlgorithmArgValueConsumer).getInstantiationCall() = KeyAgreementFlowAnalysisImpl::getInstantiationFromUse(this, _, _) } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result.asExpr() = this } } /* * MACs */ module MacKnownAlgorithmToConsumerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { mac_names(src.asExpr().(StringLiteral).getValue()) } predicate isSink(DataFlow::Node sink) { exists(MacGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg()) } } module MacKnownAlgorithmToConsumerFlow = DataFlow::Global; module MacGetInstanceToMacOperationFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof MacGetInstanceCall } predicate isSink(DataFlow::Node sink) { exists(MacOperationCall call | sink.asExpr() = call.(MethodCall).getQualifier()) or exists(MacInitCall call | sink.asExpr() = call.(MethodCall).getQualifier()) } } module MacGetInstanceToMacOperationFlow = DataFlow::Global; module MacInitCallToMacOperationFlowConfig implements DataFlow::ConfigSig { // TODO: use flow state with one config predicate isSource(DataFlow::Node src) { exists(MacInitCall init | src.asExpr() = init.getQualifier()) } predicate isSink(DataFlow::Node sink) { exists(MacOperationCall call | sink.asExpr() = call.(MethodCall).getQualifier()) } } module MacInitCallToMacOperationFlow = DataFlow::Global; class KnownMacAlgorithm extends Crypto::KeyOperationAlgorithmInstance instanceof StringLiteral { MacGetInstanceAlgorithmValueConsumer consumer; KnownMacAlgorithm() { mac_names(this.getValue()) and MacKnownAlgorithmToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } MacGetInstanceAlgorithmValueConsumer getConsumer() { result = consumer } override string getRawAlgorithmName() { result = super.getValue() } override Crypto::KeyOpAlg::AlgorithmType getAlgorithmType() { if super.getValue().toUpperCase().matches("HMAC%") then result = KeyOpAlg::TMac(KeyOpAlg::HMAC()) else if super.getValue().toUpperCase().matches("CMAC%") then result = KeyOpAlg::TMac(KeyOpAlg::CMAC()) else result = KeyOpAlg::TMac(KeyOpAlg::OtherMacAlgorithmType()) } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { // TODO: trace to any key size initializer? none() } override int getKeySizeFixed() { // TODO: are there known fixed key sizes to consider? none() } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } } class MacGetInstanceCall extends MethodCall { MacGetInstanceCall() { this.getCallee().hasQualifiedName("javax.crypto", "Mac", "getInstance") } Expr getAlgorithmArg() { result = this.getArgument(0) } MacOperationCall getOperation() { MacGetInstanceToMacOperationFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(result.(MethodCall).getQualifier())) } MacInitCall getInitCall() { MacGetInstanceToMacOperationFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(result.getQualifier())) } } class MacInitCall extends MethodCall { MacInitCall() { this.getCallee().hasQualifiedName("javax.crypto", "Mac", "init") } Expr getKeyArg() { result = this.getArgument(0) and this.getMethod().getParameterType(0).hasName("Key") } MacOperationCall getOperation() { MacInitCallToMacOperationFlow::flow(DataFlow::exprNode(this.getQualifier()), DataFlow::exprNode(result.(MethodCall).getQualifier())) } } class MacGetInstanceAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer { MacGetInstanceAlgorithmValueConsumer() { this = any(MacGetInstanceCall c).getAlgorithmArg() } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { exists(KnownMacAlgorithm l | l.getConsumer() = this and result = l) } } class MacOperationCall extends Crypto::MacOperationInstance instanceof MethodCall { Expr output; MacOperationCall() { super.getMethod().getDeclaringType().hasQualifiedName("javax.crypto", "Mac") and ( super.getMethod().hasStringSignature(["doFinal()", "doFinal(byte[])"]) and this = output or super.getMethod().hasStringSignature("doFinal(byte[], int)") and this.getArgument(0) = output ) } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { exists(MacGetInstanceCall instantiation | instantiation.getOperation() = this and result = instantiation.getAlgorithmArg() ) } override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { exists(MacGetInstanceCall instantiation, MacInitCall initCall | instantiation.getOperation() = this and initCall.getOperation() = this and instantiation.getInitCall() = initCall and result.asExpr() = initCall.getKeyArg() ) } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result.asExpr() = super.getArgument(0) and super.getMethod().getParameterType(0).hasName("byte[]") } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result.asExpr() = output } override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() { none() } override predicate hasHashAlgorithmConsumer() { none() } override Crypto::KeyOperationSubtype getKeyOperationSubtype() { result instanceof Crypto::TMacMode } override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { none() } } /** * Signatures */ module SignatureKnownAlgorithmToConsumerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SignatureStringLiteral } predicate isSink(DataFlow::Node sink) { sink = any(SignatureAlgorithmValueConsumer call).getInputNode() } } module SignatureKnownAlgorithmToConsumerFlow = TaintTracking::Global; class SignatureGetInstanceCall extends MethodCall { SignatureGetInstanceCall() { this.getCallee().hasQualifiedName("java.security", "Signature", "getInstance") } Expr getAlgorithmArg() { result = this.getArgument(0) } } class SignatureGetInstanceAlgorithmValueConsumer extends SignatureAlgorithmValueConsumer instanceof Expr { SignatureGetInstanceAlgorithmValueConsumer() { this = any(SignatureGetInstanceCall c).getAlgorithmArg() } override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result.(SignatureStringLiteralAlgorithmInstance).getConsumer() = this } } class SignatureStringLiteral extends StringLiteral { SignatureStringLiteral() { signature_names(this.getValue()) } } class SignatureStringLiteralAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance instanceof SignatureStringLiteral { SignatureAlgorithmValueConsumer consumer; SignatureStringLiteralAlgorithmInstance() { SignatureKnownAlgorithmToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } SignatureAlgorithmValueConsumer getConsumer() { result = consumer } override string getRawAlgorithmName() { result = super.getValue() } override Crypto::KeyOpAlg::AlgorithmType getAlgorithmType() { if signature_name_to_type_known(_, super.getValue()) then signature_name_to_type_known(result, super.getValue()) else result = Crypto::KeyOpAlg::TOtherKeyOperationAlgorithmType() } override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { // TODO: trace to any key size initializer? none() } override int getKeySizeFixed() { // TODO: are there known fixed key sizes to consider? none() } override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } } class SignatureHashAlgorithmInstance extends Crypto::HashAlgorithmInstance instanceof SignatureStringLiteralAlgorithmInstance { Crypto::THashType hashType; int digestLength; SignatureHashAlgorithmInstance() { hashType = signature_name_to_hash_type_known(this.(StringLiteral).getValue(), digestLength) } override string getRawHashAlgorithmName() { result = this.(StringLiteral).getValue() } override Crypto::THashType getHashType() { result = hashType } override int getFixedDigestLength() { result = digestLength } } class SignatureInitCall extends MethodCall { SignatureInitCall() { this.getCallee().hasQualifiedName("java.security", "Signature", ["initSign", "initVerify"]) } Expr getKeyArg() { result = this.getArgument(0) // TODO: verify can take in a certificate too? } } private class SignatureOperationCall extends MethodCall { SignatureOperationCall() { this.getMethod().hasQualifiedName("java.security", "Signature", ["update", "sign", "verify"]) } predicate isIntermediate() { super.getMethod().getName() = "update" } Expr getMsgInput() { result = this.getArgument(0) and this.getMethod().getName() = "update" } Expr getSignatureOutput() { // no args, the signature is returned result = this and this.getMethod().getName() = "sign" and not exists(this.getArgument(0)) or // with args, the signature is written to the arg result = this.getArgument(0) and this.getMethod().getName() = "sign" } Expr getSignatureInput() { result = this.getArgument(0) and this.getMethod().getName() = "verify" } Crypto::KeyOperationSubtype getSubtype() { result instanceof Crypto::TSignMode and this.getMethod().getName() = "sign" or result instanceof Crypto::TVerifyMode and this.getMethod().getName() = "verify" } } class SignatureOperationInstance extends Crypto::SignatureOperationInstance instanceof SignatureOperationCall { SignatureOperationInstance() { // exclude update (only include sign and verify) not super.isIntermediate() } SignatureGetInstanceCall getInstantiationCall() { result = SignatureFlowAnalysisImpl::getInstantiationFromUse(this, _, _) } SignatureInitCall getInitCall() { result = SignatureFlowAnalysisImpl::getInitFromUse(this, _, _) } override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result.asExpr() = super.getMsgInput() or result.asExpr() = SignatureFlowAnalysisImpl::getAnIntermediateUseFromFinalUse(this, _, _).getMsgInput() } override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { result.asExpr() = this.getInitCall().getKeyArg() } override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result = this.getInstantiationCall().getAlgorithmArg() } override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result.asExpr() = super.getSignatureOutput() or result.asExpr() = SignatureFlowAnalysisImpl::getAnIntermediateUseFromFinalUse(this, _, _).getSignatureOutput() } override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() { // TODO: RSASSA-PSS literal sets hashes differently, through a ParameterSpec result = this.getInstantiationCall().getAlgorithmArg() } override predicate hasHashAlgorithmConsumer() { // All jca signature algorithms specify a hash unless explicitly set as "NONEwith..." exists(SignatureStringLiteralAlgorithmInstance i | i.getConsumer() = this.getAnAlgorithmValueConsumer() and not i.getRawAlgorithmName().toUpperCase().matches("NONEwith%".toUpperCase()) ) } override Crypto::KeyOperationSubtype getKeyOperationSubtype() { result = super.getSubtype() } override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { none() } override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { result.asExpr() = super.getSignatureInput() or result.asExpr() = SignatureFlowAnalysisImpl::getAnIntermediateUseFromFinalUse(this, _, _).getSignatureInput() } } module SignatureFlowAnalysisImpl = GetInstanceInitUseFlowAnalysis; /* * Elliptic Curves (EC) */ module EllipticCurveStringToConsumerConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof EllipticCurveStringLiteral } predicate isSink(DataFlow::Node sink) { exists(EllipticCurveAlgorithmValueConsumer consumer | sink = consumer.getInputNode()) } } module EllipticCurveStringToConsumerFlow = DataFlow::Global; class EllipticCurveStringLiteral extends StringLiteral { EllipticCurveStringLiteral() { elliptic_curve_names(this.getValue()) } } class EllipticCurveStringLiteralInstance extends Crypto::EllipticCurveInstance instanceof EllipticCurveStringLiteral { EllipticCurveAlgorithmValueConsumer consumer; EllipticCurveStringLiteralInstance() { EllipticCurveStringToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode()) } override string getRawEllipticCurveName() { result = super.getValue() } override Crypto::EllipticCurveType getEllipticCurveType() { if Crypto::ellipticCurveNameToKnownKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), _, _) then Crypto::ellipticCurveNameToKnownKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), _, result) else result = Crypto::OtherEllipticCurveType() } override int getKeySize() { Crypto::ellipticCurveNameToKnownKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), result, _) } EllipticCurveAlgorithmValueConsumer getConsumer() { result = consumer } } }