@@ -18,6 +18,8 @@ module JCAModel {
1818
1919 abstract class KeyAgreementAlgorithmValueConsumer extends Crypto:: AlgorithmValueConsumer { }
2020
21+ abstract class SignatureAlgorithmValueConsumer extends Crypto:: AlgorithmValueConsumer { }
22+
2123 // TODO: Verify that the PBEWith% case works correctly
2224 bindingset [ algo]
2325 predicate cipher_names ( string algo ) {
@@ -100,9 +102,21 @@ module JCAModel {
100102 ] .toUpperCase ( ) )
101103 }
102104
105+ /**
106+ * Names that match known signature algorithms.
107+ * https://docs.oracle.com/en/java/javase/25/docs/specs/security/standard-names.html
108+ */
109+ bindingset [ name]
110+ predicate signature_names ( string name ) {
111+ name .toUpperCase ( ) .splitAt ( "WITH" , 1 ) .matches ( [ "RSA%" , "ECDSA%" , "DSA%" ] )
112+ or
113+ name .toUpperCase ( ) .matches ( [ "RSASSA-PSS" , "ED25519" , "ED448" , "EDDSA" , "ML-DSA%" , "HSS/LMS" ] )
114+ }
115+
103116 bindingset [ name]
104117 predicate key_agreement_names ( string name ) {
105- name .toUpperCase ( ) .matches ( [ "DH" , "EDH" , "ECDH" , "X25519" , "X448" ] .toUpperCase ( ) )
118+ name .toUpperCase ( )
119+ .matches ( [ "DH" , "EDH" , "ECDH" , "X25519" , "X448" , "ML-KEM%" , "XDH" ] .toUpperCase ( ) )
106120 }
107121
108122 bindingset [ name]
@@ -208,13 +222,46 @@ module JCAModel {
208222 bindingset [ name]
209223 predicate key_agreement_name_to_type_known ( Crypto:: TKeyAgreementType type , string name ) {
210224 type = Crypto:: DH ( ) and
211- name .toUpperCase ( ) = "DH"
225+ name .toUpperCase ( ) in [ "DH" , "XDH" ]
212226 or
213227 type = Crypto:: EDH ( ) and
214228 name .toUpperCase ( ) = "EDH"
215229 or
216230 type = Crypto:: ECDH ( ) and
217231 name .toUpperCase ( ) in [ "ECDH" , "X25519" , "X448" ]
232+ or
233+ type = Crypto:: OtherKeyAgreementType ( ) and
234+ name .toUpperCase ( ) .matches ( "ML-KEM%" )
235+ }
236+
237+ /**
238+ * Maps a signature algorithm name to its type, if known.
239+ * see https://docs.oracle.com/en/java/javase/25/docs/specs/security/standard-names.html
240+ */
241+ bindingset [ name]
242+ predicate signature_name_to_type_known ( Crypto:: KeyOpAlg:: TAlgorithm type , string name ) {
243+ name .toUpperCase ( ) .splitAt ( "with" .toUpperCase ( ) , 1 ) .matches ( "RSA%" ) and
244+ type = KeyOpAlg:: TAsymmetricCipher ( KeyOpAlg:: RSA ( ) )
245+ or
246+ name .toUpperCase ( ) .splitAt ( "with" .toUpperCase ( ) , 1 ) .matches ( "ECDSA%" ) and
247+ type = KeyOpAlg:: TSignature ( KeyOpAlg:: ECDSA ( ) )
248+ or
249+ name .toUpperCase ( ) .splitAt ( "with" .toUpperCase ( ) , 1 ) .matches ( "DSA%" ) and
250+ type = KeyOpAlg:: TSignature ( KeyOpAlg:: DSA ( ) )
251+ or
252+ name .toUpperCase ( ) = "RSASSA-PSS" and type = KeyOpAlg:: TAsymmetricCipher ( KeyOpAlg:: RSA ( ) )
253+ or
254+ name .toUpperCase ( ) .matches ( [ "EDDSA" , "ED25519" , "ED448" ] ) and
255+ type = KeyOpAlg:: TSignature ( KeyOpAlg:: EDDSA ( ) )
256+ or
257+ name .toUpperCase ( ) .matches ( "ML-DSA%" ) and type = KeyOpAlg:: TSignature ( KeyOpAlg:: DSA ( ) )
258+ or
259+ name .toUpperCase ( ) = "HSS/LMS" and type = KeyOpAlg:: TSignature ( KeyOpAlg:: HSS_LMS ( ) )
260+ }
261+
262+ bindingset [ name]
263+ Crypto:: HashType signature_name_to_hash_type_known ( string name , int digestLength ) {
264+ result = hash_name_to_type_known ( name .splitAt ( "with" , 0 ) , digestLength )
218265 }
219266
220267 /**
@@ -345,7 +392,7 @@ module JCAModel {
345392 override KeyOpAlg:: AlgorithmType getAlgorithmType ( ) {
346393 if cipher_name_to_type_known ( _, super .getAlgorithmName ( ) )
347394 then cipher_name_to_type_known ( result , super .getAlgorithmName ( ) )
348- else result instanceof KeyOpAlg:: TUnknownKeyOperationAlgorithmType
395+ else result instanceof KeyOpAlg:: TOtherKeyOperationAlgorithmType
349396 }
350397
351398 override int getKeySizeFixed ( ) {
@@ -999,7 +1046,8 @@ module JCAModel {
9991046 override Crypto:: AlgorithmInstance getAKnownAlgorithmSource ( ) {
10001047 result .( CipherStringLiteralAlgorithmInstance ) .getConsumer ( ) = this or
10011048 result .( KeyAgreementStringLiteralAlgorithmInstance ) .getConsumer ( ) = this or
1002- result .( EllipticCurveStringLiteralInstance ) .getConsumer ( ) = this
1049+ result .( EllipticCurveStringLiteralInstance ) .getConsumer ( ) = this or
1050+ result .( SignatureStringLiteralAlgorithmInstance ) .getConsumer ( ) = this
10031051 }
10041052
10051053 KeyGeneratorGetInstanceCall getInstantiationCall ( ) { result = instantiationCall }
@@ -1047,6 +1095,21 @@ module JCAModel {
10471095 }
10481096 }
10491097
1098+ /**
1099+ * An instance of `java.security.SecureRandom.nextBytes(byte[])` call.
1100+ * This is already generally modeled for Java in CodeQL, but
1101+ * we model it again as part of the crypto API model to have a cohesive model.
1102+ */
1103+ class JavaSecuritySecureRandom extends Crypto:: RandomNumberGenerationInstance instanceof Call {
1104+ JavaSecuritySecureRandom ( ) {
1105+ this .getCallee ( ) .hasQualifiedName ( "java.security" , "SecureRandom" , "nextBytes" )
1106+ }
1107+
1108+ override Crypto:: DataFlowNode getOutputNode ( ) { result .asExpr ( ) = this .( Call ) .getArgument ( 0 ) }
1109+
1110+ override string getGeneratorName ( ) { result = this .( Call ) .getCallee ( ) .getName ( ) }
1111+ }
1112+
10501113 class KeyGeneratorGenerateCall extends Crypto:: KeyGenerationOperationInstance instanceof MethodCall
10511114 {
10521115 Crypto:: KeyArtifactType type ;
@@ -1624,6 +1687,196 @@ module JCAModel {
16241687 override Crypto:: ConsumerInputDataFlowNode getNonceConsumer ( ) { none ( ) }
16251688 }
16261689
1690+ /**
1691+ * Signatures
1692+ */
1693+ module SignatureKnownAlgorithmToConsumerConfig implements DataFlow:: ConfigSig {
1694+ predicate isSource ( DataFlow:: Node src ) { src .asExpr ( ) instanceof SignatureStringLiteral }
1695+
1696+ predicate isSink ( DataFlow:: Node sink ) {
1697+ sink = any ( SignatureAlgorithmValueConsumer call ) .getInputNode ( )
1698+ }
1699+ }
1700+
1701+ module SignatureKnownAlgorithmToConsumerFlow =
1702+ TaintTracking:: Global< SignatureKnownAlgorithmToConsumerConfig > ;
1703+
1704+ class SignatureGetInstanceCall extends MethodCall {
1705+ SignatureGetInstanceCall ( ) {
1706+ this .getCallee ( ) .hasQualifiedName ( "java.security" , "Signature" , "getInstance" )
1707+ }
1708+
1709+ Expr getAlgorithmArg ( ) { result = this .getArgument ( 0 ) }
1710+ }
1711+
1712+ class SignatureGetInstanceAlgorithmValueConsumer extends SignatureAlgorithmValueConsumer instanceof Expr
1713+ {
1714+ SignatureGetInstanceAlgorithmValueConsumer ( ) {
1715+ this = any ( SignatureGetInstanceCall c ) .getAlgorithmArg ( )
1716+ }
1717+
1718+ override Crypto:: ConsumerInputDataFlowNode getInputNode ( ) { result .asExpr ( ) = this }
1719+
1720+ override Crypto:: AlgorithmInstance getAKnownAlgorithmSource ( ) {
1721+ result .( SignatureStringLiteralAlgorithmInstance ) .getConsumer ( ) = this
1722+ }
1723+ }
1724+
1725+ class SignatureStringLiteral extends StringLiteral {
1726+ SignatureStringLiteral ( ) { signature_names ( this .getValue ( ) ) }
1727+ }
1728+
1729+ class SignatureStringLiteralAlgorithmInstance extends Crypto:: KeyOperationAlgorithmInstance instanceof SignatureStringLiteral
1730+ {
1731+ SignatureAlgorithmValueConsumer consumer ;
1732+
1733+ SignatureStringLiteralAlgorithmInstance ( ) {
1734+ SignatureKnownAlgorithmToConsumerFlow:: flow ( DataFlow:: exprNode ( this ) , consumer .getInputNode ( ) )
1735+ }
1736+
1737+ SignatureAlgorithmValueConsumer getConsumer ( ) { result = consumer }
1738+
1739+ override string getRawAlgorithmName ( ) { result = super .getValue ( ) }
1740+
1741+ override Crypto:: KeyOpAlg:: AlgorithmType getAlgorithmType ( ) {
1742+ if signature_name_to_type_known ( _, super .getValue ( ) )
1743+ then signature_name_to_type_known ( result , super .getValue ( ) )
1744+ else result = Crypto:: KeyOpAlg:: TOtherKeyOperationAlgorithmType ( )
1745+ }
1746+
1747+ override Crypto:: ConsumerInputDataFlowNode getKeySizeConsumer ( ) {
1748+ // TODO: trace to any key size initializer?
1749+ none ( )
1750+ }
1751+
1752+ override int getKeySizeFixed ( ) {
1753+ // TODO: are there known fixed key sizes to consider?
1754+ none ( )
1755+ }
1756+
1757+ override Crypto:: ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm ( ) { none ( ) }
1758+
1759+ override Crypto:: PaddingAlgorithmInstance getPaddingAlgorithm ( ) { none ( ) }
1760+ }
1761+
1762+ class SignatureHashAlgorithmInstance extends Crypto:: HashAlgorithmInstance instanceof SignatureStringLiteralAlgorithmInstance
1763+ {
1764+ Crypto:: THashType hashType ;
1765+ int digestLength ;
1766+
1767+ SignatureHashAlgorithmInstance ( ) {
1768+ hashType = signature_name_to_hash_type_known ( this .( StringLiteral ) .getValue ( ) , digestLength )
1769+ }
1770+
1771+ override string getRawHashAlgorithmName ( ) { result = this .( StringLiteral ) .getValue ( ) }
1772+
1773+ override Crypto:: THashType getHashFamily ( ) { result = hashType }
1774+
1775+ override int getFixedDigestLength ( ) { result = digestLength }
1776+ }
1777+
1778+ class SignatureInitCall extends MethodCall {
1779+ SignatureInitCall ( ) {
1780+ this .getCallee ( ) .hasQualifiedName ( "java.security" , "Signature" , [ "initSign" , "initVerify" ] )
1781+ }
1782+
1783+ Expr getKeyArg ( ) {
1784+ result = this .getArgument ( 0 )
1785+ // TODO: verify can take in a certificate too?
1786+ }
1787+ }
1788+
1789+ private class SignatureOperationCall extends MethodCall {
1790+ SignatureOperationCall ( ) {
1791+ this .getMethod ( ) .hasQualifiedName ( "java.security" , "Signature" , [ "update" , "sign" , "verify" ] )
1792+ }
1793+
1794+ predicate isIntermediate ( ) { super .getMethod ( ) .getName ( ) = "update" }
1795+
1796+ Expr getMsgInput ( ) { result = this .getArgument ( 0 ) and this .getMethod ( ) .getName ( ) = "update" }
1797+
1798+ Expr getSignatureOutput ( ) {
1799+ // no args, the signature is returned
1800+ result = this and this .getMethod ( ) .getName ( ) = "sign" and not exists ( this .getArgument ( 0 ) )
1801+ or
1802+ // with args, the signature is written to the arg
1803+ result = this .getArgument ( 0 ) and this .getMethod ( ) .getName ( ) = "sign"
1804+ }
1805+
1806+ Expr getSignatureInput ( ) {
1807+ result = this .getArgument ( 0 ) and this .getMethod ( ) .getName ( ) = "verify"
1808+ }
1809+
1810+ Crypto:: KeyOperationSubtype getSubtype ( ) {
1811+ result instanceof Crypto:: TSignMode and this .getMethod ( ) .getName ( ) = "sign"
1812+ or
1813+ result instanceof Crypto:: TVerifyMode and this .getMethod ( ) .getName ( ) = "verify"
1814+ }
1815+ }
1816+
1817+ class SignatureOperationInstance extends Crypto:: SignatureOperationInstance instanceof SignatureOperationCall
1818+ {
1819+ SignatureOperationInstance ( ) {
1820+ // exclude update (only include sign and verify)
1821+ not super .isIntermediate ( )
1822+ }
1823+
1824+ SignatureGetInstanceCall getInstantiationCall ( ) {
1825+ result = SignatureFlowAnalysisImpl:: getInstantiationFromUse ( this , _, _)
1826+ }
1827+
1828+ SignatureInitCall getInitCall ( ) {
1829+ result = SignatureFlowAnalysisImpl:: getInitFromUse ( this , _, _)
1830+ }
1831+
1832+ override Crypto:: ConsumerInputDataFlowNode getInputConsumer ( ) {
1833+ result .asExpr ( ) = super .getMsgInput ( ) or
1834+ result .asExpr ( ) =
1835+ SignatureFlowAnalysisImpl:: getAnIntermediateUseFromFinalUse ( this , _, _) .getMsgInput ( )
1836+ }
1837+
1838+ override Crypto:: ConsumerInputDataFlowNode getKeyConsumer ( ) {
1839+ result .asExpr ( ) = this .getInitCall ( ) .getKeyArg ( )
1840+ }
1841+
1842+ override Crypto:: AlgorithmValueConsumer getAnAlgorithmValueConsumer ( ) {
1843+ result = this .getInstantiationCall ( ) .getAlgorithmArg ( )
1844+ }
1845+
1846+ override Crypto:: ArtifactOutputDataFlowNode getOutputArtifact ( ) {
1847+ result .asExpr ( ) = super .getSignatureOutput ( ) or
1848+ result .asExpr ( ) =
1849+ SignatureFlowAnalysisImpl:: getAnIntermediateUseFromFinalUse ( this , _, _) .getSignatureOutput ( )
1850+ }
1851+
1852+ override Crypto:: AlgorithmValueConsumer getHashAlgorithmValueConsumer ( ) {
1853+ // TODO: RSASSA-PSS literal sets hashes differently, through a ParameterSpec
1854+ result = this .getInstantiationCall ( ) .getAlgorithmArg ( )
1855+ }
1856+
1857+ override predicate hasHashAlgorithmConsumer ( ) {
1858+ // All jca signature algorithms specify a hash unless explicitly set as "NONEwith..."
1859+ exists ( SignatureStringLiteralAlgorithmInstance i |
1860+ i .getConsumer ( ) = this .getAnAlgorithmValueConsumer ( ) and
1861+ not i .getRawAlgorithmName ( ) .toUpperCase ( ) .matches ( "NONEwith%" .toUpperCase ( ) )
1862+ )
1863+ }
1864+
1865+ override Crypto:: KeyOperationSubtype getKeyOperationSubtype ( ) { result = super .getSubtype ( ) }
1866+
1867+ override Crypto:: ConsumerInputDataFlowNode getNonceConsumer ( ) { none ( ) }
1868+
1869+ override Crypto:: ConsumerInputDataFlowNode getSignatureConsumer ( ) {
1870+ result .asExpr ( ) = super .getSignatureInput ( ) or
1871+ result .asExpr ( ) =
1872+ SignatureFlowAnalysisImpl:: getAnIntermediateUseFromFinalUse ( this , _, _) .getSignatureInput ( )
1873+ }
1874+ }
1875+
1876+ module SignatureFlowAnalysisImpl =
1877+ GetInstanceInitUseFlowAnalysis< SignatureGetInstanceCall , SignatureInitCall ,
1878+ SignatureOperationCall > ;
1879+
16271880 /*
16281881 * Elliptic Curves (EC)
16291882 */
0 commit comments