|
|
|
|
@@ -3,22 +3,7 @@
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import javascript
|
|
|
|
|
import semmle.javascript.security.CryptoAlgorithms
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* An application of a cryptographic algorithm.
|
|
|
|
|
*/
|
|
|
|
|
abstract class CryptographicOperation extends DataFlow::Node {
|
|
|
|
|
/**
|
|
|
|
|
* Gets the input the algorithm is used on, e.g. the plain text input to be encrypted.
|
|
|
|
|
*/
|
|
|
|
|
abstract DataFlow::Node getInput();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gets the applied algorithm.
|
|
|
|
|
*/
|
|
|
|
|
abstract CryptographicAlgorithm getAlgorithm();
|
|
|
|
|
}
|
|
|
|
|
import semmle.javascript.Concepts::Cryptography
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A key used in a cryptographic algorithm.
|
|
|
|
|
@@ -52,13 +37,20 @@ class CryptographicKeyCredentialsExpr extends CredentialsNode instanceof Cryptog
|
|
|
|
|
override string getCredentialsKind() { result = "key" }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Holds if `algorithm` is an `EncryptionAlgorithm` that uses a block cipher
|
|
|
|
|
private predicate isBlockEncryptionAlgorithm(CryptographicAlgorithm algorithm) {
|
|
|
|
|
algorithm instanceof EncryptionAlgorithm and
|
|
|
|
|
not algorithm.(EncryptionAlgorithm).isStreamCipher()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A model of the asmCrypto library.
|
|
|
|
|
*/
|
|
|
|
|
private module AsmCrypto {
|
|
|
|
|
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
|
|
|
|
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
|
|
|
|
|
DataFlow::Node input;
|
|
|
|
|
CryptographicAlgorithm algorithm; // non-functional
|
|
|
|
|
private string algorithmName;
|
|
|
|
|
|
|
|
|
|
Apply() {
|
|
|
|
|
/*
|
|
|
|
|
@@ -71,17 +63,22 @@ private module AsmCrypto {
|
|
|
|
|
* ```
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exists(DataFlow::SourceNode asmCrypto, string algorithmName |
|
|
|
|
|
exists(DataFlow::SourceNode asmCrypto |
|
|
|
|
|
asmCrypto = DataFlow::globalVarRef("asmCrypto") and
|
|
|
|
|
algorithm.matchesName(algorithmName) and
|
|
|
|
|
this = asmCrypto.getAPropertyRead(algorithmName).getAMemberCall(_) and
|
|
|
|
|
input = this.getAnArgument()
|
|
|
|
|
input = this.getArgument(0)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override DataFlow::Node getInput() { result = input }
|
|
|
|
|
override DataFlow::Node getAnInput() { result = input }
|
|
|
|
|
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
|
|
|
|
|
override BlockMode getBlockMode() {
|
|
|
|
|
isBlockEncryptionAlgorithm(this.getAlgorithm()) and
|
|
|
|
|
result.matchesString(algorithmName)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -93,7 +90,7 @@ private module BrowserIdCrypto {
|
|
|
|
|
Key() { this = any(Apply apply).getKey() }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Apply extends CryptographicOperation instanceof DataFlow::MethodCallNode {
|
|
|
|
|
private class Apply extends CryptographicOperation::Range instanceof DataFlow::MethodCallNode {
|
|
|
|
|
CryptographicAlgorithm algorithm; // non-functional
|
|
|
|
|
|
|
|
|
|
Apply() {
|
|
|
|
|
@@ -126,10 +123,13 @@ private module BrowserIdCrypto {
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override DataFlow::Node getInput() { result = super.getArgument(0) }
|
|
|
|
|
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
|
|
|
|
|
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
|
|
|
|
|
// not relevant for browserid-crypto
|
|
|
|
|
override BlockMode getBlockMode() { none() }
|
|
|
|
|
|
|
|
|
|
DataFlow::Node getKey() { result = super.getArgument(1) }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -140,6 +140,7 @@ private module BrowserIdCrypto {
|
|
|
|
|
private module NodeJSCrypto {
|
|
|
|
|
private class InstantiatedAlgorithm extends DataFlow::CallNode {
|
|
|
|
|
CryptographicAlgorithm algorithm; // non-functional
|
|
|
|
|
private string algorithmName;
|
|
|
|
|
|
|
|
|
|
InstantiatedAlgorithm() {
|
|
|
|
|
/*
|
|
|
|
|
@@ -158,11 +159,25 @@ private module NodeJSCrypto {
|
|
|
|
|
exists(DataFlow::SourceNode mod |
|
|
|
|
|
mod = DataFlow::moduleImport("crypto") and
|
|
|
|
|
this = mod.getAMemberCall("create" + ["Hash", "Hmac", "Sign", "Cipher"]) and
|
|
|
|
|
algorithm.matchesName(this.getArgument(0).getStringValue())
|
|
|
|
|
algorithmName = this.getArgument(0).getStringValue() and
|
|
|
|
|
algorithm.matchesName(algorithmName)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
|
|
|
|
|
private BlockMode getExplicitBlockMode() { result.matchesString(algorithmName) }
|
|
|
|
|
|
|
|
|
|
BlockMode getBlockMode() {
|
|
|
|
|
isBlockEncryptionAlgorithm(this.getAlgorithm()) and
|
|
|
|
|
(
|
|
|
|
|
if exists(this.getExplicitBlockMode())
|
|
|
|
|
then result = this.getExplicitBlockMode()
|
|
|
|
|
else
|
|
|
|
|
// CBC is the default if not explicitly specified
|
|
|
|
|
result = "CBC"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class CreateKey extends CryptographicKeyCreation, DataFlow::CallNode {
|
|
|
|
|
@@ -211,14 +226,16 @@ private module NodeJSCrypto {
|
|
|
|
|
override predicate isSymmetricKey() { none() }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Apply extends CryptographicOperation instanceof DataFlow::MethodCallNode {
|
|
|
|
|
private class Apply extends CryptographicOperation::Range instanceof DataFlow::MethodCallNode {
|
|
|
|
|
InstantiatedAlgorithm instantiation;
|
|
|
|
|
|
|
|
|
|
Apply() { this = instantiation.getAMethodCall(any(string m | m = "update" or m = "write")) }
|
|
|
|
|
|
|
|
|
|
override DataFlow::Node getInput() { result = super.getArgument(0) }
|
|
|
|
|
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
|
|
|
|
|
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = instantiation.getAlgorithm() }
|
|
|
|
|
|
|
|
|
|
override BlockMode getBlockMode() { result = instantiation.getBlockMode() }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Key extends CryptographicKey {
|
|
|
|
|
@@ -307,7 +324,7 @@ private module CryptoJS {
|
|
|
|
|
input = result.getArgument(0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Apply extends CryptographicOperation {
|
|
|
|
|
private class Apply extends CryptographicOperation::Range, DataFlow::CallNode {
|
|
|
|
|
DataFlow::Node input;
|
|
|
|
|
CryptographicAlgorithm algorithm; // non-functional
|
|
|
|
|
|
|
|
|
|
@@ -316,9 +333,31 @@ private module CryptoJS {
|
|
|
|
|
this = getDirectApplication(input, algorithm)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override DataFlow::Node getInput() { result = input }
|
|
|
|
|
override DataFlow::Node getAnInput() { result = input }
|
|
|
|
|
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
|
|
|
|
|
// e.g. CryptoJS.AES.encrypt("msg", "key", { mode: CryptoJS.mode.<modeString> })
|
|
|
|
|
private BlockMode getExplicitBlockMode() {
|
|
|
|
|
exists(DataFlow::ObjectLiteralNode o, DataFlow::SourceNode modeNode, string modeString |
|
|
|
|
|
modeNode = API::moduleImport("crypto-js").getMember("mode").getMember(modeString).asSource() and
|
|
|
|
|
o.flowsTo(this.getArgument(2)) and
|
|
|
|
|
modeNode = o.getAPropertySource("mode")
|
|
|
|
|
|
|
|
|
|
|
result.matchesString(modeString)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override BlockMode getBlockMode() {
|
|
|
|
|
isBlockEncryptionAlgorithm(this.getAlgorithm()) and
|
|
|
|
|
(
|
|
|
|
|
if exists(this.getExplicitBlockMode())
|
|
|
|
|
then result = this.getExplicitBlockMode()
|
|
|
|
|
else
|
|
|
|
|
// CBC is the default if not explicitly specified
|
|
|
|
|
result = "CBC"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Key extends CryptographicKey {
|
|
|
|
|
@@ -374,7 +413,7 @@ private module CryptoJS {
|
|
|
|
|
* A model of the TweetNaCl library.
|
|
|
|
|
*/
|
|
|
|
|
private module TweetNaCl {
|
|
|
|
|
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
|
|
|
|
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
|
|
|
|
|
DataFlow::Node input;
|
|
|
|
|
CryptographicAlgorithm algorithm;
|
|
|
|
|
|
|
|
|
|
@@ -401,9 +440,12 @@ private module TweetNaCl {
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override DataFlow::Node getInput() { result = input }
|
|
|
|
|
override DataFlow::Node getAnInput() { result = input }
|
|
|
|
|
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
|
|
|
|
|
// No block ciphers implemented
|
|
|
|
|
override BlockMode getBlockMode() { none() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -434,7 +476,7 @@ private module HashJs {
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
|
|
|
|
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
|
|
|
|
|
DataFlow::Node input;
|
|
|
|
|
CryptographicAlgorithm algorithm; // non-functional
|
|
|
|
|
|
|
|
|
|
@@ -456,9 +498,12 @@ private module HashJs {
|
|
|
|
|
input = super.getArgument(0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override DataFlow::Node getInput() { result = input }
|
|
|
|
|
override DataFlow::Node getAnInput() { result = input }
|
|
|
|
|
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
|
|
|
|
|
// not relevant for hash.js
|
|
|
|
|
override BlockMode getBlockMode() { none() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -478,19 +523,20 @@ private module Forge {
|
|
|
|
|
private class KeyCipher extends Cipher {
|
|
|
|
|
DataFlow::Node key;
|
|
|
|
|
CryptographicAlgorithm algorithm; // non-functional
|
|
|
|
|
private string blockModeString;
|
|
|
|
|
|
|
|
|
|
KeyCipher() {
|
|
|
|
|
exists(DataFlow::SourceNode mod, string algorithmName |
|
|
|
|
|
mod = getAnImportNode() and
|
|
|
|
|
algorithm.matchesName(algorithmName)
|
|
|
|
|
|
|
|
|
|
|
exists(string createName, string cipherName, string cipherPrefix, string cipherSuffix |
|
|
|
|
|
exists(string createName, string cipherName, string cipherPrefix |
|
|
|
|
|
// `require('forge').cipher.createCipher("3DES-CBC").update("secret", "key");`
|
|
|
|
|
(createName = "createCipher" or createName = "createDecipher") and
|
|
|
|
|
this = mod.getAPropertyRead("cipher").getAMemberCall(createName) and
|
|
|
|
|
this.getArgument(0).mayHaveStringValue(cipherName) and
|
|
|
|
|
cipherName = cipherPrefix + "-" + cipherSuffix and
|
|
|
|
|
cipherSuffix = ["CBC", "CFB", "CTR", "ECB", "GCM", "OFB"] and
|
|
|
|
|
cipherName = cipherPrefix + "-" + blockModeString and
|
|
|
|
|
blockModeString = ["CBC", "CFB", "CTR", "ECB", "GCM", "OFB"] and
|
|
|
|
|
algorithmName = cipherPrefix and
|
|
|
|
|
key = this.getArgument(1)
|
|
|
|
|
)
|
|
|
|
|
@@ -500,7 +546,8 @@ private module Forge {
|
|
|
|
|
createName = "createEncryptionCipher" or createName = "createDecryptionCipher"
|
|
|
|
|
|
|
|
|
|
|
this = mod.getAPropertyRead(algorithmName).getAMemberCall(createName) and
|
|
|
|
|
key = this.getArgument(0)
|
|
|
|
|
key = this.getArgument(0) and
|
|
|
|
|
blockModeString = algorithmName
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
@@ -508,6 +555,11 @@ private module Forge {
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
|
|
|
|
|
DataFlow::Node getKey() { result = key }
|
|
|
|
|
|
|
|
|
|
BlockMode getBlockMode() {
|
|
|
|
|
isBlockEncryptionAlgorithm(this.getAlgorithm()) and
|
|
|
|
|
result.matchesString(blockModeString)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class NonKeyCipher extends Cipher {
|
|
|
|
|
@@ -527,21 +579,22 @@ private module Forge {
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
|
|
|
|
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
|
|
|
|
|
DataFlow::Node input;
|
|
|
|
|
CryptographicAlgorithm algorithm; // non-functional
|
|
|
|
|
private Cipher cipher;
|
|
|
|
|
|
|
|
|
|
Apply() {
|
|
|
|
|
exists(Cipher cipher |
|
|
|
|
|
this = cipher.getAMemberCall("update") and
|
|
|
|
|
super.getArgument(0) = input and
|
|
|
|
|
algorithm = cipher.getAlgorithm()
|
|
|
|
|
)
|
|
|
|
|
this = cipher.getAMemberCall("update") and
|
|
|
|
|
super.getArgument(0) = input and
|
|
|
|
|
algorithm = cipher.getAlgorithm()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override DataFlow::Node getInput() { result = input }
|
|
|
|
|
override DataFlow::Node getAnInput() { result = input }
|
|
|
|
|
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
|
|
|
|
|
override BlockMode getBlockMode() { result = cipher.(KeyCipher).getBlockMode() }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class Key extends CryptographicKey {
|
|
|
|
|
@@ -586,7 +639,7 @@ private module Forge {
|
|
|
|
|
* A model of the md5 library.
|
|
|
|
|
*/
|
|
|
|
|
private module Md5 {
|
|
|
|
|
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
|
|
|
|
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
|
|
|
|
|
DataFlow::Node input;
|
|
|
|
|
CryptographicAlgorithm algorithm;
|
|
|
|
|
|
|
|
|
|
@@ -600,9 +653,12 @@ private module Md5 {
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override DataFlow::Node getInput() { result = input }
|
|
|
|
|
override DataFlow::Node getAnInput() { result = input }
|
|
|
|
|
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
|
|
|
|
|
// not relevant for md5
|
|
|
|
|
override BlockMode getBlockMode() { none() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -610,7 +666,7 @@ private module Md5 {
|
|
|
|
|
* A model of the bcrypt, bcryptjs, bcrypt-nodejs libraries.
|
|
|
|
|
*/
|
|
|
|
|
private module Bcrypt {
|
|
|
|
|
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
|
|
|
|
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
|
|
|
|
|
DataFlow::Node input;
|
|
|
|
|
CryptographicAlgorithm algorithm;
|
|
|
|
|
|
|
|
|
|
@@ -633,9 +689,12 @@ private module Bcrypt {
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override DataFlow::Node getInput() { result = input }
|
|
|
|
|
override DataFlow::Node getAnInput() { result = input }
|
|
|
|
|
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
|
|
|
|
|
// not relevant for bcrypt
|
|
|
|
|
override BlockMode getBlockMode() { none() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -643,7 +702,7 @@ private module Bcrypt {
|
|
|
|
|
* A model of the hasha library.
|
|
|
|
|
*/
|
|
|
|
|
private module Hasha {
|
|
|
|
|
private class Apply extends CryptographicOperation instanceof DataFlow::CallNode {
|
|
|
|
|
private class Apply extends CryptographicOperation::Range instanceof DataFlow::CallNode {
|
|
|
|
|
DataFlow::Node input;
|
|
|
|
|
CryptographicAlgorithm algorithm;
|
|
|
|
|
|
|
|
|
|
@@ -659,9 +718,12 @@ private module Hasha {
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override DataFlow::Node getInput() { result = input }
|
|
|
|
|
override DataFlow::Node getAnInput() { result = input }
|
|
|
|
|
|
|
|
|
|
override CryptographicAlgorithm getAlgorithm() { result = algorithm }
|
|
|
|
|
|
|
|
|
|
// not relevant for hasha
|
|
|
|
|
override BlockMode getBlockMode() { none() }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|