|
| 1 | +/** |
| 2 | + * @name Implementation of a cryptographic primitive |
| 3 | + * @description Writing your own cryptographic primitives is prone to errors and omissions that weaken cryptographic protection. |
| 4 | + * @kind problem |
| 5 | + * @problem.severity warning |
| 6 | + * @security-severity 7.5 |
| 7 | + * @precision medium |
| 8 | + * @id cpp/crypto-primitive |
| 9 | + * @tags security |
| 10 | + * external/cwe/cwe-1240 |
| 11 | + */ |
| 12 | + |
| 13 | +import cpp |
| 14 | + |
| 15 | +/** |
| 16 | + * A word that might be in the name of an encryption function. |
| 17 | + */ |
| 18 | +string encryptionWord() { |
| 19 | + exists(string word | |
| 20 | + // `(?<!P)` is negative lookbehind, i.e. the match is not preceeded by `P`. |
| 21 | + // `(?!P)` is negative lookahead, i.e. the match is not followed by `P`. |
| 22 | + word = |
| 23 | + [ |
| 24 | + "Crypt", "Cipher", "Aes", "Rijndael", |
| 25 | + //"(?<!Wi|Co|No)Des(?!truct)", |
| 26 | + "(?<!C)Rc[0-9]", "(?<!Cha|Unive)Rsa", "Blowfish", "Twofish", "Idea", "Kyber", "(?<!V)Aria", |
| 27 | + //"Asn[0-9]", |
| 28 | + "Camellia", |
| 29 | + //"(?<!Bit|Type)Cast", |
| 30 | + "Chacha", "ChaCha", "Idea", "Poly[0-9]", "Ripemd", "Whirlpool", "Sbox", "SBox", "Cblock", |
| 31 | + "CBlock", "Sub.?Byte", "Mix.?Column", "ECDH", "ECDSA", "EdDSA", "ECMQV", "ECQV", |
| 32 | + "Curve[0-9][0-9]" |
| 33 | + ] and |
| 34 | + ( |
| 35 | + result = word or |
| 36 | + result = word.toLowerCase() + "(?![a-z])" or // avoid matching middles of words |
| 37 | + result = word.toUpperCase() + "(?![A-Z])" // avoid matching middles of words |
| 38 | + ) |
| 39 | + ) |
| 40 | +} |
| 41 | + |
| 42 | +/** |
| 43 | + * A function whose name suggests it may be doing encryption (but may or may |
| 44 | + * not actually implement an encryption primitive itself). |
| 45 | + */ |
| 46 | +predicate likelyEncryptionFunction(Function f) { |
| 47 | + exists(string fName | fName = f.getName() | |
| 48 | + fName.regexpMatch(".*(" + concat(encryptionWord(), "|") + ").*") |
| 49 | + ) |
| 50 | +} |
| 51 | + |
| 52 | +/** |
| 53 | + * A type that is common in encryption-like computations. That is, an integral |
| 54 | + * type or array of integral type elements. |
| 55 | + */ |
| 56 | +predicate computeHeuristicType(Type t) { |
| 57 | + t instanceof IntegralType or |
| 58 | + computeHeuristicType(t.(ArrayType).getBaseType().getUnspecifiedType()) |
| 59 | +} |
| 60 | + |
| 61 | +/** |
| 62 | + * An operation that is common in encryption-like computations. Looking for |
| 63 | + * clusters of these tends to find things like encrpytion, compression, random |
| 64 | + * number generation, graphics processing and other compute heavy algoritms. |
| 65 | + */ |
| 66 | +predicate computeHeuristic(Expr e) { |
| 67 | + ( |
| 68 | + e instanceof BitwiseXorExpr or |
| 69 | + e instanceof AssignXorExpr or |
| 70 | + e instanceof LShiftExpr or |
| 71 | + e instanceof AssignLShiftExpr or |
| 72 | + e instanceof RShiftExpr or |
| 73 | + e instanceof AssignRShiftExpr or |
| 74 | + e instanceof ArrayExpr |
| 75 | + ) and |
| 76 | + computeHeuristicType(e.getUnspecifiedType()) |
| 77 | +} |
| 78 | + |
| 79 | +from Function f, int amount |
| 80 | +where |
| 81 | + likelyEncryptionFunction(f) and |
| 82 | + amount = strictcount(Expr e | computeHeuristic(e) and e.getEnclosingFunction() = f) and |
| 83 | + amount >= 8 and |
| 84 | + exists(f.getFile().getRelativePath()) // exclude library files |
| 85 | +select f, "This may be a custom implementation of a cryptographic primitive." |
0 commit comments