src/pqc_lint/rules.py
5.0 KB · 143 lines · python Raw
1 """Rule catalog for pqc-lint."""
2
3 from __future__ import annotations
4
5 from dataclasses import dataclass
6
7 from pqc_lint.findings import Severity
8 from pqc_lint.suggestions import suggest_replacement
9
10
11 @dataclass(frozen=True)
12 class Rule:
13 id: str
14 severity: Severity
15 classical_primitive: str # RSA-PSS, ECDSA, DH, etc.
16 title: str # short title
17 message: str # detailed message
18 languages: tuple[str, ...] # languages this rule applies to
19 cwe: str | None = None
20
21 @property
22 def suggestion(self) -> str:
23 return suggest_replacement(self.classical_primitive)
24
25
26 # -------------------------------------------------------------------------
27 # Rules
28 # -------------------------------------------------------------------------
29 # ID scheme:
30 # PQC001-099 - signature schemes (RSA, ECDSA, Ed25519, DSA)
31 # PQC100-199 - key exchange (DH, ECDH, X25519)
32 # PQC200-299 - encryption (RSA-OAEP, RSA-PKCS1)
33 # PQC300-399 - weak hashes (MD5, SHA-1)
34
35 RULES: tuple[Rule, ...] = (
36 # -- Signatures (broken by Shor's) --
37 Rule(
38 id="PQC001", severity=Severity.CRITICAL,
39 classical_primitive="RSA-signing",
40 title="RSA signature usage",
41 message="RSA signatures are broken by Shor's algorithm on a sufficiently large quantum computer.",
42 languages=("python", "javascript", "go", "rust", "java", "c"),
43 cwe="CWE-327",
44 ),
45 Rule(
46 id="PQC002", severity=Severity.CRITICAL,
47 classical_primitive="ECDSA",
48 title="ECDSA signature usage",
49 message="ECDSA signatures are broken by Shor's algorithm. All elliptic-curve signatures are quantum-vulnerable.",
50 languages=("python", "javascript", "go", "rust", "java", "c"),
51 cwe="CWE-327",
52 ),
53 Rule(
54 id="PQC003", severity=Severity.HIGH,
55 classical_primitive="Ed25519",
56 title="Ed25519 signature usage",
57 message="Ed25519 is a classical EC signature - broken by Shor's algorithm. Consider PQC alternative.",
58 languages=("python", "javascript", "go", "rust", "java"),
59 cwe="CWE-327",
60 ),
61 Rule(
62 id="PQC004", severity=Severity.HIGH,
63 classical_primitive="DSA",
64 title="DSA signature usage",
65 message="DSA is broken by Shor's algorithm and deprecated even in classical settings.",
66 languages=("python", "java"),
67 cwe="CWE-327",
68 ),
69
70 # -- Key exchange (broken by Shor's) --
71 Rule(
72 id="PQC101", severity=Severity.CRITICAL,
73 classical_primitive="ECDH",
74 title="ECDH key exchange",
75 message="Elliptic Curve Diffie-Hellman key exchange is broken by Shor's algorithm.",
76 languages=("python", "javascript", "go", "rust", "java", "c"),
77 cwe="CWE-327",
78 ),
79 Rule(
80 id="PQC102", severity=Severity.CRITICAL,
81 classical_primitive="DH",
82 title="Finite-field Diffie-Hellman",
83 message="Classical Diffie-Hellman is broken by Shor's algorithm.",
84 languages=("python", "javascript", "go", "rust", "java", "c"),
85 cwe="CWE-327",
86 ),
87 Rule(
88 id="PQC103", severity=Severity.HIGH,
89 classical_primitive="X25519",
90 title="X25519 key agreement",
91 message="X25519 is a classical EC key agreement - broken by Shor's algorithm.",
92 languages=("python", "javascript", "go", "rust", "java"),
93 cwe="CWE-327",
94 ),
95
96 # -- Encryption --
97 Rule(
98 id="PQC201", severity=Severity.CRITICAL,
99 classical_primitive="RSA-OAEP",
100 title="RSA-OAEP encryption",
101 message="RSA-OAEP encryption is broken by Shor's algorithm. All data encrypted today may be decrypted once CRQC exists (HNDL).",
102 languages=("python", "javascript", "go", "rust", "java", "c"),
103 cwe="CWE-327",
104 ),
105 Rule(
106 id="PQC202", severity=Severity.CRITICAL,
107 classical_primitive="RSA-PKCS1v15",
108 title="RSA PKCS#1v1.5 encryption",
109 message="RSA PKCS#1v1.5 is broken by Shor's algorithm AND has padding oracle vulnerabilities in classical settings.",
110 languages=("python", "javascript", "go", "rust", "java", "c"),
111 cwe="CWE-327",
112 ),
113
114 # -- Weak hashes --
115 Rule(
116 id="PQC301", severity=Severity.MEDIUM,
117 classical_primitive="MD5",
118 title="MD5 hashing",
119 message="MD5 is cryptographically broken. Use SHA3 family.",
120 languages=("python", "javascript", "go", "rust", "java", "c"),
121 cwe="CWE-328",
122 ),
123 Rule(
124 id="PQC302", severity=Severity.MEDIUM,
125 classical_primitive="SHA1",
126 title="SHA-1 hashing",
127 message="SHA-1 is broken (SHAttered). Use SHA3 for quantum-safe hashing.",
128 languages=("python", "javascript", "go", "rust", "java", "c"),
129 cwe="CWE-328",
130 ),
131 )
132
133
134 RULE_BY_ID: dict[str, Rule] = {r.id: r for r in RULES}
135
136
137 def get_rule(rule_id: str) -> Rule:
138 return RULE_BY_ID[rule_id]
139
140
141 def get_rules_for_language(language: str) -> list[Rule]:
142 return [r for r in RULES if language in r.languages]
143